blob: 3437ac0d9a7fc9759eb4350590a1a2267e69ecb3 [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 "AppInfo.h"
18#include "Debug.h"
19#include "Flags.h"
Adam Lesinski6a008172016-02-02 17:02:58 -080020#include "Locale.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "NameMangler.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080022#include "ResourceUtils.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023#include "compile/IdAssigner.h"
Adam Lesinski6a008172016-02-02 17:02:58 -080024#include "filter/ConfigFilter.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070025#include "flatten/Archive.h"
26#include "flatten/TableFlattener.h"
27#include "flatten/XmlFlattener.h"
Adam Lesinskia40e9722015-11-24 19:11:46 -080028#include "io/FileSystem.h"
29#include "io/ZipArchive.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070030#include "java/JavaClassGenerator.h"
31#include "java/ManifestClassGenerator.h"
32#include "java/ProguardRules.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033#include "link/Linkers.h"
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -080034#include "link/ProductFilter.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080035#include "link/ReferenceLinker.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080036#include "link/ManifestFixer.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070037#include "link/TableMerger.h"
38#include "process/IResourceTableConsumer.h"
39#include "process/SymbolTable.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080040#include "proto/ProtoSerialize.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070041#include "unflatten/BinaryResourceParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070042#include "util/Files.h"
43#include "util/StringPiece.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080044#include "xml/XmlDom.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070045
Adam Lesinski59e04c62016-02-04 15:59:23 -080046#include <google/protobuf/io/coded_stream.h>
47
Adam Lesinski1ab598f2015-08-14 14:26:04 -070048#include <fstream>
49#include <sys/stat.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070050#include <vector>
51
52namespace aapt {
53
54struct LinkOptions {
55 std::string outputPath;
56 std::string manifestPath;
57 std::vector<std::string> includePaths;
Adam Lesinskifb48d292015-11-07 15:52:13 -080058 std::vector<std::string> overlayFiles;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070059 Maybe<std::string> generateJavaClassPath;
Adam Lesinski52364f72016-01-11 13:10:24 -080060 Maybe<std::u16string> customJavaPackage;
61 std::set<std::u16string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 Maybe<std::string> generateProguardRulesPath;
63 bool noAutoVersion = false;
64 bool staticLib = false;
Adam Lesinskief9c5012016-01-22 14:09:53 -080065 bool generateNonFinalIds = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070066 bool verbose = false;
67 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080068 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080069 bool doNotCompressAnything = false;
70 std::vector<std::string> extensionsToNotCompress;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070071 Maybe<std::u16string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080072 ManifestFixerOptions manifestFixerOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -080073 IConfigFilter* configFilter = nullptr;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -080074 std::unordered_set<std::string> products;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070075};
76
77struct LinkContext : public IAaptContext {
78 StdErrDiagnostics mDiagnostics;
79 std::unique_ptr<NameMangler> mNameMangler;
80 std::u16string mCompilationPackage;
81 uint8_t mPackageId;
82 std::unique_ptr<ISymbolTable> mSymbols;
83
84 IDiagnostics* getDiagnostics() override {
85 return &mDiagnostics;
86 }
87
88 NameMangler* getNameMangler() override {
89 return mNameMangler.get();
90 }
91
92 StringPiece16 getCompilationPackage() override {
93 return mCompilationPackage;
94 }
95
96 uint8_t getPackageId() override {
97 return mPackageId;
98 }
99
100 ISymbolTable* getExternalSymbols() override {
101 return mSymbols.get();
102 }
103};
104
Adam Lesinskifb48d292015-11-07 15:52:13 -0800105class LinkCommand {
106public:
Adam Lesinski6a008172016-02-02 17:02:58 -0800107 LinkCommand(LinkContext* context, const LinkOptions& options) :
108 mOptions(options), mContext(context), mFinalTable(), mFileCollection(nullptr) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800109 std::unique_ptr<io::FileCollection> fileCollection =
110 util::make_unique<io::FileCollection>();
111
112 // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
113 mFileCollection = fileCollection.get();
114
115 // Move it to the collection.
116 mCollections.push_back(std::move(fileCollection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800117 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700118
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700119 /**
120 * Creates a SymbolTable that loads symbols from the various APKs and caches the
121 * results for faster lookup.
122 */
123 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
124 AssetManagerSymbolTableBuilder builder;
125 for (const std::string& path : mOptions.includePaths) {
126 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800127 mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700128 }
129
130 std::unique_ptr<android::AssetManager> assetManager =
131 util::make_unique<android::AssetManager>();
132 int32_t cookie = 0;
133 if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800134 mContext->getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800135 DiagMessage(path) << "failed to load include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700136 return {};
137 }
138 builder.add(std::move(assetManager));
139 }
140 return builder.build();
141 }
142
Adam Lesinskia40e9722015-11-24 19:11:46 -0800143 std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700144 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinski6a008172016-02-02 17:02:58 -0800145 BinaryResourceParser parser(mContext, table.get(), source, data, len);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700146 if (!parser.parse()) {
147 return {};
148 }
149 return table;
150 }
151
Adam Lesinski59e04c62016-02-04 15:59:23 -0800152 std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
153 const void* data, size_t len) {
154 pb::ResourceTable pbTable;
155 if (!pbTable.ParseFromArray(data, len)) {
156 mContext->getDiagnostics()->error(DiagMessage(source) << "invalid compiled table");
157 return {};
158 }
159
160 std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source,
161 mContext->getDiagnostics());
162 if (!table) {
163 return {};
164 }
165 return table;
166 }
167
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700168 /**
169 * Inflates an XML file from the source path.
170 */
Adam Lesinskia40e9722015-11-24 19:11:46 -0800171 static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700172 std::ifstream fin(path, std::ifstream::binary);
173 if (!fin) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800174 diag->error(DiagMessage(path) << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700175 return {};
176 }
177
Adam Lesinskia40e9722015-11-24 19:11:46 -0800178 return xml::inflate(&fin, diag, Source(path));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700179 }
180
Adam Lesinskia40e9722015-11-24 19:11:46 -0800181 static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(
182 const Source& source,
183 const void* data, size_t len,
184 IDiagnostics* diag) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800185 CompiledFileInputStream inputStream(data, len);
186 if (!inputStream.CompiledFile()) {
187 diag->error(DiagMessage(source) << "invalid compiled file header");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188 return {};
189 }
190
Adam Lesinski59e04c62016-02-04 15:59:23 -0800191 const uint8_t* xmlData = reinterpret_cast<const uint8_t*>(inputStream.data());
192 const size_t xmlDataLen = inputStream.size();
193
194 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(xmlData, xmlDataLen, diag, source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700195 if (!xmlRes) {
196 return {};
197 }
198 return xmlRes;
199 }
200
Adam Lesinskia40e9722015-11-24 19:11:46 -0800201 static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
202 const void* data, size_t len,
203 IDiagnostics* diag) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800204 CompiledFileInputStream inputStream(data, len);
205 const pb::CompiledFile* pbFile = inputStream.CompiledFile();
206 if (!pbFile) {
207 diag->error(DiagMessage(source) << "invalid compiled file header");
208 return {};
209 }
210
211 std::unique_ptr<ResourceFile> resFile = deserializeCompiledFileFromPb(*pbFile, source,
212 diag);
213 if (!resFile) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700214 return {};
215 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800216 return resFile;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700217 }
218
Adam Lesinski52364f72016-01-11 13:10:24 -0800219 uint32_t getCompressionFlags(const StringPiece& str) {
220 if (mOptions.doNotCompressAnything) {
221 return 0;
222 }
223
224 for (const std::string& extension : mOptions.extensionsToNotCompress) {
225 if (util::stringEndsWith<char>(str, extension)) {
226 return 0;
227 }
228 }
229 return ArchiveEntry::kCompress;
230 }
231
232 bool copyFileToArchive(io::IFile* file, const std::string& outPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700233 IArchiveWriter* writer) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800234 std::unique_ptr<io::IData> data = file->openAsData();
235 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800236 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800237 << "failed to open file");
238 return false;
239 }
240
Adam Lesinski59e04c62016-02-04 15:59:23 -0800241 CompiledFileInputStream inputStream(data->data(), data->size());
242 if (!inputStream.CompiledFile()) {
243 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
244 << "invalid compiled file header");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700245 return false;
246 }
247
Adam Lesinski52364f72016-01-11 13:10:24 -0800248 if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800249 if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()),
250 inputStream.size())) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800251 if (writer->finishEntry()) {
252 return true;
253 }
254 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700255 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800256
Adam Lesinski6a008172016-02-02 17:02:58 -0800257 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800258 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
259 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700260 }
261
Adam Lesinski467f1712015-11-16 17:35:44 -0800262 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700263 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800264 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700265 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
266 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
267 return AppInfo{ packageAttr->value };
268 }
269 }
270 }
271 return {};
272 }
273
Adam Lesinski979ccb22016-01-11 10:42:19 -0800274 /**
275 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
276 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
277 * is an error and false is returned.
278 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800279 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800280 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800281 return mContext->getCompilationPackage() != pkg->name ||
Adam Lesinski979ccb22016-01-11 10:42:19 -0800282 !pkg->id ||
Adam Lesinski6a008172016-02-02 17:02:58 -0800283 pkg->id.value() != mContext->getPackageId();
Adam Lesinski979ccb22016-01-11 10:42:19 -0800284 };
285
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700286 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800287 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800288 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700289 // We have a package that is not related to the one we're building!
290 for (const auto& type : package->types) {
291 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800292 ResourceNameRef resName(package->name, type->type, entry->name);
293
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700294 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800295 // Special case the occurrence of an ID that is being generated for the
296 // 'android' package. This is due to legacy reasons.
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800297 if (valueCast<Id>(configValue->value.get()) &&
Adam Lesinski979ccb22016-01-11 10:42:19 -0800298 package->name == u"android") {
Adam Lesinski6a008172016-02-02 17:02:58 -0800299 mContext->getDiagnostics()->warn(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800300 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800301 << "generated id '" << resName
302 << "' for external package '" << package->name
303 << "'");
304 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800305 mContext->getDiagnostics()->error(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800306 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800307 << "defined resource '" << resName
308 << "' for external package '" << package->name
309 << "'");
310 error = true;
311 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700312 }
313 }
314 }
315 }
316 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800317
318 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
319 isExtPackageFunc);
320 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700321 return !error;
322 }
323
324 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
325 if (mOptions.outputToDirectory) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800326 return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700327 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800328 return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700329 }
330 }
331
332 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
333 BigBuffer buffer(1024);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800334 TableFlattener flattener(&buffer);
Adam Lesinski6a008172016-02-02 17:02:58 -0800335 if (!flattener.consume(mContext, table)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700336 return false;
337 }
338
Adam Lesinskia40e9722015-11-24 19:11:46 -0800339 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
340 if (writer->writeEntry(buffer)) {
341 if (writer->finishEntry()) {
342 return true;
343 }
344 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700345 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800346
Adam Lesinski6a008172016-02-02 17:02:58 -0800347 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800348 DiagMessage() << "failed to write resources.arsc to archive");
349 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700350 }
351
Adam Lesinski467f1712015-11-16 17:35:44 -0800352 bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700353 IArchiveWriter* writer) {
354 BigBuffer buffer(1024);
355 XmlFlattenerOptions options = {};
356 options.keepRawValues = mOptions.staticLib;
357 options.maxSdkLevel = maxSdkLevel;
358 XmlFlattener flattener(&buffer, options);
Adam Lesinski6a008172016-02-02 17:02:58 -0800359 if (!flattener.consume(mContext, xmlRes)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700360 return false;
361 }
362
Adam Lesinskia40e9722015-11-24 19:11:46 -0800363 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
364 if (writer->writeEntry(buffer)) {
365 if (writer->finishEntry()) {
366 return true;
367 }
368 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700369 }
Adam Lesinski6a008172016-02-02 17:02:58 -0800370 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800371 DiagMessage() << "failed to write " << path << " to archive");
372 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700373 }
374
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700375 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
376 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700377 if (!mOptions.generateJavaClassPath) {
378 return true;
379 }
380
381 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700382 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700383 file::mkdirs(outPath);
384 file::appendPath(&outPath, "R.java");
385
386 std::ofstream fout(outPath, std::ofstream::binary);
387 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800388 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700389 return false;
390 }
391
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700392 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700393 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800394 mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700395 return false;
396 }
397 return true;
398 }
399
Adam Lesinski467f1712015-11-16 17:35:44 -0800400 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700401 if (!mOptions.generateJavaClassPath) {
402 return true;
403 }
404
405 std::string outPath = mOptions.generateJavaClassPath.value();
406 file::appendPath(&outPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800407 file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700408 file::mkdirs(outPath);
409 file::appendPath(&outPath, "Manifest.java");
410
411 std::ofstream fout(outPath, std::ofstream::binary);
412 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800413 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700414 return false;
415 }
416
417 ManifestClassGenerator generator;
Adam Lesinski6a008172016-02-02 17:02:58 -0800418 if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(),
Adam Lesinskica5638f2015-10-21 14:42:43 -0700419 manifestXml, &fout)) {
420 return false;
421 }
422
423 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800424 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700425 return false;
426 }
427 return true;
428 }
429
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700430 bool writeProguardFile(const proguard::KeepSet& keepSet) {
431 if (!mOptions.generateProguardRulesPath) {
432 return true;
433 }
434
435 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
436 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800437 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700438 return false;
439 }
440
441 proguard::writeKeepSet(&fout, keepSet);
442 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800443 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700444 return false;
445 }
446 return true;
447 }
448
Adam Lesinskifb48d292015-11-07 15:52:13 -0800449 bool mergeStaticLibrary(const std::string& input) {
450 // TODO(adamlesinski): Load resources from a static library APK and merge the table into
451 // TableMerger.
Adam Lesinski6a008172016-02-02 17:02:58 -0800452 mContext->getDiagnostics()->warn(DiagMessage()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800453 << "linking static libraries not supported yet: "
454 << input);
455 return true;
456 }
457
Adam Lesinskia40e9722015-11-24 19:11:46 -0800458 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800459 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800460 mContext->getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800461 }
462
Adam Lesinskia40e9722015-11-24 19:11:46 -0800463 std::unique_ptr<io::IData> data = file->openAsData();
464 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800465 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800466 << "failed to open file");
467 return false;
468 }
469
Adam Lesinski59e04c62016-02-04 15:59:23 -0800470 std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(), data->data(),
471 data->size());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800472 if (!table) {
473 return false;
474 }
475
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800476 bool result = false;
477 if (override) {
478 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
479 } else {
480 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800481 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800482 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800483 }
484
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800485 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
486 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800487 mContext->getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800488 }
489
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800490 bool result = false;
491 if (overlay) {
492 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800493 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800494 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800495 }
496
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800497 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800498 return false;
499 }
500
501 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800502 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800503 if (exportedSymbol.name.package.empty()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800504 exportedSymbol.name.package = mContext->getCompilationPackage().toString();
Adam Lesinskifb48d292015-11-07 15:52:13 -0800505 }
506
507 ResourceNameRef resName = exportedSymbol.name;
508
Adam Lesinski6a008172016-02-02 17:02:58 -0800509 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800510 exportedSymbol.name);
511 if (mangledName) {
512 resName = mangledName.value();
513 }
514
515 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800516 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800517 bool result = mFinalTable.addResourceAllowMangled(resName,
518 ConfigDescription::defaultConfig(),
519 std::string(),
520 std::move(id),
Adam Lesinski6a008172016-02-02 17:02:58 -0800521 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800522 if (!result) {
523 return false;
524 }
525 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800526 return true;
527 }
528
Adam Lesinskia40e9722015-11-24 19:11:46 -0800529 /**
530 * Creates an io::IFileCollection from the ZIP archive and processes the files within.
531 */
532 bool mergeArchive(const std::string& input, bool override) {
533 std::string errorStr;
534 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
535 input, &errorStr);
536 if (!collection) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800537 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800538 return false;
539 }
540
541 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800542 for (auto iter = collection->iterator(); iter->hasNext(); ) {
543 if (!processFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800544 error = true;
545 }
546 }
547
548 // Make sure to move the collection into the set of IFileCollections.
549 mCollections.push_back(std::move(collection));
550 return !error;
551 }
552
553 bool processFile(const std::string& path, bool override) {
Adam Lesinski656a5772016-01-14 15:17:41 -0800554 if (util::stringEndsWith<char>(path, ".flata") ||
555 util::stringEndsWith<char>(path, ".jar") ||
556 util::stringEndsWith<char>(path, ".jack") ||
557 util::stringEndsWith<char>(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800558 return mergeArchive(path, override);
559 }
560
561 io::IFile* file = mFileCollection->insertFile(path);
562 return processFile(file, override);
563 }
564
565 bool processFile(io::IFile* file, bool override) {
566 const Source& src = file->getSource();
567 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
568 return mergeResourceTable(file, override);
Adam Lesinski52364f72016-01-11 13:10:24 -0800569 } else if (util::stringEndsWith<char>(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -0800570 // Try opening the file and looking for an Export header.
571 std::unique_ptr<io::IData> data = file->openAsData();
572 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800573 mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
Adam Lesinskia40e9722015-11-24 19:11:46 -0800574 return false;
575 }
576
577 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
Adam Lesinski6a008172016-02-02 17:02:58 -0800578 src, data->data(), data->size(), mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800579 if (resourceFile) {
580 return mergeCompiledFile(file, std::move(resourceFile), override);
581 }
Adam Lesinskic446a732016-01-21 11:04:46 -0800582
583 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800584 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800585
Adam Lesinskic446a732016-01-21 11:04:46 -0800586 // Ignore non .flat files. This could be classes.dex or something else that happens
587 // to be in an archive.
588 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800589 }
590
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700591 int run(const std::vector<std::string>& inputFiles) {
592 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800593 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800594 mContext->getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700595 if (!manifestXml) {
596 return 1;
597 }
598
599 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800600 mContext->mCompilationPackage = maybeAppInfo.value().package;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700601 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800602 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700603 << "no package specified in <manifest> tag");
604 return 1;
605 }
606
Adam Lesinski6a008172016-02-02 17:02:58 -0800607 if (!util::isJavaPackageName(mContext->mCompilationPackage)) {
608 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700609 << "invalid package name '"
Adam Lesinski6a008172016-02-02 17:02:58 -0800610 << mContext->mCompilationPackage
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700611 << "'");
612 return 1;
613 }
614
Adam Lesinski6a008172016-02-02 17:02:58 -0800615 mContext->mNameMangler = util::make_unique<NameMangler>(
616 NameManglerPolicy{ mContext->mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700617
Adam Lesinski6a008172016-02-02 17:02:58 -0800618 if (mContext->mCompilationPackage == u"android") {
619 mContext->mPackageId = 0x01;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700620 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800621 mContext->mPackageId = 0x7f;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700622 }
623
Adam Lesinski6a008172016-02-02 17:02:58 -0800624 mContext->mSymbols = createSymbolTableFromIncludePaths();
625 if (!mContext->mSymbols) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700626 return 1;
627 }
628
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800629 TableMergerOptions tableMergerOptions;
630 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
Adam Lesinski6a008172016-02-02 17:02:58 -0800631 tableMergerOptions.filter = mOptions.configFilter;
632 mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800633
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700634 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800635 mContext->getDiagnostics()->note(
636 DiagMessage() << "linking package '" << mContext->mCompilationPackage << "' "
637 << "with package ID " << std::hex << (int) mContext->mPackageId);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700638 }
639
Adam Lesinskifb48d292015-11-07 15:52:13 -0800640
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700641 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800642 if (!processFile(input, false)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800643 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
Adam Lesinski467f1712015-11-16 17:35:44 -0800644 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800645 }
646 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700647
Adam Lesinskifb48d292015-11-07 15:52:13 -0800648 for (const std::string& input : mOptions.overlayFiles) {
649 if (!processFile(input, true)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800650 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
Adam Lesinski467f1712015-11-16 17:35:44 -0800651 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700652 }
653 }
654
Adam Lesinskifb48d292015-11-07 15:52:13 -0800655 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700656 return 1;
657 }
658
659 if (!mOptions.staticLib) {
660 PrivateAttributeMover mover;
Adam Lesinski6a008172016-02-02 17:02:58 -0800661 if (!mover.consume(mContext, &mFinalTable)) {
662 mContext->getDiagnostics()->error(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700663 DiagMessage() << "failed moving private attributes");
664 return 1;
665 }
666 }
667
668 {
669 IdAssigner idAssigner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800670 if (!idAssigner.consume(mContext, &mFinalTable)) {
671 mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700672 return 1;
673 }
674 }
675
Adam Lesinski6a008172016-02-02 17:02:58 -0800676 mContext->mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
677 mContext->mCompilationPackage, mTableMerger->getMergedPackages() });
678 mContext->mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800679 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski6a008172016-02-02 17:02:58 -0800680 .addSymbolTable(std::move(mContext->mSymbols))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700681 .build();
682
683 {
684 ReferenceLinker linker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800685 if (!linker.consume(mContext, &mFinalTable)) {
686 mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700687 return 1;
688 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800689
690 ProductFilter productFilter(mOptions.products);
691 if (!productFilter.consume(mContext, &mFinalTable)) {
692 mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
693 return 1;
694 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700695 }
696
697 proguard::KeepSet proguardKeepSet;
698
699 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
700 if (!archiveWriter) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800701 mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700702 return 1;
703 }
704
Adam Lesinski467f1712015-11-16 17:35:44 -0800705 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700706 {
Adam Lesinski52364f72016-01-11 13:10:24 -0800707 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski6a008172016-02-02 17:02:58 -0800708 if (!manifestFixer.consume(mContext, manifestXml.get())) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800709 error = true;
710 }
711
Adam Lesinski467f1712015-11-16 17:35:44 -0800712 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
713 // (aka, which package the AndroidManifest.xml is coming from).
714 // So we give it a package name so it can see local resources.
Adam Lesinski6a008172016-02-02 17:02:58 -0800715 manifestXml->file.name.package = mContext->getCompilationPackage().toString();
Adam Lesinski467f1712015-11-16 17:35:44 -0800716
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700717 XmlReferenceLinker manifestLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800718 if (manifestLinker.consume(mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700719 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
720 manifestXml.get(),
721 &proguardKeepSet)) {
722 error = true;
723 }
724
Adam Lesinskica5638f2015-10-21 14:42:43 -0700725 if (mOptions.generateJavaClassPath) {
726 if (!writeManifestJavaFile(manifestXml.get())) {
727 error = true;
728 }
729 }
730
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700731 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
732 archiveWriter.get())) {
733 error = true;
734 }
735 } else {
736 error = true;
737 }
738 }
739
Adam Lesinski467f1712015-11-16 17:35:44 -0800740 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800741 mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
Adam Lesinski467f1712015-11-16 17:35:44 -0800742 return 1;
743 }
744
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800745 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
746 const ResourceKeyRef& key = mergeEntry.first;
747 const FileToMerge& fileToMerge = mergeEntry.second;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800748
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800749 const StringPiece path = fileToMerge.file->getSource().path;
750
751 if (key.name.type != ResourceType::kRaw &&
752 (util::stringEndsWith<char>(path, ".xml.flat") ||
753 util::stringEndsWith<char>(path, ".xml"))) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700754 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800755 mContext->getDiagnostics()->note(DiagMessage() << "linking " << path);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800756 }
757
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800758 io::IFile* file = fileToMerge.file;
759 std::unique_ptr<io::IData> data = file->openAsData();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800760 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800761 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800762 << "failed to open file");
763 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700764 }
765
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800766 std::unique_ptr<xml::XmlResource> xmlRes;
767 if (util::stringEndsWith<char>(path, ".flat")) {
768 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
769 data->data(), data->size(),
Adam Lesinski6a008172016-02-02 17:02:58 -0800770 mContext->getDiagnostics());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800771 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800772 xmlRes = xml::inflate(data->data(), data->size(), mContext->getDiagnostics(),
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800773 file->getSource());
774 }
775
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700776 if (!xmlRes) {
777 return 1;
778 }
779
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800780 // Create the file description header.
781 xmlRes->file = ResourceFile{
782 key.name.toResourceName(),
783 key.config,
784 fileToMerge.originalSource,
785 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700786
787 XmlReferenceLinker xmlLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800788 if (xmlLinker.consume(mContext, xmlRes.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700789 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
790 &proguardKeepSet)) {
791 error = true;
792 }
793
794 Maybe<size_t> maxSdkLevel;
795 if (!mOptions.noAutoVersion) {
796 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
797 }
798
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800799 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700800 archiveWriter.get())) {
801 error = true;
802 }
803
804 if (!mOptions.noAutoVersion) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800805 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700806 xmlRes->file.name);
807 for (int sdkLevel : xmlLinker.getSdkLevels()) {
808 if (sdkLevel > xmlRes->file.config.sdkVersion &&
809 shouldGenerateVersionedResource(result.value().entry,
810 xmlRes->file.config,
811 sdkLevel)) {
812 xmlRes->file.config.sdkVersion = sdkLevel;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800813
814 std::string genResourcePath = ResourceUtils::buildResourceFileName(
Adam Lesinski6a008172016-02-02 17:02:58 -0800815 xmlRes->file, mContext->getNameMangler());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800816
Adam Lesinskia40e9722015-11-24 19:11:46 -0800817 bool added = mFinalTable.addFileReference(
818 xmlRes->file.name,
819 xmlRes->file.config,
820 xmlRes->file.source,
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800821 util::utf8ToUtf16(genResourcePath),
Adam Lesinski6a008172016-02-02 17:02:58 -0800822 mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800823 if (!added) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700824 error = true;
825 continue;
826 }
827
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800828 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
829 archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700830 error = true;
831 }
832 }
833 }
834 }
835
836 } else {
837 error = true;
838 }
839 } else {
840 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800841 mContext->getDiagnostics()->note(DiagMessage() << "copying " << path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700842 }
843
Adam Lesinski52364f72016-01-11 13:10:24 -0800844 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700845 archiveWriter.get())) {
846 error = true;
847 }
848 }
849 }
850
851 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800852 mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700853 return 1;
854 }
855
856 if (!mOptions.noAutoVersion) {
857 AutoVersioner versioner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800858 if (!versioner.consume(mContext, &mFinalTable)) {
859 mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700860 return 1;
861 }
862 }
863
Adam Lesinskifb48d292015-11-07 15:52:13 -0800864 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800865 mContext->getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700866 return 1;
867 }
868
869 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700870 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -0800871 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
872
Adam Lesinskief9c5012016-01-22 14:09:53 -0800873 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700874 options.useFinal = false;
875 }
876
Adam Lesinski6a008172016-02-02 17:02:58 -0800877 const StringPiece16 actualPackage = mContext->getCompilationPackage();
878 StringPiece16 outputPackage = mContext->getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -0800879 if (mOptions.customJavaPackage) {
880 // Override the output java package to the custom one.
881 outputPackage = mOptions.customJavaPackage.value();
882 }
Adam Lesinski83f22552015-11-07 11:51:23 -0800883
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700884 if (mOptions.privateSymbols) {
885 // If we defined a private symbols package, we only emit Public symbols
886 // to the original package, and private and public symbols to the private package.
887
888 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinski6a008172016-02-02 17:02:58 -0800889 if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -0800890 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700891 return 1;
892 }
893
894 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -0800895 outputPackage = mOptions.privateSymbols.value();
896 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700897
Adam Lesinskifb48d292015-11-07 15:52:13 -0800898 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800899 return 1;
900 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700901
Adam Lesinski52364f72016-01-11 13:10:24 -0800902 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
903 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700904 return 1;
905 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700906 }
907 }
908
909 if (mOptions.generateProguardRulesPath) {
910 if (!writeProguardFile(proguardKeepSet)) {
911 return 1;
912 }
913 }
914
915 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800916 Debug::printTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700917 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700918 return 0;
919 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800920
921private:
922 LinkOptions mOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -0800923 LinkContext* mContext;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800924 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800925
926 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800927 std::unique_ptr<TableMerger> mTableMerger;
928
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800929 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -0800930 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800931
932 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800933 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700934};
935
936int link(const std::vector<StringPiece>& args) {
937 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700938 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800939 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -0800940 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
941 Maybe<std::string> versionCode, versionName;
942 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800943 std::vector<std::string> extraJavaPackages;
Adam Lesinski6a008172016-02-02 17:02:58 -0800944 Maybe<std::string> configs;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800945 Maybe<std::string> productList;
Adam Lesinski8900aa82016-01-25 22:48:15 -0800946 bool legacyXFlag = false;
947 bool requireLocalization = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700948 Flags flags = Flags()
949 .requiredFlag("-o", "Output path", &options.outputPath)
950 .requiredFlag("--manifest", "Path to the Android manifest to build",
951 &options.manifestPath)
952 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -0800953 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -0800954 "The last conflicting resource given takes precedence.",
955 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700956 .optionalFlag("--java", "Directory in which to generate R.java",
957 &options.generateJavaClassPath)
958 .optionalFlag("--proguard", "Output file for generated Proguard rules",
959 &options.generateProguardRulesPath)
960 .optionalSwitch("--no-auto-version",
961 "Disables automatic style and layout SDK versioning",
962 &options.noAutoVersion)
Adam Lesinski8900aa82016-01-25 22:48:15 -0800963 .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
964 &legacyXFlag)
965 .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
966 &requireLocalization)
Adam Lesinski6a008172016-02-02 17:02:58 -0800967 .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
968 "is all configurations", &configs)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800969 .optionalFlag("--product", "Comma separated list of product names to keep",
970 &productList)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700971 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
972 "by -o",
973 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800974 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
975 "AndroidManifest.xml", &minSdkVersion)
976 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
977 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -0800978 .optionalFlag("--version-code", "Version code (integer) to inject into the "
979 "AndroidManifest.xml if none is present", &versionCode)
980 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
981 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700982 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinskief9c5012016-01-22 14:09:53 -0800983 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
984 "This is implied when --static-lib is specified.",
985 &options.generateNonFinalIds)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700986 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800987 "private symbols.\n"
988 "If not specified, public and private symbols will use the application's "
989 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -0800990 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
991 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -0800992 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800993 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800994 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
995 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -0800996 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
997 &renameManifestPackage)
998 .optionalFlag("--rename-instrumentation-target-package",
999 "Changes the name of the target package for instrumentation. Most useful "
1000 "when used\nin conjunction with --rename-manifest-package",
1001 &renameInstrumentationTargetPackage)
1002 .optionalFlagList("-0", "File extensions not to compress",
1003 &options.extensionsToNotCompress)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001004 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
1005
1006 if (!flags.parse("aapt2 link", args, &std::cerr)) {
1007 return 1;
1008 }
1009
Adam Lesinski6a008172016-02-02 17:02:58 -08001010 LinkContext context;
1011
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001012 if (privateSymbolsPackage) {
1013 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
1014 }
1015
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001016 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001017 options.manifestFixerOptions.minSdkVersionDefault =
1018 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001019 }
1020
1021 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001022 options.manifestFixerOptions.targetSdkVersionDefault =
1023 util::utf8ToUtf16(targetSdkVersion.value());
1024 }
1025
1026 if (renameManifestPackage) {
1027 options.manifestFixerOptions.renameManifestPackage =
1028 util::utf8ToUtf16(renameManifestPackage.value());
1029 }
1030
1031 if (renameInstrumentationTargetPackage) {
1032 options.manifestFixerOptions.renameInstrumentationTargetPackage =
1033 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
1034 }
1035
1036 if (versionCode) {
1037 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
1038 }
1039
1040 if (versionName) {
1041 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
1042 }
1043
1044 if (customJavaPackage) {
1045 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001046 }
1047
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001048 // Populate the set of extra packages for which to generate R.java.
1049 for (std::string& extraPackage : extraJavaPackages) {
1050 // A given package can actually be a colon separated list of packages.
1051 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001052 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001053 }
1054 }
1055
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001056 if (productList) {
1057 for (StringPiece product : util::tokenize<char>(productList.value(), ',')) {
1058 if (product != "" && product != "default") {
1059 options.products.insert(product.toString());
1060 }
1061 }
1062 }
1063
Adam Lesinski6a008172016-02-02 17:02:58 -08001064 AxisConfigFilter filter;
1065 if (configs) {
1066 for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
1067 ConfigDescription config;
1068 LocaleValue lv;
1069 if (lv.initFromFilterString(configStr)) {
1070 lv.writeTo(&config);
1071 } else if (!ConfigDescription::parse(configStr, &config)) {
1072 context.getDiagnostics()->error(
1073 DiagMessage() << "invalid config '" << configStr << "' for -c option");
1074 return 1;
1075 }
1076
1077 if (config.density != 0) {
1078 context.getDiagnostics()->warn(
1079 DiagMessage() << "ignoring density '" << config << "' for -c option");
1080 } else {
1081 filter.addConfig(config);
1082 }
1083 }
1084
1085 options.configFilter = &filter;
1086 }
1087
1088 LinkCommand cmd(&context, options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001089 return cmd.run(flags.getArgs());
1090}
1091
1092} // namespace aapt