blob: f3a9ea319216582956690e231b9a769086d090f9 [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 Lesinski1ab598f2015-08-14 14:26:04 -070020#include "NameMangler.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "compile/IdAssigner.h"
22#include "flatten/Archive.h"
23#include "flatten/TableFlattener.h"
24#include "flatten/XmlFlattener.h"
Adam Lesinskia40e9722015-11-24 19:11:46 -080025#include "io/FileSystem.h"
26#include "io/ZipArchive.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070027#include "java/JavaClassGenerator.h"
28#include "java/ManifestClassGenerator.h"
29#include "java/ProguardRules.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070030#include "link/Linkers.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080031#include "link/ReferenceLinker.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080032#include "link/ManifestFixer.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033#include "link/TableMerger.h"
34#include "process/IResourceTableConsumer.h"
35#include "process/SymbolTable.h"
36#include "unflatten/BinaryResourceParser.h"
37#include "unflatten/FileExportHeaderReader.h"
38#include "util/Files.h"
39#include "util/StringPiece.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080040#include "xml/XmlDom.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070041
42#include <fstream>
43#include <sys/stat.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070044#include <vector>
45
46namespace aapt {
47
48struct LinkOptions {
49 std::string outputPath;
50 std::string manifestPath;
51 std::vector<std::string> includePaths;
Adam Lesinskifb48d292015-11-07 15:52:13 -080052 std::vector<std::string> overlayFiles;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070053 Maybe<std::string> generateJavaClassPath;
Adam Lesinski52364f72016-01-11 13:10:24 -080054 Maybe<std::u16string> customJavaPackage;
55 std::set<std::u16string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070056 Maybe<std::string> generateProguardRulesPath;
57 bool noAutoVersion = false;
58 bool staticLib = false;
Adam Lesinskief9c5012016-01-22 14:09:53 -080059 bool generateNonFinalIds = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070060 bool verbose = false;
61 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080062 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080063 bool doNotCompressAnything = false;
64 std::vector<std::string> extensionsToNotCompress;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070065 Maybe<std::u16string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080066 ManifestFixerOptions manifestFixerOptions;
67
Adam Lesinski1ab598f2015-08-14 14:26:04 -070068};
69
70struct LinkContext : public IAaptContext {
71 StdErrDiagnostics mDiagnostics;
72 std::unique_ptr<NameMangler> mNameMangler;
73 std::u16string mCompilationPackage;
74 uint8_t mPackageId;
75 std::unique_ptr<ISymbolTable> mSymbols;
76
77 IDiagnostics* getDiagnostics() override {
78 return &mDiagnostics;
79 }
80
81 NameMangler* getNameMangler() override {
82 return mNameMangler.get();
83 }
84
85 StringPiece16 getCompilationPackage() override {
86 return mCompilationPackage;
87 }
88
89 uint8_t getPackageId() override {
90 return mPackageId;
91 }
92
93 ISymbolTable* getExternalSymbols() override {
94 return mSymbols.get();
95 }
96};
97
Adam Lesinskifb48d292015-11-07 15:52:13 -080098class LinkCommand {
99public:
100 LinkCommand(const LinkOptions& options) :
Adam Lesinskia40e9722015-11-24 19:11:46 -0800101 mOptions(options), mContext(), mFinalTable(), mFileCollection(nullptr) {
102 std::unique_ptr<io::FileCollection> fileCollection =
103 util::make_unique<io::FileCollection>();
104
105 // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
106 mFileCollection = fileCollection.get();
107
108 // Move it to the collection.
109 mCollections.push_back(std::move(fileCollection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800110 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700111
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700112 /**
113 * Creates a SymbolTable that loads symbols from the various APKs and caches the
114 * results for faster lookup.
115 */
116 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
117 AssetManagerSymbolTableBuilder builder;
118 for (const std::string& path : mOptions.includePaths) {
119 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800120 mContext.getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700121 }
122
123 std::unique_ptr<android::AssetManager> assetManager =
124 util::make_unique<android::AssetManager>();
125 int32_t cookie = 0;
126 if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
127 mContext.getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800128 DiagMessage(path) << "failed to load include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700129 return {};
130 }
131 builder.add(std::move(assetManager));
132 }
133 return builder.build();
134 }
135
Adam Lesinskia40e9722015-11-24 19:11:46 -0800136 std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700137 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800138 BinaryResourceParser parser(&mContext, table.get(), source, data, len);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700139 if (!parser.parse()) {
140 return {};
141 }
142 return table;
143 }
144
145 /**
146 * Inflates an XML file from the source path.
147 */
Adam Lesinskia40e9722015-11-24 19:11:46 -0800148 static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700149 std::ifstream fin(path, std::ifstream::binary);
150 if (!fin) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800151 diag->error(DiagMessage(path) << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700152 return {};
153 }
154
Adam Lesinskia40e9722015-11-24 19:11:46 -0800155 return xml::inflate(&fin, diag, Source(path));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700156 }
157
Adam Lesinskia40e9722015-11-24 19:11:46 -0800158 static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(
159 const Source& source,
160 const void* data, size_t len,
161 IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700162 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800163 ssize_t offset = getWrappedDataOffset(data, len, &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700164 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800165 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700166 return {};
167 }
168
Adam Lesinski467f1712015-11-16 17:35:44 -0800169 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800170 reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset),
171 len - static_cast<size_t>(offset),
172 diag,
173 source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700174 if (!xmlRes) {
175 return {};
176 }
177 return xmlRes;
178 }
179
Adam Lesinskia40e9722015-11-24 19:11:46 -0800180 static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
181 const void* data, size_t len,
182 IDiagnostics* diag) {
183 std::unique_ptr<ResourceFile> resFile = util::make_unique<ResourceFile>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700184 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800185 ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700186 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800187 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188 return {};
189 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800190 return resFile;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700191 }
192
Adam Lesinski52364f72016-01-11 13:10:24 -0800193 uint32_t getCompressionFlags(const StringPiece& str) {
194 if (mOptions.doNotCompressAnything) {
195 return 0;
196 }
197
198 for (const std::string& extension : mOptions.extensionsToNotCompress) {
199 if (util::stringEndsWith<char>(str, extension)) {
200 return 0;
201 }
202 }
203 return ArchiveEntry::kCompress;
204 }
205
206 bool copyFileToArchive(io::IFile* file, const std::string& outPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700207 IArchiveWriter* writer) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800208 std::unique_ptr<io::IData> data = file->openAsData();
209 if (!data) {
210 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
211 << "failed to open file");
212 return false;
213 }
214
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700215 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800216 ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700217 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800218 mContext.getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700219 return false;
220 }
221
Adam Lesinski52364f72016-01-11 13:10:24 -0800222 if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800223 if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
224 data->size() - static_cast<size_t>(offset))) {
225 if (writer->finishEntry()) {
226 return true;
227 }
228 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700229 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800230
231 mContext.getDiagnostics()->error(
232 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
233 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700234 }
235
Adam Lesinski467f1712015-11-16 17:35:44 -0800236 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700237 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800238 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700239 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
240 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
241 return AppInfo{ packageAttr->value };
242 }
243 }
244 }
245 return {};
246 }
247
Adam Lesinski979ccb22016-01-11 10:42:19 -0800248 /**
249 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
250 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
251 * is an error and false is returned.
252 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800253 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800254 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
255 return mContext.getCompilationPackage() != pkg->name ||
256 !pkg->id ||
257 pkg->id.value() != mContext.getPackageId();
258 };
259
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700260 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800261 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800262 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700263 // We have a package that is not related to the one we're building!
264 for (const auto& type : package->types) {
265 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800266 ResourceNameRef resName(package->name, type->type, entry->name);
267
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700268 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800269 // Special case the occurrence of an ID that is being generated for the
270 // 'android' package. This is due to legacy reasons.
271 if (valueCast<Id>(configValue.value.get()) &&
272 package->name == u"android") {
273 mContext.getDiagnostics()->warn(
274 DiagMessage(configValue.value->getSource())
275 << "generated id '" << resName
276 << "' for external package '" << package->name
277 << "'");
278 } else {
279 mContext.getDiagnostics()->error(
280 DiagMessage(configValue.value->getSource())
281 << "defined resource '" << resName
282 << "' for external package '" << package->name
283 << "'");
284 error = true;
285 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700286 }
287 }
288 }
289 }
290 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800291
292 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
293 isExtPackageFunc);
294 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700295 return !error;
296 }
297
298 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
299 if (mOptions.outputToDirectory) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800300 return createDirectoryArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700301 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800302 return createZipFileArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700303 }
304 }
305
306 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
307 BigBuffer buffer(1024);
308 TableFlattenerOptions options = {};
309 options.useExtendedChunks = mOptions.staticLib;
310 TableFlattener flattener(&buffer, options);
311 if (!flattener.consume(&mContext, table)) {
312 return false;
313 }
314
Adam Lesinskia40e9722015-11-24 19:11:46 -0800315 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
316 if (writer->writeEntry(buffer)) {
317 if (writer->finishEntry()) {
318 return true;
319 }
320 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700321 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800322
323 mContext.getDiagnostics()->error(
324 DiagMessage() << "failed to write resources.arsc to archive");
325 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700326 }
327
Adam Lesinski467f1712015-11-16 17:35:44 -0800328 bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700329 IArchiveWriter* writer) {
330 BigBuffer buffer(1024);
331 XmlFlattenerOptions options = {};
332 options.keepRawValues = mOptions.staticLib;
333 options.maxSdkLevel = maxSdkLevel;
334 XmlFlattener flattener(&buffer, options);
335 if (!flattener.consume(&mContext, xmlRes)) {
336 return false;
337 }
338
Adam Lesinskia40e9722015-11-24 19:11:46 -0800339 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
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 mContext.getDiagnostics()->error(
347 DiagMessage() << "failed to write " << path << " to archive");
348 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700349 }
350
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700351 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
352 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700353 if (!mOptions.generateJavaClassPath) {
354 return true;
355 }
356
357 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700358 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700359 file::mkdirs(outPath);
360 file::appendPath(&outPath, "R.java");
361
362 std::ofstream fout(outPath, std::ofstream::binary);
363 if (!fout) {
364 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
365 return false;
366 }
367
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700368 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700369 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700370 mContext.getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
371 return false;
372 }
373 return true;
374 }
375
Adam Lesinski467f1712015-11-16 17:35:44 -0800376 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700377 if (!mOptions.generateJavaClassPath) {
378 return true;
379 }
380
381 std::string outPath = mOptions.generateJavaClassPath.value();
382 file::appendPath(&outPath,
383 file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage())));
384 file::mkdirs(outPath);
385 file::appendPath(&outPath, "Manifest.java");
386
387 std::ofstream fout(outPath, std::ofstream::binary);
388 if (!fout) {
389 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
390 return false;
391 }
392
393 ManifestClassGenerator generator;
394 if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(),
395 manifestXml, &fout)) {
396 return false;
397 }
398
399 if (!fout) {
400 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
401 return false;
402 }
403 return true;
404 }
405
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700406 bool writeProguardFile(const proguard::KeepSet& keepSet) {
407 if (!mOptions.generateProguardRulesPath) {
408 return true;
409 }
410
411 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
412 if (!fout) {
413 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
414 return false;
415 }
416
417 proguard::writeKeepSet(&fout, keepSet);
418 if (!fout) {
419 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
420 return false;
421 }
422 return true;
423 }
424
Adam Lesinskifb48d292015-11-07 15:52:13 -0800425 bool mergeStaticLibrary(const std::string& input) {
426 // TODO(adamlesinski): Load resources from a static library APK and merge the table into
427 // TableMerger.
428 mContext.getDiagnostics()->warn(DiagMessage()
429 << "linking static libraries not supported yet: "
430 << input);
431 return true;
432 }
433
Adam Lesinskia40e9722015-11-24 19:11:46 -0800434 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800435 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800436 mContext.getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800437 }
438
Adam Lesinskia40e9722015-11-24 19:11:46 -0800439 std::unique_ptr<io::IData> data = file->openAsData();
440 if (!data) {
441 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
442 << "failed to open file");
443 return false;
444 }
445
446 std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(),
447 data->size());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800448 if (!table) {
449 return false;
450 }
451
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800452 bool result = false;
453 if (override) {
454 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
455 } else {
456 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800457 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800458 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800459 }
460
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800461 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
462 if (mOptions.verbose) {
463 mContext.getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800464 }
465
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800466 bool result = false;
467 if (overlay) {
468 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800469 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800470 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800471 }
472
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800473 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800474 return false;
475 }
476
477 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800478 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800479 if (exportedSymbol.name.package.empty()) {
480 exportedSymbol.name.package = mContext.getCompilationPackage().toString();
481 }
482
483 ResourceNameRef resName = exportedSymbol.name;
484
485 Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
486 exportedSymbol.name);
487 if (mangledName) {
488 resName = mangledName.value();
489 }
490
491 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800492 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800493 bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
Adam Lesinskia40e9722015-11-24 19:11:46 -0800494 mContext.getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800495 if (!result) {
496 return false;
497 }
498 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800499 return true;
500 }
501
Adam Lesinskia40e9722015-11-24 19:11:46 -0800502 /**
503 * Creates an io::IFileCollection from the ZIP archive and processes the files within.
504 */
505 bool mergeArchive(const std::string& input, bool override) {
506 std::string errorStr;
507 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
508 input, &errorStr);
509 if (!collection) {
510 mContext.getDiagnostics()->error(DiagMessage(input) << errorStr);
511 return false;
512 }
513
514 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800515 for (auto iter = collection->iterator(); iter->hasNext(); ) {
516 if (!processFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800517 error = true;
518 }
519 }
520
521 // Make sure to move the collection into the set of IFileCollections.
522 mCollections.push_back(std::move(collection));
523 return !error;
524 }
525
526 bool processFile(const std::string& path, bool override) {
Adam Lesinski656a5772016-01-14 15:17:41 -0800527 if (util::stringEndsWith<char>(path, ".flata") ||
528 util::stringEndsWith<char>(path, ".jar") ||
529 util::stringEndsWith<char>(path, ".jack") ||
530 util::stringEndsWith<char>(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800531 return mergeArchive(path, override);
532 }
533
534 io::IFile* file = mFileCollection->insertFile(path);
535 return processFile(file, override);
536 }
537
538 bool processFile(io::IFile* file, bool override) {
539 const Source& src = file->getSource();
540 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
541 return mergeResourceTable(file, override);
Adam Lesinski52364f72016-01-11 13:10:24 -0800542 } else if (util::stringEndsWith<char>(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -0800543 // Try opening the file and looking for an Export header.
544 std::unique_ptr<io::IData> data = file->openAsData();
545 if (!data) {
546 mContext.getDiagnostics()->error(DiagMessage(src) << "failed to open");
547 return false;
548 }
549
550 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
551 src, data->data(), data->size(), mContext.getDiagnostics());
552 if (resourceFile) {
553 return mergeCompiledFile(file, std::move(resourceFile), override);
554 }
Adam Lesinskic446a732016-01-21 11:04:46 -0800555
556 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800557 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800558
Adam Lesinskic446a732016-01-21 11:04:46 -0800559 // Ignore non .flat files. This could be classes.dex or something else that happens
560 // to be in an archive.
561 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800562 }
563
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700564 int run(const std::vector<std::string>& inputFiles) {
565 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800566 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
567 mContext.getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700568 if (!manifestXml) {
569 return 1;
570 }
571
572 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
573 mContext.mCompilationPackage = maybeAppInfo.value().package;
574 } else {
575 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
576 << "no package specified in <manifest> tag");
577 return 1;
578 }
579
580 if (!util::isJavaPackageName(mContext.mCompilationPackage)) {
581 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
582 << "invalid package name '"
583 << mContext.mCompilationPackage
584 << "'");
585 return 1;
586 }
587
588 mContext.mNameMangler = util::make_unique<NameMangler>(
589 NameManglerPolicy{ mContext.mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700590
591 if (mContext.mCompilationPackage == u"android") {
592 mContext.mPackageId = 0x01;
593 } else {
594 mContext.mPackageId = 0x7f;
595 }
596
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700597 mContext.mSymbols = createSymbolTableFromIncludePaths();
598 if (!mContext.mSymbols) {
599 return 1;
600 }
601
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800602 TableMergerOptions tableMergerOptions;
603 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
604 mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800605
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700606 if (mOptions.verbose) {
607 mContext.getDiagnostics()->note(
608 DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
609 << "with package ID " << std::hex << (int) mContext.mPackageId);
610 }
611
Adam Lesinskifb48d292015-11-07 15:52:13 -0800612
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700613 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800614 if (!processFile(input, false)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800615 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
616 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800617 }
618 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700619
Adam Lesinskifb48d292015-11-07 15:52:13 -0800620 for (const std::string& input : mOptions.overlayFiles) {
621 if (!processFile(input, true)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800622 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
623 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700624 }
625 }
626
Adam Lesinskifb48d292015-11-07 15:52:13 -0800627 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700628 return 1;
629 }
630
631 if (!mOptions.staticLib) {
632 PrivateAttributeMover mover;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800633 if (!mover.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700634 mContext.getDiagnostics()->error(
635 DiagMessage() << "failed moving private attributes");
636 return 1;
637 }
638 }
639
640 {
641 IdAssigner idAssigner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800642 if (!idAssigner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700643 mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
644 return 1;
645 }
646 }
647
Adam Lesinskifb48d292015-11-07 15:52:13 -0800648 mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
649 mContext.mCompilationPackage, mTableMerger->getMergedPackages() });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700650 mContext.mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800651 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700652 .addSymbolTable(std::move(mContext.mSymbols))
653 .build();
654
655 {
656 ReferenceLinker linker;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800657 if (!linker.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700658 mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
659 return 1;
660 }
661 }
662
663 proguard::KeepSet proguardKeepSet;
664
665 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
666 if (!archiveWriter) {
667 mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive");
668 return 1;
669 }
670
Adam Lesinski467f1712015-11-16 17:35:44 -0800671 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700672 {
Adam Lesinski52364f72016-01-11 13:10:24 -0800673 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800674 if (!manifestFixer.consume(&mContext, manifestXml.get())) {
675 error = true;
676 }
677
Adam Lesinski467f1712015-11-16 17:35:44 -0800678 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
679 // (aka, which package the AndroidManifest.xml is coming from).
680 // So we give it a package name so it can see local resources.
681 manifestXml->file.name.package = mContext.getCompilationPackage().toString();
682
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700683 XmlReferenceLinker manifestLinker;
684 if (manifestLinker.consume(&mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700685 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
686 manifestXml.get(),
687 &proguardKeepSet)) {
688 error = true;
689 }
690
Adam Lesinskica5638f2015-10-21 14:42:43 -0700691 if (mOptions.generateJavaClassPath) {
692 if (!writeManifestJavaFile(manifestXml.get())) {
693 error = true;
694 }
695 }
696
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700697 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
698 archiveWriter.get())) {
699 error = true;
700 }
701 } else {
702 error = true;
703 }
704 }
705
Adam Lesinski467f1712015-11-16 17:35:44 -0800706 if (error) {
707 mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest");
708 return 1;
709 }
710
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800711 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
712 const ResourceKeyRef& key = mergeEntry.first;
713 const FileToMerge& fileToMerge = mergeEntry.second;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800714
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800715 const StringPiece path = fileToMerge.file->getSource().path;
716
717 if (key.name.type != ResourceType::kRaw &&
718 (util::stringEndsWith<char>(path, ".xml.flat") ||
719 util::stringEndsWith<char>(path, ".xml"))) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700720 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800721 mContext.getDiagnostics()->note(DiagMessage() << "linking " << path);
722 }
723
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800724 io::IFile* file = fileToMerge.file;
725 std::unique_ptr<io::IData> data = file->openAsData();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800726 if (!data) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800727 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800728 << "failed to open file");
729 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700730 }
731
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800732 std::unique_ptr<xml::XmlResource> xmlRes;
733 if (util::stringEndsWith<char>(path, ".flat")) {
734 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
735 data->data(), data->size(),
736 mContext.getDiagnostics());
737 } else {
738 xmlRes = xml::inflate(data->data(), data->size(), mContext.getDiagnostics(),
739 file->getSource());
740 }
741
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700742 if (!xmlRes) {
743 return 1;
744 }
745
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800746 // Create the file description header.
747 xmlRes->file = ResourceFile{
748 key.name.toResourceName(),
749 key.config,
750 fileToMerge.originalSource,
751 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700752
753 XmlReferenceLinker xmlLinker;
754 if (xmlLinker.consume(&mContext, xmlRes.get())) {
755 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
756 &proguardKeepSet)) {
757 error = true;
758 }
759
760 Maybe<size_t> maxSdkLevel;
761 if (!mOptions.noAutoVersion) {
762 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
763 }
764
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800765 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700766 archiveWriter.get())) {
767 error = true;
768 }
769
770 if (!mOptions.noAutoVersion) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800771 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700772 xmlRes->file.name);
773 for (int sdkLevel : xmlLinker.getSdkLevels()) {
774 if (sdkLevel > xmlRes->file.config.sdkVersion &&
775 shouldGenerateVersionedResource(result.value().entry,
776 xmlRes->file.config,
777 sdkLevel)) {
778 xmlRes->file.config.sdkVersion = sdkLevel;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800779
780 std::string genResourcePath = ResourceUtils::buildResourceFileName(
781 xmlRes->file, mContext.getNameMangler());
782
Adam Lesinskia40e9722015-11-24 19:11:46 -0800783 bool added = mFinalTable.addFileReference(
784 xmlRes->file.name,
785 xmlRes->file.config,
786 xmlRes->file.source,
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800787 util::utf8ToUtf16(genResourcePath),
Adam Lesinskia40e9722015-11-24 19:11:46 -0800788 mContext.getDiagnostics());
789 if (!added) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700790 error = true;
791 continue;
792 }
793
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800794 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
795 archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700796 error = true;
797 }
798 }
799 }
800 }
801
802 } else {
803 error = true;
804 }
805 } else {
806 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800807 mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700808 }
809
Adam Lesinski52364f72016-01-11 13:10:24 -0800810 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700811 archiveWriter.get())) {
812 error = true;
813 }
814 }
815 }
816
817 if (error) {
818 mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources");
819 return 1;
820 }
821
822 if (!mOptions.noAutoVersion) {
823 AutoVersioner versioner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800824 if (!versioner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700825 mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
826 return 1;
827 }
828 }
829
Adam Lesinskifb48d292015-11-07 15:52:13 -0800830 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700831 mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
832 return 1;
833 }
834
835 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700836 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -0800837 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
838
Adam Lesinskief9c5012016-01-22 14:09:53 -0800839 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700840 options.useFinal = false;
841 }
842
Adam Lesinski52364f72016-01-11 13:10:24 -0800843 const StringPiece16 actualPackage = mContext.getCompilationPackage();
Adam Lesinski83f22552015-11-07 11:51:23 -0800844 StringPiece16 outputPackage = mContext.getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -0800845 if (mOptions.customJavaPackage) {
846 // Override the output java package to the custom one.
847 outputPackage = mOptions.customJavaPackage.value();
848 }
Adam Lesinski83f22552015-11-07 11:51:23 -0800849
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700850 if (mOptions.privateSymbols) {
851 // If we defined a private symbols package, we only emit Public symbols
852 // to the original package, and private and public symbols to the private package.
853
854 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800855 if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -0800856 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700857 return 1;
858 }
859
860 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -0800861 outputPackage = mOptions.privateSymbols.value();
862 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700863
Adam Lesinskifb48d292015-11-07 15:52:13 -0800864 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800865 return 1;
866 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700867
Adam Lesinski52364f72016-01-11 13:10:24 -0800868 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
869 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700870 return 1;
871 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700872 }
873 }
874
875 if (mOptions.generateProguardRulesPath) {
876 if (!writeProguardFile(proguardKeepSet)) {
877 return 1;
878 }
879 }
880
881 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800882 Debug::printTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700883 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700884 return 0;
885 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800886
887private:
888 LinkOptions mOptions;
889 LinkContext mContext;
890 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800891
892 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800893 std::unique_ptr<TableMerger> mTableMerger;
894
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800895 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -0800896 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800897
898 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800899 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700900};
901
902int link(const std::vector<StringPiece>& args) {
903 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700904 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800905 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -0800906 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
907 Maybe<std::string> versionCode, versionName;
908 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800909 std::vector<std::string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700910 Flags flags = Flags()
911 .requiredFlag("-o", "Output path", &options.outputPath)
912 .requiredFlag("--manifest", "Path to the Android manifest to build",
913 &options.manifestPath)
914 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -0800915 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -0800916 "The last conflicting resource given takes precedence.",
917 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700918 .optionalFlag("--java", "Directory in which to generate R.java",
919 &options.generateJavaClassPath)
920 .optionalFlag("--proguard", "Output file for generated Proguard rules",
921 &options.generateProguardRulesPath)
922 .optionalSwitch("--no-auto-version",
923 "Disables automatic style and layout SDK versioning",
924 &options.noAutoVersion)
925 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
926 "by -o",
927 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800928 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
929 "AndroidManifest.xml", &minSdkVersion)
930 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
931 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -0800932 .optionalFlag("--version-code", "Version code (integer) to inject into the "
933 "AndroidManifest.xml if none is present", &versionCode)
934 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
935 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700936 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinskief9c5012016-01-22 14:09:53 -0800937 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
938 "This is implied when --static-lib is specified.",
939 &options.generateNonFinalIds)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700940 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800941 "private symbols.\n"
942 "If not specified, public and private symbols will use the application's "
943 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -0800944 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
945 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -0800946 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800947 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800948 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
949 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -0800950 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
951 &renameManifestPackage)
952 .optionalFlag("--rename-instrumentation-target-package",
953 "Changes the name of the target package for instrumentation. Most useful "
954 "when used\nin conjunction with --rename-manifest-package",
955 &renameInstrumentationTargetPackage)
956 .optionalFlagList("-0", "File extensions not to compress",
957 &options.extensionsToNotCompress)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700958 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
959
960 if (!flags.parse("aapt2 link", args, &std::cerr)) {
961 return 1;
962 }
963
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700964 if (privateSymbolsPackage) {
965 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
966 }
967
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800968 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800969 options.manifestFixerOptions.minSdkVersionDefault =
970 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800971 }
972
973 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800974 options.manifestFixerOptions.targetSdkVersionDefault =
975 util::utf8ToUtf16(targetSdkVersion.value());
976 }
977
978 if (renameManifestPackage) {
979 options.manifestFixerOptions.renameManifestPackage =
980 util::utf8ToUtf16(renameManifestPackage.value());
981 }
982
983 if (renameInstrumentationTargetPackage) {
984 options.manifestFixerOptions.renameInstrumentationTargetPackage =
985 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
986 }
987
988 if (versionCode) {
989 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
990 }
991
992 if (versionName) {
993 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
994 }
995
996 if (customJavaPackage) {
997 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800998 }
999
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001000 // Populate the set of extra packages for which to generate R.java.
1001 for (std::string& extraPackage : extraJavaPackages) {
1002 // A given package can actually be a colon separated list of packages.
1003 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001004 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001005 }
1006 }
1007
Adam Lesinskifb48d292015-11-07 15:52:13 -08001008 LinkCommand cmd(options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001009 return cmd.run(flags.getArgs());
1010}
1011
1012} // namespace aapt