blob: a06a1bfe21a534c1896b5813a959d9fcd70b2f8b [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 Lesinskifb48d292015-11-07 15:52:13 -080040bool TableMerger::merge(const Source& src, ResourceTable* table, bool overrideExisting) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070041 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.
Adam Lesinskifb48d292015-11-07 15:52:13 -080058 if (!doMerge(src, table, package.get(), false, overrideExisting)) {
Adam Lesinski83f22552015-11-07 11:51:23 -080059 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);
Adam Lesinskifb48d292015-11-07 15:52:13 -080082 if (!doMerge(src, table, package.get(), mangle, false)) {
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,
Adam Lesinskifb48d292015-11-07 15:52:13 -080090 ResourceTablePackage* srcPackage, const bool manglePackage,
91 const bool overrideExisting) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070092 bool error = false;
93
94 for (auto& srcType : srcPackage->types) {
95 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -070096 if (srcType->symbolStatus.state == SymbolState::kPublic) {
97 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -070098 && dstType->id.value() == srcType->id.value()) {
99 // Both types are public and have different IDs.
100 mContext->getDiagnostics()->error(DiagMessage(src)
101 << "can not merge type '"
102 << srcType->type
103 << "': conflicting public IDs");
104 error = true;
105 continue;
106 }
107
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700108 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700109 dstType->id = srcType->id;
110 }
111
112 for (auto& srcEntry : srcType->entries) {
113 ResourceEntry* dstEntry;
114 if (manglePackage) {
115 dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
116 srcPackage->name, srcEntry->name));
117 } else {
118 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
119 }
120
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700121 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
122 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
123 if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
124 dstEntry->id && srcEntry->id &&
125 dstEntry->id.value() != srcEntry->id.value()) {
126 // Both entries are public and have different IDs.
127 mContext->getDiagnostics()->error(DiagMessage(src)
128 << "can not merge entry '"
129 << srcEntry->name
130 << "': conflicting public IDs");
131 error = true;
132 continue;
133 }
134
135 if (srcEntry->id) {
136 dstEntry->id = srcEntry->id;
137 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700138 }
139
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700140 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
141 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
142 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
143 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700144 }
145
146 for (ResourceConfigValue& srcValue : srcEntry->values) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700147 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
Adam Lesinskib274e352015-11-06 15:14:35 -0800148 srcValue.config, cmp::lessThanConfig);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700149
150 if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
151 const int collisionResult = ResourceTable::resolveValueCollision(
152 iter->value.get(), srcValue.value.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800153 if (collisionResult == 0 && !overrideExisting) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700154 // Error!
Adam Lesinskie78fd612015-10-22 12:48:43 -0700155 ResourceNameRef resourceName(srcPackage->name,
156 srcType->type,
157 srcEntry->name);
158
159 mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700160 << "resource '" << resourceName
161 << "' has a conflicting value for "
162 << "configuration ("
163 << srcValue.config << ")");
Adam Lesinskie78fd612015-10-22 12:48:43 -0700164 mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700165 << "originally defined here");
166 error = true;
167 continue;
168 } else if (collisionResult < 0) {
169 // Keep our existing value.
170 continue;
171 }
172
173 } else {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700174 // Insert a place holder value. We will fill it in below.
175 iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700176 }
177
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700178 if (manglePackage) {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700179 iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700180 } else {
181 iter->value = clone(srcValue.value.get());
182 }
183 }
184 }
185 }
186 return !error;
187}
188
189std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
190 const std::u16string& package,
191 Value* value) {
192 if (FileReference* f = valueCast<FileReference>(value)) {
193 // Mangle the path.
194 StringPiece16 prefix, entry, suffix;
195 if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
196 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
197 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
198 mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
Adam Lesinskie78fd612015-10-22 12:48:43 -0700199 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
200 mMasterTable->stringPool.makeRef(newPath));
201 fileRef->setComment(f->getComment());
202 fileRef->setSource(f->getSource());
203 return std::move(fileRef);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700204 }
205 }
206 return clone(value);
207}
208
209std::unique_ptr<Value> TableMerger::clone(Value* value) {
210 return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
211}
212
213} // namespace aapt