TableMerger.cpp revision fb48d292d9eb78a73f9473d90911ecb81da5fffd
1/*
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/Comparators.h"
23#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
37/**
38 * This will merge packages with the same package name (or no package name).
39 */
40bool TableMerger::merge(const Source& src, ResourceTable* table, bool overrideExisting) {
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.
46        if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
47            mContext->getDiagnostics()->warn(DiagMessage(src)
48                                             << "ignoring package " << package->name);
49            continue;
50        }
51
52        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, overrideExisting)) {
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;
78        }
79
80        bool mangle = packageName != mContext->getCompilationPackage();
81        mMergedPackages.insert(package->name);
82        if (!doMerge(src, table, package.get(), mangle, false)) {
83            error = true;
84        }
85    }
86    return !error;
87}
88
89bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
90                          ResourceTablePackage* srcPackage, const bool manglePackage,
91                          const bool overrideExisting) {
92    bool error = false;
93
94    for (auto& srcType : srcPackage->types) {
95        ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
96        if (srcType->symbolStatus.state == SymbolState::kPublic) {
97            if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
98                    && 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
108            dstType->symbolStatus = std::move(srcType->symbolStatus);
109            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
121            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                    }
138                }
139
140                if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
141                        dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
142                    dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
143                }
144            }
145
146            for (ResourceConfigValue& srcValue : srcEntry->values) {
147                auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
148                                             srcValue.config, cmp::lessThanConfig);
149
150                if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
151                    const int collisionResult = ResourceTable::resolveValueCollision(
152                            iter->value.get(), srcValue.value.get());
153                    if (collisionResult == 0 && !overrideExisting) {
154                        // Error!
155                        ResourceNameRef resourceName(srcPackage->name,
156                                                     srcType->type,
157                                                     srcEntry->name);
158
159                        mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
160                                                          << "resource '" << resourceName
161                                                          << "' has a conflicting value for "
162                                                          << "configuration ("
163                                                          << srcValue.config << ")");
164                        mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
165                                                         << "originally defined here");
166                        error = true;
167                        continue;
168                    } else if (collisionResult < 0) {
169                        // Keep our existing value.
170                        continue;
171                    }
172
173                } else {
174                    // Insert a place holder value. We will fill it in below.
175                    iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
176                }
177
178                if (manglePackage) {
179                    iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
180                } 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 });
199            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);
204        }
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
214