blob: 2ecd5b018691a1f51814dd0710bd5dc98a5c6581 [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 Lesinski6a008172016-02-02 17:02:58 -0800101 mFilesToMerge[ResourceKeyRef(name, config)] = FileToMerge{
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800102 f, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
103 return true;
104 };
105
106 error |= !doMerge(src, table, package.get(),
107 mangle, false /* overlay */, true /* allow new */, callback);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700108 }
109 return !error;
110}
111
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800112bool TableMerger::doMerge(const Source& src,
113 ResourceTable* srcTable,
114 ResourceTablePackage* srcPackage,
115 const bool manglePackage,
116 const bool overlay,
117 const bool allowNewResources,
118 FileMergeCallback callback) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700119 bool error = false;
120
121 for (auto& srcType : srcPackage->types) {
122 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700123 if (srcType->symbolStatus.state == SymbolState::kPublic) {
124 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700125 && dstType->id.value() == srcType->id.value()) {
126 // Both types are public and have different IDs.
127 mContext->getDiagnostics()->error(DiagMessage(src)
128 << "can not merge type '"
129 << srcType->type
130 << "': conflicting public IDs");
131 error = true;
132 continue;
133 }
134
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700135 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700136 dstType->id = srcType->id;
137 }
138
139 for (auto& srcEntry : srcType->entries) {
140 ResourceEntry* dstEntry;
141 if (manglePackage) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800142 std::u16string mangledName = NameMangler::mangleEntry(srcPackage->name,
143 srcEntry->name);
144 if (allowNewResources) {
145 dstEntry = dstType->findOrCreateEntry(mangledName);
146 } else {
147 dstEntry = dstType->findEntry(mangledName);
148 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700149 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800150 if (allowNewResources) {
151 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
152 } else {
153 dstEntry = dstType->findEntry(srcEntry->name);
154 }
155 }
156
157 if (!dstEntry) {
158 mContext->getDiagnostics()->error(DiagMessage(src)
159 << "resource "
160 << ResourceNameRef(srcPackage->name,
161 srcType->type,
162 srcEntry->name)
163 << " does not override an existing resource");
164 mContext->getDiagnostics()->note(DiagMessage(src)
165 << "define an <add-resource> tag or use "
166 "--auto-add-overlay");
167 error = true;
168 continue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700169 }
170
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700171 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
172 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
173 if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
174 dstEntry->id && srcEntry->id &&
175 dstEntry->id.value() != srcEntry->id.value()) {
176 // Both entries are public and have different IDs.
177 mContext->getDiagnostics()->error(DiagMessage(src)
178 << "can not merge entry '"
179 << srcEntry->name
180 << "': conflicting public IDs");
181 error = true;
182 continue;
183 }
184
185 if (srcEntry->id) {
186 dstEntry->id = srcEntry->id;
187 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188 }
189
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700190 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
191 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
192 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
193 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700194 }
195
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800196 ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
197
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800198 for (auto& srcValue : srcEntry->values) {
199 ResourceConfigValue* dstValue = dstEntry->findValue(srcValue->config,
200 srcValue->product);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700201
Adam Lesinski6a008172016-02-02 17:02:58 -0800202 const bool stripConfig = mOptions.filter ?
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800203 !mOptions.filter->match(srcValue->config) : false;
Adam Lesinski6a008172016-02-02 17:02:58 -0800204
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800205 if (dstValue) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700206 const int collisionResult = ResourceTable::resolveValueCollision(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800207 dstValue->value.get(), srcValue->value.get());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800208 if (collisionResult == 0 && !overlay) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700209 // Error!
Adam Lesinskie78fd612015-10-22 12:48:43 -0700210 ResourceNameRef resourceName(srcPackage->name,
211 srcType->type,
212 srcEntry->name);
213
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800214 mContext->getDiagnostics()->error(DiagMessage(srcValue->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700215 << "resource '" << resourceName
216 << "' has a conflicting value for "
217 << "configuration ("
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800218 << srcValue->config << ")");
219 mContext->getDiagnostics()->note(DiagMessage(dstValue->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700220 << "originally defined here");
221 error = true;
222 continue;
223 } else if (collisionResult < 0) {
224 // Keep our existing value.
225 continue;
226 }
227
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700228 }
229
Adam Lesinski6a008172016-02-02 17:02:58 -0800230 if (stripConfig) {
231 continue;
232 }
233
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800234 if (!dstValue) {
235 // Force create the entry if we didn't have it.
236 dstValue = dstEntry->findOrCreateValue(srcValue->config, srcValue->product);
237 }
238
239 if (FileReference* f = valueCast<FileReference>(srcValue->value.get())) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800240 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) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800249 if (!callback(resName, srcValue->config, newFileRef.get(), f)) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800250 error = true;
251 continue;
252 }
253 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800254 dstValue->value = std::move(newFileRef);
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800255
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700256 } else {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800257 dstValue->value = std::unique_ptr<Value>(srcValue->value->clone(
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800258 &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)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800293 ->findOrCreateValue(fileDesc.config, {})
294 ->value = std::move(fileRef);
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800295
296 auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
297 FileReference* newFile, FileReference* oldFile) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800298 mFilesToMerge[ResourceKeyRef(name, config)] = FileToMerge{
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800299 file, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
300 return true;
301 };
302
303 return doMerge(file->getSource(), &table, pkg,
304 false /* mangle */, overlay /* overlay */, true /* allow new */, callback);
305}
306
307bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
308 return mergeFileImpl(fileDesc, file, false /* overlay */);
309}
310
311bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
312 return mergeFileImpl(fileDesc, file, true /* overlay */);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700313}
314
315} // namespace aapt