blob: de0fe404b867f6327cba6afd768b5fcd92a6edf9 [file] [log] [blame]
Adam Lesinski458b8772016-04-25 14:20:21 -07001/*
2 * Copyright (C) 2016 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
Adam Lesinskice5e56e2016-10-21 17:56:45 -070017#include "android-base/macros.h"
18
Adam Lesinski458b8772016-04-25 14:20:21 -070019#include "Flags.h"
20#include "ResourceTable.h"
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -070021#include "ValueVisitor.h"
Adam Lesinski458b8772016-04-25 14:20:21 -070022#include "io/ZipArchive.h"
23#include "process/IResourceTableConsumer.h"
24#include "process/SymbolTable.h"
25#include "unflatten/BinaryResourceParser.h"
26
Adam Lesinskid5083f62017-01-16 15:07:21 -080027using android::StringPiece;
28
Adam Lesinski458b8772016-04-25 14:20:21 -070029namespace aapt {
30
31class DiffContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070032 public:
Adam Lesinskice5e56e2016-10-21 17:56:45 -070033 const std::string& GetCompilationPackage() override { return empty_; }
Adam Lesinski458b8772016-04-25 14:20:21 -070034
Adam Lesinskice5e56e2016-10-21 17:56:45 -070035 uint8_t GetPackageId() override { return 0x0; }
Adam Lesinski458b8772016-04-25 14:20:21 -070036
Adam Lesinskice5e56e2016-10-21 17:56:45 -070037 IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
Adam Lesinski458b8772016-04-25 14:20:21 -070038
Adam Lesinskice5e56e2016-10-21 17:56:45 -070039 NameMangler* GetNameMangler() override { return &name_mangler_; }
Adam Lesinski458b8772016-04-25 14:20:21 -070040
Adam Lesinskice5e56e2016-10-21 17:56:45 -070041 SymbolTable* GetExternalSymbols() override { return &symbol_table_; }
Adam Lesinski458b8772016-04-25 14:20:21 -070042
Adam Lesinskice5e56e2016-10-21 17:56:45 -070043 bool IsVerbose() override { return false; }
Adam Lesinski458b8772016-04-25 14:20:21 -070044
Adam Lesinskice5e56e2016-10-21 17:56:45 -070045 int GetMinSdkVersion() override { return 0; }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070046
Adam Lesinskicacb28f2016-10-19 12:18:14 -070047 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -070048 std::string empty_;
49 StdErrDiagnostics diagnostics_;
50 NameMangler name_mangler_ = NameMangler(NameManglerPolicy{});
51 SymbolTable symbol_table_;
Adam Lesinski458b8772016-04-25 14:20:21 -070052};
53
54class LoadedApk {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070055 public:
56 LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
57 std::unique_ptr<ResourceTable> table)
Adam Lesinskice5e56e2016-10-21 17:56:45 -070058 : source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
Adam Lesinski458b8772016-04-25 14:20:21 -070059
Adam Lesinskice5e56e2016-10-21 17:56:45 -070060 io::IFileCollection* GetFileCollection() { return apk_.get(); }
Adam Lesinski458b8772016-04-25 14:20:21 -070061
Adam Lesinskice5e56e2016-10-21 17:56:45 -070062 ResourceTable* GetResourceTable() { return table_.get(); }
Adam Lesinski458b8772016-04-25 14:20:21 -070063
Adam Lesinskice5e56e2016-10-21 17:56:45 -070064 const Source& GetSource() { return source_; }
Adam Lesinski458b8772016-04-25 14:20:21 -070065
Adam Lesinskicacb28f2016-10-19 12:18:14 -070066 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -070067 Source source_;
68 std::unique_ptr<io::IFileCollection> apk_;
69 std::unique_ptr<ResourceTable> table_;
Adam Lesinski458b8772016-04-25 14:20:21 -070070
Adam Lesinskicacb28f2016-10-19 12:18:14 -070071 DISALLOW_COPY_AND_ASSIGN(LoadedApk);
Adam Lesinski458b8772016-04-25 14:20:21 -070072};
73
Adam Lesinskice5e56e2016-10-21 17:56:45 -070074static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
Adam Lesinskicacb28f2016-10-19 12:18:14 -070075 const StringPiece& path) {
76 Source source(path);
77 std::string error;
78 std::unique_ptr<io::ZipFileCollection> apk =
Adam Lesinskice5e56e2016-10-21 17:56:45 -070079 io::ZipFileCollection::Create(path, &error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070080 if (!apk) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081 context->GetDiagnostics()->Error(DiagMessage(source) << error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070082 return {};
83 }
Adam Lesinski458b8772016-04-25 14:20:21 -070084
Adam Lesinskice5e56e2016-10-21 17:56:45 -070085 io::IFile* file = apk->FindFile("resources.arsc");
Adam Lesinskicacb28f2016-10-19 12:18:14 -070086 if (!file) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070087 context->GetDiagnostics()->Error(DiagMessage(source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -070088 << "no resources.arsc found");
89 return {};
90 }
Adam Lesinski458b8772016-04-25 14:20:21 -070091
Adam Lesinskice5e56e2016-10-21 17:56:45 -070092 std::unique_ptr<io::IData> data = file->OpenAsData();
Adam Lesinskicacb28f2016-10-19 12:18:14 -070093 if (!data) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070094 context->GetDiagnostics()->Error(DiagMessage(source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -070095 << "could not open resources.arsc");
96 return {};
97 }
Adam Lesinski458b8772016-04-25 14:20:21 -070098
Adam Lesinskicacb28f2016-10-19 12:18:14 -070099 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
100 BinaryResourceParser parser(context, table.get(), source, data->data(),
101 data->size());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700102 if (!parser.Parse()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700103 return {};
104 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700105
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700106 return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
Adam Lesinski458b8772016-04-25 14:20:21 -0700107}
108
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700109static void EmitDiffLine(const Source& source, const StringPiece& message) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700110 std::cerr << source << ": " << message << "\n";
Adam Lesinski458b8772016-04-25 14:20:21 -0700111}
112
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700113static bool IsSymbolVisibilityDifferent(const Symbol& symbol_a,
114 const Symbol& symbol_b) {
115 return symbol_a.state != symbol_b.state;
Adam Lesinski458b8772016-04-25 14:20:21 -0700116}
117
118template <typename Id>
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700119static bool IsIdDiff(const Symbol& symbol_a, const Maybe<Id>& id_a,
120 const Symbol& symbol_b, const Maybe<Id>& id_b) {
121 if (symbol_a.state == SymbolState::kPublic ||
122 symbol_b.state == SymbolState::kPublic) {
123 return id_a != id_b;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700124 }
125 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700126}
127
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700128static bool EmitResourceConfigValueDiff(
129 IAaptContext* context, LoadedApk* apk_a, ResourceTablePackage* pkg_a,
130 ResourceTableType* type_a, ResourceEntry* entry_a,
131 ResourceConfigValue* config_value_a, LoadedApk* apk_b,
132 ResourceTablePackage* pkg_b, ResourceTableType* type_b,
133 ResourceEntry* entry_b, ResourceConfigValue* config_value_b) {
134 Value* value_a = config_value_a->value.get();
135 Value* value_b = config_value_b->value.get();
136 if (!value_a->Equals(value_b)) {
137 std::stringstream str_stream;
138 str_stream << "value " << pkg_a->name << ":" << type_a->type << "/"
139 << entry_a->name << " config=" << config_value_a->config
140 << " does not match:\n";
141 value_a->Print(&str_stream);
142 str_stream << "\n vs \n";
143 value_b->Print(&str_stream);
144 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700145 return true;
146 }
147 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700148}
149
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700150static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
151 ResourceTablePackage* pkg_a,
152 ResourceTableType* type_a,
153 ResourceEntry* entry_a, LoadedApk* apk_b,
154 ResourceTablePackage* pkg_b,
155 ResourceTableType* type_b,
156 ResourceEntry* entry_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700157 bool diff = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700158 for (std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
159 ResourceConfigValue* config_value_b =
160 entry_b->FindValue(config_value_a->config);
161 if (!config_value_b) {
162 std::stringstream str_stream;
163 str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/"
164 << entry_a->name << " config=" << config_value_a->config;
165 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700166 diff = true;
167 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700168 diff |= EmitResourceConfigValueDiff(
169 context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(), apk_b,
170 pkg_b, type_b, entry_b, config_value_b);
Adam Lesinski458b8772016-04-25 14:20:21 -0700171 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700172 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700173
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700174 // Check for any newly added config values.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700175 for (std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
176 ResourceConfigValue* config_value_a =
177 entry_a->FindValue(config_value_b->config);
178 if (!config_value_a) {
179 std::stringstream str_stream;
180 str_stream << "new config " << pkg_b->name << ":" << type_b->type << "/"
181 << entry_b->name << " config=" << config_value_b->config;
182 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700183 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700184 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700185 }
186 return false;
Adam Lesinski458b8772016-04-25 14:20:21 -0700187}
188
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700189static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
190 ResourceTablePackage* pkg_a,
191 ResourceTableType* type_a, LoadedApk* apk_b,
192 ResourceTablePackage* pkg_b,
193 ResourceTableType* type_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 bool diff = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700195 for (std::unique_ptr<ResourceEntry>& entry_a : type_a->entries) {
196 ResourceEntry* entry_b = type_b->FindEntry(entry_a->name);
197 if (!entry_b) {
198 std::stringstream str_stream;
199 str_stream << "missing " << pkg_a->name << ":" << type_a->type << "/"
200 << entry_a->name;
201 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700202 diff = true;
203 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700204 if (IsSymbolVisibilityDifferent(entry_a->symbol_status,
205 entry_b->symbol_status)) {
206 std::stringstream str_stream;
207 str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
208 << " has different visibility (";
209 if (entry_b->symbol_status.state == SymbolState::kPublic) {
210 str_stream << "PUBLIC";
Adam Lesinski458b8772016-04-25 14:20:21 -0700211 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700212 str_stream << "PRIVATE";
Adam Lesinski458b8772016-04-25 14:20:21 -0700213 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700214 str_stream << " vs ";
215 if (entry_a->symbol_status.state == SymbolState::kPublic) {
216 str_stream << "PUBLIC";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700217 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700218 str_stream << "PRIVATE";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700219 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700220 str_stream << ")";
221 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700222 diff = true;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700223 } else if (IsIdDiff(entry_a->symbol_status, entry_a->id,
224 entry_b->symbol_status, entry_b->id)) {
225 std::stringstream str_stream;
226 str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
227 << " has different public ID (";
228 if (entry_b->id) {
229 str_stream << "0x" << std::hex << entry_b->id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700230 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700231 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700232 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700233 str_stream << " vs ";
234 if (entry_a->id) {
235 str_stream << "0x " << std::hex << entry_a->id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700236 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700237 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700238 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700239 str_stream << ")";
240 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700241 diff = true;
242 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700243 diff |=
244 EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a.get(),
245 apk_b, pkg_b, type_b, entry_b);
Adam Lesinski458b8772016-04-25 14:20:21 -0700246 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700247 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700248
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700249 // Check for any newly added entries.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700250 for (std::unique_ptr<ResourceEntry>& entry_b : type_b->entries) {
251 ResourceEntry* entry_a = type_a->FindEntry(entry_b->name);
252 if (!entry_a) {
253 std::stringstream str_stream;
254 str_stream << "new entry " << pkg_b->name << ":" << type_b->type << "/"
255 << entry_b->name;
256 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700257 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700258 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700259 }
260 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700261}
262
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700263static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
264 ResourceTablePackage* pkg_a,
265 LoadedApk* apk_b,
266 ResourceTablePackage* pkg_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700267 bool diff = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700268 for (std::unique_ptr<ResourceTableType>& type_a : pkg_a->types) {
269 ResourceTableType* type_b = pkg_b->FindType(type_a->type);
270 if (!type_b) {
271 std::stringstream str_stream;
272 str_stream << "missing " << pkg_a->name << ":" << type_a->type;
273 EmitDiffLine(apk_a->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700274 diff = true;
275 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700276 if (IsSymbolVisibilityDifferent(type_a->symbol_status,
277 type_b->symbol_status)) {
278 std::stringstream str_stream;
279 str_stream << pkg_a->name << ":" << type_a->type
280 << " has different visibility (";
281 if (type_b->symbol_status.state == SymbolState::kPublic) {
282 str_stream << "PUBLIC";
Adam Lesinski458b8772016-04-25 14:20:21 -0700283 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700284 str_stream << "PRIVATE";
Adam Lesinski458b8772016-04-25 14:20:21 -0700285 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700286 str_stream << " vs ";
287 if (type_a->symbol_status.state == SymbolState::kPublic) {
288 str_stream << "PUBLIC";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700289 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700290 str_stream << "PRIVATE";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700291 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700292 str_stream << ")";
293 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700294 diff = true;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700295 } else if (IsIdDiff(type_a->symbol_status, type_a->id,
296 type_b->symbol_status, type_b->id)) {
297 std::stringstream str_stream;
298 str_stream << pkg_a->name << ":" << type_a->type
299 << " has different public ID (";
300 if (type_b->id) {
301 str_stream << "0x" << std::hex << type_b->id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700302 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700303 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700304 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700305 str_stream << " vs ";
306 if (type_a->id) {
307 str_stream << "0x " << std::hex << type_a->id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700308 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700309 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700310 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700311 str_stream << ")";
312 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700313 diff = true;
314 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700315 diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b,
316 pkg_b, type_b);
Adam Lesinski458b8772016-04-25 14:20:21 -0700317 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700318 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700319
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700320 // Check for any newly added types.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700321 for (std::unique_ptr<ResourceTableType>& type_b : pkg_b->types) {
322 ResourceTableType* type_a = pkg_a->FindType(type_b->type);
323 if (!type_a) {
324 std::stringstream str_stream;
325 str_stream << "new type " << pkg_b->name << ":" << type_b->type;
326 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700327 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700328 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700329 }
330 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700331}
332
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700333static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a,
334 LoadedApk* apk_b) {
335 ResourceTable* table_a = apk_a->GetResourceTable();
336 ResourceTable* table_b = apk_b->GetResourceTable();
Adam Lesinski458b8772016-04-25 14:20:21 -0700337
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700338 bool diff = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700339 for (std::unique_ptr<ResourceTablePackage>& pkg_a : table_a->packages) {
340 ResourceTablePackage* pkg_b = table_b->FindPackage(pkg_a->name);
341 if (!pkg_b) {
342 std::stringstream str_stream;
343 str_stream << "missing package " << pkg_a->name;
344 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700345 diff = true;
346 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700347 if (pkg_a->id != pkg_b->id) {
348 std::stringstream str_stream;
349 str_stream << "package '" << pkg_a->name << "' has different id (";
350 if (pkg_b->id) {
351 str_stream << "0x" << std::hex << pkg_b->id.value();
Adam Lesinski458b8772016-04-25 14:20:21 -0700352 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700353 str_stream << "none";
Adam Lesinski458b8772016-04-25 14:20:21 -0700354 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355 str_stream << " vs ";
356 if (pkg_a->id) {
357 str_stream << "0x" << std::hex << pkg_a->id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700358 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700359 str_stream << "none";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700360 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700361 str_stream << ")";
362 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700363 diff = true;
364 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700365 diff |=
366 EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b);
Adam Lesinski458b8772016-04-25 14:20:21 -0700367 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700368 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700369
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700370 // Check for any newly added packages.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700371 for (std::unique_ptr<ResourceTablePackage>& pkg_b : table_b->packages) {
372 ResourceTablePackage* pkg_a = table_a->FindPackage(pkg_b->name);
373 if (!pkg_a) {
374 std::stringstream str_stream;
375 str_stream << "new package " << pkg_b->name;
376 EmitDiffLine(apk_b->GetSource(), str_stream.str());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700377 diff = true;
Adam Lesinski458b8772016-04-25 14:20:21 -0700378 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700379 }
380 return diff;
Adam Lesinski458b8772016-04-25 14:20:21 -0700381}
382
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700383class ZeroingReferenceVisitor : public ValueVisitor {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700384 public:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700385 using ValueVisitor::Visit;
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700386
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700387 void Visit(Reference* ref) override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700388 if (ref->name && ref->id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700389 if (ref->id.value().package_id() == 0x7f) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700390 ref->id = {};
391 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700392 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700393 }
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700394};
395
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700396static void ZeroOutAppReferences(ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700397 ZeroingReferenceVisitor visitor;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700398 VisitAllValuesInTable(table, &visitor);
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700399}
400
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700401int Diff(const std::vector<StringPiece>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700402 DiffContext context;
Adam Lesinski458b8772016-04-25 14:20:21 -0700403
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700404 Flags flags;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700405 if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700406 return 1;
407 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700408
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700409 if (flags.GetArgs().size() != 2u) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700410 std::cerr << "must have two apks as arguments.\n\n";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700411 flags.Usage("aapt2 diff", &std::cerr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700412 return 1;
413 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700414
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700415 std::unique_ptr<LoadedApk> apk_a =
416 LoadApkFromPath(&context, flags.GetArgs()[0]);
417 std::unique_ptr<LoadedApk> apk_b =
418 LoadApkFromPath(&context, flags.GetArgs()[1]);
419 if (!apk_a || !apk_b) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700420 return 1;
421 }
Adam Lesinski458b8772016-04-25 14:20:21 -0700422
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700423 // Zero out Application IDs in references.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700424 ZeroOutAppReferences(apk_a->GetResourceTable());
425 ZeroOutAppReferences(apk_b->GetResourceTable());
Adam Lesinski5e8fa3a2016-06-27 16:21:42 -0700426
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700427 if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700428 // We emitted a diff, so return 1 (failure).
429 return 1;
430 }
431 return 0;
Adam Lesinski458b8772016-04-25 14:20:21 -0700432}
433
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700434} // namespace aapt