blob: db525467933f7c4df7eecc211bd71994ee4fb115 [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"
22#include "util/Util.h"
23
24#include <cassert>
25
26namespace aapt {
27
28TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) :
29 mContext(context), mMasterTable(outTable) {
30 // Create the desired package that all tables will be merged into.
31 mMasterPackage = mMasterTable->createPackage(
32 mContext->getCompilationPackage(), mContext->getPackageId());
33 assert(mMasterPackage && "package name or ID already taken");
34}
35
36bool TableMerger::merge(const Source& src, ResourceTable* table) {
37 const uint8_t desiredPackageId = mContext->getPackageId();
38
39 bool error = false;
40 for (auto& package : table->packages) {
41 // Warn of packages with an unrelated ID.
Adam Lesinski9ba47d82015-10-13 11:37:10 -070042 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043 mContext->getDiagnostics()->warn(DiagMessage(src)
44 << "ignoring package " << package->name);
45 continue;
46 }
47
48 bool manglePackage = false;
49 if (!package->name.empty() && mContext->getCompilationPackage() != package->name) {
50 manglePackage = true;
51 mMergedPackages.insert(package->name);
52 }
53
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.
59 if (!doMerge(src, table, package.get(), manglePackage)) {
60 error = true;
61 }
62 }
63 return !error;
64}
65
66bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
67 ResourceTablePackage* srcPackage, const bool manglePackage) {
68 bool error = false;
69
70 for (auto& srcType : srcPackage->types) {
71 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
Adam Lesinski9e10ac72015-10-16 14:37:48 -070072 if (srcType->symbolStatus.state == SymbolState::kPublic) {
73 if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
Adam Lesinski1ab598f2015-08-14 14:26:04 -070074 && dstType->id.value() == srcType->id.value()) {
75 // Both types are public and have different IDs.
76 mContext->getDiagnostics()->error(DiagMessage(src)
77 << "can not merge type '"
78 << srcType->type
79 << "': conflicting public IDs");
80 error = true;
81 continue;
82 }
83
Adam Lesinski9e10ac72015-10-16 14:37:48 -070084 dstType->symbolStatus = std::move(srcType->symbolStatus);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070085 dstType->id = srcType->id;
86 }
87
88 for (auto& srcEntry : srcType->entries) {
89 ResourceEntry* dstEntry;
90 if (manglePackage) {
91 dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
92 srcPackage->name, srcEntry->name));
93 } else {
94 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
95 }
96
Adam Lesinski9e10ac72015-10-16 14:37:48 -070097 if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
98 if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
99 if (dstEntry->symbolStatus.state == SymbolState::kPublic &&
100 dstEntry->id && srcEntry->id &&
101 dstEntry->id.value() != srcEntry->id.value()) {
102 // Both entries are public and have different IDs.
103 mContext->getDiagnostics()->error(DiagMessage(src)
104 << "can not merge entry '"
105 << srcEntry->name
106 << "': conflicting public IDs");
107 error = true;
108 continue;
109 }
110
111 if (srcEntry->id) {
112 dstEntry->id = srcEntry->id;
113 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700114 }
115
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700116 if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
117 dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
118 dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
119 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700120 }
121
122 for (ResourceConfigValue& srcValue : srcEntry->values) {
123 auto cmp = [](const ResourceConfigValue& a,
124 const ConfigDescription& b) -> bool {
125 return a.config < b;
126 };
127
128 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
129 srcValue.config, cmp);
130
131 if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
132 const int collisionResult = ResourceTable::resolveValueCollision(
133 iter->value.get(), srcValue.value.get());
134 if (collisionResult == 0) {
135 // Error!
136 ResourceNameRef resourceName =
137 { srcPackage->name, srcType->type, srcEntry->name };
138 mContext->getDiagnostics()->error(DiagMessage(srcValue.source)
139 << "resource '" << resourceName
140 << "' has a conflicting value for "
141 << "configuration ("
142 << srcValue.config << ")");
143 mContext->getDiagnostics()->note(DiagMessage(iter->source)
144 << "originally defined here");
145 error = true;
146 continue;
147 } else if (collisionResult < 0) {
148 // Keep our existing value.
149 continue;
150 }
151
152 } else {
153 // Insert a new value.
154 iter = dstEntry->values.insert(iter,
155 ResourceConfigValue{ srcValue.config });
156 }
157
158 iter->source = std::move(srcValue.source);
159 iter->comment = std::move(srcValue.comment);
160 if (manglePackage) {
161 iter->value = cloneAndMangle(srcTable, srcPackage->name,
162 srcValue.value.get());
163 } else {
164 iter->value = clone(srcValue.value.get());
165 }
166 }
167 }
168 }
169 return !error;
170}
171
172std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
173 const std::u16string& package,
174 Value* value) {
175 if (FileReference* f = valueCast<FileReference>(value)) {
176 // Mangle the path.
177 StringPiece16 prefix, entry, suffix;
178 if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
179 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
180 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
181 mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
182 return util::make_unique<FileReference>(mMasterTable->stringPool.makeRef(newPath));
183 }
184 }
185 return clone(value);
186}
187
188std::unique_ptr<Value> TableMerger::clone(Value* value) {
189 return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
190}
191
192} // namespace aapt