blob: 1eea410cf2a6e052ce3cd09b19918f13f1d46a2c [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"
18#include "ResourceValues.h"
19#include "ValueVisitor.h"
20
21#include "link/TableMerger.h"
Adam Lesinskie78fd612015-10-22 12:48:43 -070022#include "util/Comparators.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023#include "util/Util.h"
24
25#include <cassert>
26
27namespace aapt {
28
29TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) :
30 mContext(context), mMasterTable(outTable) {
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
Adam Lesinski83f22552015-11-07 11:51:23 -080037/**
38 * This will merge packages with the same package name (or no package name).
39 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -070040bool TableMerger::merge(const Source& src, ResourceTable* table) {
41 const uint8_t desiredPackageId = mContext->getPackageId();
42
43 bool error = false;
44 for (auto& package : table->packages) {
45 // Warn of packages with an unrelated ID.
Adam Lesinski9ba47d82015-10-13 11:37:10 -070046 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070047 mContext->getDiagnostics()->warn(DiagMessage(src)
48 << "ignoring package " << package->name);
49 continue;
50 }
51
Adam Lesinski83f22552015-11-07 11:51:23 -080052 if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
53 // Merge here. Once the entries are merged and mangled, any references to
54 // them are still valid. This is because un-mangled references are
55 // mangled, then looked up at resolution time.
56 // Also, when linking, we convert references with no package name to use
57 // the compilation package name.
58 if (!doMerge(src, table, package.get(), false)) {
59 error = true;
60 }
61 }
62 }
63 return !error;
64}
65
66/**
67 * This will merge and mangle resources from a static library.
68 */
69bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName,
70 ResourceTable* table) {
71 bool error = false;
72 for (auto& package : table->packages) {
73 // Warn of packages with an unrelated ID.
74 if (packageName != package->name) {
75 mContext->getDiagnostics()->warn(DiagMessage(src)
76 << "ignoring package " << package->name);
77 continue;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070078 }
79
Adam Lesinski83f22552015-11-07 11:51:23 -080080 bool mangle = packageName != mContext->getCompilationPackage();
81 mMergedPackages.insert(package->name);
82 if (!doMerge(src, table, package.get(), mangle)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070083 error = true;
84 }
85 }
86 return !error;
87}
88
89bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
90 ResourceTablePackage* srcPackage, const bool manglePackage) {
91 bool error = false;
92
93 for (auto& srcType : srcPackage->types) {
94 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -070095 if (srcType->symbolStatus.state == SymbolState::kPublic) {
96 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -070097 && dstType->id.value() == srcType->id.value()) {
98 // Both types are public and have different IDs.
99 mContext->getDiagnostics()->error(DiagMessage(src)
100 << "can not merge type '"
101 << srcType->type
102 << "': conflicting public IDs");
103 error = true;
104 continue;
105 }
106
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700107 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700108 dstType->id = srcType->id;
109 }
110
111 for (auto& srcEntry : srcType->entries) {
112 ResourceEntry* dstEntry;
113 if (manglePackage) {
114 dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
115 srcPackage->name, srcEntry->name));
116 } else {
117 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
118 }
119
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700120 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
121 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
122 if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
123 dstEntry->id && srcEntry->id &&
124 dstEntry->id.value() != srcEntry->id.value()) {
125 // Both entries are public and have different IDs.
126 mContext->getDiagnostics()->error(DiagMessage(src)
127 << "can not merge entry '"
128 << srcEntry->name
129 << "': conflicting public IDs");
130 error = true;
131 continue;
132 }
133
134 if (srcEntry->id) {
135 dstEntry->id = srcEntry->id;
136 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700137 }
138
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700139 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
140 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
141 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
142 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700143 }
144
145 for (ResourceConfigValue& srcValue : srcEntry->values) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700146 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
Adam Lesinskib274e352015-11-06 15:14:35 -0800147 srcValue.config, cmp::lessThanConfig);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700148
149 if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
150 const int collisionResult = ResourceTable::resolveValueCollision(
151 iter->value.get(), srcValue.value.get());
152 if (collisionResult == 0) {
153 // Error!
Adam Lesinskie78fd612015-10-22 12:48:43 -0700154 ResourceNameRef resourceName(srcPackage->name,
155 srcType->type,
156 srcEntry->name);
157
158 mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700159 << "resource '" << resourceName
160 << "' has a conflicting value for "
161 << "configuration ("
162 << srcValue.config << ")");
Adam Lesinskie78fd612015-10-22 12:48:43 -0700163 mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700164 << "originally defined here");
165 error = true;
166 continue;
167 } else if (collisionResult < 0) {
168 // Keep our existing value.
169 continue;
170 }
171
172 } else {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700173 // Insert a place holder value. We will fill it in below.
174 iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700175 }
176
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700177 if (manglePackage) {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700178 iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700179 } else {
180 iter->value = clone(srcValue.value.get());
181 }
182 }
183 }
184 }
185 return !error;
186}
187
188std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
189 const std::u16string& package,
190 Value* value) {
191 if (FileReference* f = valueCast<FileReference>(value)) {
192 // Mangle the path.
193 StringPiece16 prefix, entry, suffix;
194 if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
195 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
196 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
197 mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
Adam Lesinskie78fd612015-10-22 12:48:43 -0700198 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
199 mMasterTable->stringPool.makeRef(newPath));
200 fileRef->setComment(f->getComment());
201 fileRef->setSource(f->getSource());
202 return std::move(fileRef);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700203 }
204 }
205 return clone(value);
206}
207
208std::unique_ptr<Value> TableMerger::clone(Value* value) {
209 return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
210}
211
212} // namespace aapt