blob: 0bcb5baebf8d3293b0bb0fd866598d26413cba4d [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
37bool TableMerger::merge(const Source& src, ResourceTable* table) {
38 const uint8_t desiredPackageId = mContext->getPackageId();
39
40 bool error = false;
41 for (auto& package : table->packages) {
42 // Warn of packages with an unrelated ID.
Adam Lesinski9ba47d82015-10-13 11:37:10 -070043 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070044 mContext->getDiagnostics()->warn(DiagMessage(src)
45 << "ignoring package " << package->name);
46 continue;
47 }
48
49 bool manglePackage = false;
50 if (!package->name.empty() && mContext->getCompilationPackage() != package->name) {
51 manglePackage = true;
52 mMergedPackages.insert(package->name);
53 }
54
55 // Merge here. Once the entries are merged and mangled, any references to
56 // them are still valid. This is because un-mangled references are
57 // mangled, then looked up at resolution time.
58 // Also, when linking, we convert references with no package name to use
59 // the compilation package name.
60 if (!doMerge(src, table, package.get(), manglePackage)) {
61 error = true;
62 }
63 }
64 return !error;
65}
66
67bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
68 ResourceTablePackage* srcPackage, const bool manglePackage) {
69 bool error = false;
70
71 for (auto& srcType : srcPackage->types) {
72 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -070073 if (srcType->symbolStatus.state == SymbolState::kPublic) {
74 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -070075 && dstType->id.value() == srcType->id.value()) {
76 // Both types are public and have different IDs.
77 mContext->getDiagnostics()->error(DiagMessage(src)
78 << "can not merge type '"
79 << srcType->type
80 << "': conflicting public IDs");
81 error = true;
82 continue;
83 }
84
Adam Lesinski9e10ac72015-10-16 14:37:48 -070085 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070086 dstType->id = srcType->id;
87 }
88
89 for (auto& srcEntry : srcType->entries) {
90 ResourceEntry* dstEntry;
91 if (manglePackage) {
92 dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
93 srcPackage->name, srcEntry->name));
94 } else {
95 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
96 }
97
Adam Lesinski9e10ac72015-10-16 14:37:48 -070098 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
99 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
100 if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
101 dstEntry->id && srcEntry->id &&
102 dstEntry->id.value() != srcEntry->id.value()) {
103 // Both entries are public and have different IDs.
104 mContext->getDiagnostics()->error(DiagMessage(src)
105 << "can not merge entry '"
106 << srcEntry->name
107 << "': conflicting public IDs");
108 error = true;
109 continue;
110 }
111
112 if (srcEntry->id) {
113 dstEntry->id = srcEntry->id;
114 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700115 }
116
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700117 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
118 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
119 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
120 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700121 }
122
123 for (ResourceConfigValue& srcValue : srcEntry->values) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700124 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
Adam Lesinskib274e352015-11-06 15:14:35 -0800125 srcValue.config, cmp::lessThanConfig);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700126
127 if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
128 const int collisionResult = ResourceTable::resolveValueCollision(
129 iter->value.get(), srcValue.value.get());
130 if (collisionResult == 0) {
131 // Error!
Adam Lesinskie78fd612015-10-22 12:48:43 -0700132 ResourceNameRef resourceName(srcPackage->name,
133 srcType->type,
134 srcEntry->name);
135
136 mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700137 << "resource '" << resourceName
138 << "' has a conflicting value for "
139 << "configuration ("
140 << srcValue.config << ")");
Adam Lesinskie78fd612015-10-22 12:48:43 -0700141 mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700142 << "originally defined here");
143 error = true;
144 continue;
145 } else if (collisionResult < 0) {
146 // Keep our existing value.
147 continue;
148 }
149
150 } else {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700151 // Insert a place holder value. We will fill it in below.
152 iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700153 }
154
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700155 if (manglePackage) {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700156 iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700157 } else {
158 iter->value = clone(srcValue.value.get());
159 }
160 }
161 }
162 }
163 return !error;
164}
165
166std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
167 const std::u16string& package,
168 Value* value) {
169 if (FileReference* f = valueCast<FileReference>(value)) {
170 // Mangle the path.
171 StringPiece16 prefix, entry, suffix;
172 if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
173 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
174 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
175 mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
Adam Lesinskie78fd612015-10-22 12:48:43 -0700176 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
177 mMasterTable->stringPool.makeRef(newPath));
178 fileRef->setComment(f->getComment());
179 fileRef->setSource(f->getSource());
180 return std::move(fileRef);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700181 }
182 }
183 return clone(value);
184}
185
186std::unique_ptr<Value> TableMerger::clone(Value* value) {
187 return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
188}
189
190} // namespace aapt