TableMerger.cpp revision e78fd617ec60139a973a01925fa7adad31febb39
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
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.
43        if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
44            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);
73        if (srcType->symbolStatus.state == SymbolState::kPublic) {
74            if (dstType->symbolStatus.state == SymbolState::kPublic && dstType->id && srcType->id
75                    && 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
85            dstType->symbolStatus = std::move(srcType->symbolStatus);
86            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
98            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                    }
115                }
116
117                if (dstEntry->symbolStatus.state != SymbolState::kPublic &&
118                        dstEntry->symbolStatus.state != srcEntry->symbolStatus.state) {
119                    dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
120                }
121            }
122
123            for (ResourceConfigValue& srcValue : srcEntry->values) {
124                auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
125                                             srcValue.config, cmp::lessThan);
126
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!
132                        ResourceNameRef resourceName(srcPackage->name,
133                                                     srcType->type,
134                                                     srcEntry->name);
135
136                        mContext->getDiagnostics()->error(DiagMessage(srcValue.value->getSource())
137                                                          << "resource '" << resourceName
138                                                          << "' has a conflicting value for "
139                                                          << "configuration ("
140                                                          << srcValue.config << ")");
141                        mContext->getDiagnostics()->note(DiagMessage(iter->value->getSource())
142                                                         << "originally defined here");
143                        error = true;
144                        continue;
145                    } else if (collisionResult < 0) {
146                        // Keep our existing value.
147                        continue;
148                    }
149
150                } else {
151                    // Insert a place holder value. We will fill it in below.
152                    iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
153                }
154
155                if (manglePackage) {
156                    iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
157                } 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 });
176            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);
181        }
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
191