blob: 51b9cecb7b7eebe291f9bd36c212b33b49b42147 [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;
59 bool verbose = false;
60 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080061 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080062 bool doNotCompressAnything = false;
63 std::vector<std::string> extensionsToNotCompress;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070064 Maybe<std::u16string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080065 ManifestFixerOptions manifestFixerOptions;
66
Adam Lesinski1ab598f2015-08-14 14:26:04 -070067};
68
69struct LinkContext : public IAaptContext {
70 StdErrDiagnostics mDiagnostics;
71 std::unique_ptr<NameMangler> mNameMangler;
72 std::u16string mCompilationPackage;
73 uint8_t mPackageId;
74 std::unique_ptr<ISymbolTable> mSymbols;
75
76 IDiagnostics* getDiagnostics() override {
77 return &mDiagnostics;
78 }
79
80 NameMangler* getNameMangler() override {
81 return mNameMangler.get();
82 }
83
84 StringPiece16 getCompilationPackage() override {
85 return mCompilationPackage;
86 }
87
88 uint8_t getPackageId() override {
89 return mPackageId;
90 }
91
92 ISymbolTable* getExternalSymbols() override {
93 return mSymbols.get();
94 }
95};
96
Adam Lesinskifb48d292015-11-07 15:52:13 -080097class LinkCommand {
98public:
99 LinkCommand(const LinkOptions& options) :
Adam Lesinskia40e9722015-11-24 19:11:46 -0800100 mOptions(options), mContext(), mFinalTable(), mFileCollection(nullptr) {
101 std::unique_ptr<io::FileCollection> fileCollection =
102 util::make_unique<io::FileCollection>();
103
104 // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
105 mFileCollection = fileCollection.get();
106
107 // Move it to the collection.
108 mCollections.push_back(std::move(fileCollection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800109 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700110
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700111 /**
112 * Creates a SymbolTable that loads symbols from the various APKs and caches the
113 * results for faster lookup.
114 */
115 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
116 AssetManagerSymbolTableBuilder builder;
117 for (const std::string& path : mOptions.includePaths) {
118 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800119 mContext.getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700120 }
121
122 std::unique_ptr<android::AssetManager> assetManager =
123 util::make_unique<android::AssetManager>();
124 int32_t cookie = 0;
125 if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
126 mContext.getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800127 DiagMessage(path) << "failed to load include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700128 return {};
129 }
130 builder.add(std::move(assetManager));
131 }
132 return builder.build();
133 }
134
Adam Lesinskia40e9722015-11-24 19:11:46 -0800135 std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700136 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800137 BinaryResourceParser parser(&mContext, table.get(), source, data, len);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700138 if (!parser.parse()) {
139 return {};
140 }
141 return table;
142 }
143
144 /**
145 * Inflates an XML file from the source path.
146 */
Adam Lesinskia40e9722015-11-24 19:11:46 -0800147 static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700148 std::ifstream fin(path, std::ifstream::binary);
149 if (!fin) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800150 diag->error(DiagMessage(path) << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700151 return {};
152 }
153
Adam Lesinskia40e9722015-11-24 19:11:46 -0800154 return xml::inflate(&fin, diag, Source(path));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700155 }
156
Adam Lesinskia40e9722015-11-24 19:11:46 -0800157 static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(
158 const Source& source,
159 const void* data, size_t len,
160 IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700161 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800162 ssize_t offset = getWrappedDataOffset(data, len, &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700163 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800164 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700165 return {};
166 }
167
Adam Lesinski467f1712015-11-16 17:35:44 -0800168 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800169 reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset),
170 len - static_cast<size_t>(offset),
171 diag,
172 source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700173 if (!xmlRes) {
174 return {};
175 }
176 return xmlRes;
177 }
178
Adam Lesinskia40e9722015-11-24 19:11:46 -0800179 static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
180 const void* data, size_t len,
181 IDiagnostics* diag) {
182 std::unique_ptr<ResourceFile> resFile = util::make_unique<ResourceFile>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700183 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800184 ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700185 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800186 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700187 return {};
188 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800189 return resFile;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190 }
191
Adam Lesinski52364f72016-01-11 13:10:24 -0800192 uint32_t getCompressionFlags(const StringPiece& str) {
193 if (mOptions.doNotCompressAnything) {
194 return 0;
195 }
196
197 for (const std::string& extension : mOptions.extensionsToNotCompress) {
198 if (util::stringEndsWith<char>(str, extension)) {
199 return 0;
200 }
201 }
202 return ArchiveEntry::kCompress;
203 }
204
205 bool copyFileToArchive(io::IFile* file, const std::string& outPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700206 IArchiveWriter* writer) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800207 std::unique_ptr<io::IData> data = file->openAsData();
208 if (!data) {
209 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
210 << "failed to open file");
211 return false;
212 }
213
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700214 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800215 ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700216 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800217 mContext.getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700218 return false;
219 }
220
Adam Lesinski52364f72016-01-11 13:10:24 -0800221 if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800222 if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
223 data->size() - static_cast<size_t>(offset))) {
224 if (writer->finishEntry()) {
225 return true;
226 }
227 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700228 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800229
230 mContext.getDiagnostics()->error(
231 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
232 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700233 }
234
Adam Lesinski467f1712015-11-16 17:35:44 -0800235 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700236 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800237 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700238 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
239 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
240 return AppInfo{ packageAttr->value };
241 }
242 }
243 }
244 return {};
245 }
246
Adam Lesinski979ccb22016-01-11 10:42:19 -0800247 /**
248 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
249 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
250 * is an error and false is returned.
251 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800252 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800253 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
254 return mContext.getCompilationPackage() != pkg->name ||
255 !pkg->id ||
256 pkg->id.value() != mContext.getPackageId();
257 };
258
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700259 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800260 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800261 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700262 // We have a package that is not related to the one we're building!
263 for (const auto& type : package->types) {
264 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800265 ResourceNameRef resName(package->name, type->type, entry->name);
266
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700267 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800268 // Special case the occurrence of an ID that is being generated for the
269 // 'android' package. This is due to legacy reasons.
270 if (valueCast<Id>(configValue.value.get()) &&
271 package->name == u"android") {
272 mContext.getDiagnostics()->warn(
273 DiagMessage(configValue.value->getSource())
274 << "generated id '" << resName
275 << "' for external package '" << package->name
276 << "'");
277 } else {
278 mContext.getDiagnostics()->error(
279 DiagMessage(configValue.value->getSource())
280 << "defined resource '" << resName
281 << "' for external package '" << package->name
282 << "'");
283 error = true;
284 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700285 }
286 }
287 }
288 }
289 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800290
291 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
292 isExtPackageFunc);
293 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700294 return !error;
295 }
296
297 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
298 if (mOptions.outputToDirectory) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800299 return createDirectoryArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700300 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800301 return createZipFileArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700302 }
303 }
304
305 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
306 BigBuffer buffer(1024);
307 TableFlattenerOptions options = {};
308 options.useExtendedChunks = mOptions.staticLib;
309 TableFlattener flattener(&buffer, options);
310 if (!flattener.consume(&mContext, table)) {
311 return false;
312 }
313
Adam Lesinskia40e9722015-11-24 19:11:46 -0800314 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
315 if (writer->writeEntry(buffer)) {
316 if (writer->finishEntry()) {
317 return true;
318 }
319 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700320 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800321
322 mContext.getDiagnostics()->error(
323 DiagMessage() << "failed to write resources.arsc to archive");
324 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700325 }
326
Adam Lesinski467f1712015-11-16 17:35:44 -0800327 bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700328 IArchiveWriter* writer) {
329 BigBuffer buffer(1024);
330 XmlFlattenerOptions options = {};
331 options.keepRawValues = mOptions.staticLib;
332 options.maxSdkLevel = maxSdkLevel;
333 XmlFlattener flattener(&buffer, options);
334 if (!flattener.consume(&mContext, xmlRes)) {
335 return false;
336 }
337
Adam Lesinskia40e9722015-11-24 19:11:46 -0800338 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
339 if (writer->writeEntry(buffer)) {
340 if (writer->finishEntry()) {
341 return true;
342 }
343 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700344 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800345 mContext.getDiagnostics()->error(
346 DiagMessage() << "failed to write " << path << " to archive");
347 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700348 }
349
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700350 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
351 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700352 if (!mOptions.generateJavaClassPath) {
353 return true;
354 }
355
356 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700357 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700358 file::mkdirs(outPath);
359 file::appendPath(&outPath, "R.java");
360
361 std::ofstream fout(outPath, std::ofstream::binary);
362 if (!fout) {
363 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
364 return false;
365 }
366
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700367 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700368 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700369 mContext.getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
370 return false;
371 }
372 return true;
373 }
374
Adam Lesinski467f1712015-11-16 17:35:44 -0800375 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700376 if (!mOptions.generateJavaClassPath) {
377 return true;
378 }
379
380 std::string outPath = mOptions.generateJavaClassPath.value();
381 file::appendPath(&outPath,
382 file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage())));
383 file::mkdirs(outPath);
384 file::appendPath(&outPath, "Manifest.java");
385
386 std::ofstream fout(outPath, std::ofstream::binary);
387 if (!fout) {
388 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
389 return false;
390 }
391
392 ManifestClassGenerator generator;
393 if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(),
394 manifestXml, &fout)) {
395 return false;
396 }
397
398 if (!fout) {
399 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
400 return false;
401 }
402 return true;
403 }
404
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700405 bool writeProguardFile(const proguard::KeepSet& keepSet) {
406 if (!mOptions.generateProguardRulesPath) {
407 return true;
408 }
409
410 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
411 if (!fout) {
412 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
413 return false;
414 }
415
416 proguard::writeKeepSet(&fout, keepSet);
417 if (!fout) {
418 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
419 return false;
420 }
421 return true;
422 }
423
Adam Lesinskifb48d292015-11-07 15:52:13 -0800424 bool mergeStaticLibrary(const std::string& input) {
425 // TODO(adamlesinski): Load resources from a static library APK and merge the table into
426 // TableMerger.
427 mContext.getDiagnostics()->warn(DiagMessage()
428 << "linking static libraries not supported yet: "
429 << input);
430 return true;
431 }
432
Adam Lesinskia40e9722015-11-24 19:11:46 -0800433 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800434 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800435 mContext.getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800436 }
437
Adam Lesinskia40e9722015-11-24 19:11:46 -0800438 std::unique_ptr<io::IData> data = file->openAsData();
439 if (!data) {
440 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
441 << "failed to open file");
442 return false;
443 }
444
445 std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(),
446 data->size());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800447 if (!table) {
448 return false;
449 }
450
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800451 bool result = false;
452 if (override) {
453 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
454 } else {
455 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800456 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800457 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800458 }
459
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800460 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
461 if (mOptions.verbose) {
462 mContext.getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800463 }
464
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800465 bool result = false;
466 if (overlay) {
467 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800468 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800469 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800470 }
471
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800472 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800473 return false;
474 }
475
476 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800477 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800478 if (exportedSymbol.name.package.empty()) {
479 exportedSymbol.name.package = mContext.getCompilationPackage().toString();
480 }
481
482 ResourceNameRef resName = exportedSymbol.name;
483
484 Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
485 exportedSymbol.name);
486 if (mangledName) {
487 resName = mangledName.value();
488 }
489
490 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800491 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800492 bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
Adam Lesinskia40e9722015-11-24 19:11:46 -0800493 mContext.getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800494 if (!result) {
495 return false;
496 }
497 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800498 return true;
499 }
500
Adam Lesinskia40e9722015-11-24 19:11:46 -0800501 /**
502 * Creates an io::IFileCollection from the ZIP archive and processes the files within.
503 */
504 bool mergeArchive(const std::string& input, bool override) {
505 std::string errorStr;
506 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
507 input, &errorStr);
508 if (!collection) {
509 mContext.getDiagnostics()->error(DiagMessage(input) << errorStr);
510 return false;
511 }
512
513 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800514 for (auto iter = collection->iterator(); iter->hasNext(); ) {
515 if (!processFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800516 error = true;
517 }
518 }
519
520 // Make sure to move the collection into the set of IFileCollections.
521 mCollections.push_back(std::move(collection));
522 return !error;
523 }
524
525 bool processFile(const std::string& path, bool override) {
Adam Lesinski656a5772016-01-14 15:17:41 -0800526 if (util::stringEndsWith<char>(path, ".flata") ||
527 util::stringEndsWith<char>(path, ".jar") ||
528 util::stringEndsWith<char>(path, ".jack") ||
529 util::stringEndsWith<char>(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800530 return mergeArchive(path, override);
531 }
532
533 io::IFile* file = mFileCollection->insertFile(path);
534 return processFile(file, override);
535 }
536
537 bool processFile(io::IFile* file, bool override) {
538 const Source& src = file->getSource();
539 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
540 return mergeResourceTable(file, override);
Adam Lesinski52364f72016-01-11 13:10:24 -0800541 } else if (util::stringEndsWith<char>(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -0800542 // Try opening the file and looking for an Export header.
543 std::unique_ptr<io::IData> data = file->openAsData();
544 if (!data) {
545 mContext.getDiagnostics()->error(DiagMessage(src) << "failed to open");
546 return false;
547 }
548
549 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
550 src, data->data(), data->size(), mContext.getDiagnostics());
551 if (resourceFile) {
552 return mergeCompiledFile(file, std::move(resourceFile), override);
553 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800554 } else {
555 // Ignore non .flat files. This could be classes.dex or something else that happens
556 // to be in an archive.
Adam Lesinskifb48d292015-11-07 15:52:13 -0800557 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800558
Adam Lesinskifb48d292015-11-07 15:52:13 -0800559 return false;
560 }
561
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700562 int run(const std::vector<std::string>& inputFiles) {
563 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800564 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
565 mContext.getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700566 if (!manifestXml) {
567 return 1;
568 }
569
570 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
571 mContext.mCompilationPackage = maybeAppInfo.value().package;
572 } else {
573 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
574 << "no package specified in <manifest> tag");
575 return 1;
576 }
577
578 if (!util::isJavaPackageName(mContext.mCompilationPackage)) {
579 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
580 << "invalid package name '"
581 << mContext.mCompilationPackage
582 << "'");
583 return 1;
584 }
585
586 mContext.mNameMangler = util::make_unique<NameMangler>(
587 NameManglerPolicy{ mContext.mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700588
589 if (mContext.mCompilationPackage == u"android") {
590 mContext.mPackageId = 0x01;
591 } else {
592 mContext.mPackageId = 0x7f;
593 }
594
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700595 mContext.mSymbols = createSymbolTableFromIncludePaths();
596 if (!mContext.mSymbols) {
597 return 1;
598 }
599
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800600 TableMergerOptions tableMergerOptions;
601 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
602 mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800603
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700604 if (mOptions.verbose) {
605 mContext.getDiagnostics()->note(
606 DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
607 << "with package ID " << std::hex << (int) mContext.mPackageId);
608 }
609
Adam Lesinskifb48d292015-11-07 15:52:13 -0800610
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700611 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800612 if (!processFile(input, false)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800613 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
614 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800615 }
616 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700617
Adam Lesinskifb48d292015-11-07 15:52:13 -0800618 for (const std::string& input : mOptions.overlayFiles) {
619 if (!processFile(input, true)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800620 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
621 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700622 }
623 }
624
Adam Lesinskifb48d292015-11-07 15:52:13 -0800625 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700626 return 1;
627 }
628
629 if (!mOptions.staticLib) {
630 PrivateAttributeMover mover;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800631 if (!mover.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700632 mContext.getDiagnostics()->error(
633 DiagMessage() << "failed moving private attributes");
634 return 1;
635 }
636 }
637
638 {
639 IdAssigner idAssigner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800640 if (!idAssigner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700641 mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
642 return 1;
643 }
644 }
645
Adam Lesinskifb48d292015-11-07 15:52:13 -0800646 mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
647 mContext.mCompilationPackage, mTableMerger->getMergedPackages() });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700648 mContext.mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800649 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700650 .addSymbolTable(std::move(mContext.mSymbols))
651 .build();
652
653 {
654 ReferenceLinker linker;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800655 if (!linker.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700656 mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
657 return 1;
658 }
659 }
660
661 proguard::KeepSet proguardKeepSet;
662
663 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
664 if (!archiveWriter) {
665 mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive");
666 return 1;
667 }
668
Adam Lesinski467f1712015-11-16 17:35:44 -0800669 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700670 {
Adam Lesinski52364f72016-01-11 13:10:24 -0800671 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800672 if (!manifestFixer.consume(&mContext, manifestXml.get())) {
673 error = true;
674 }
675
Adam Lesinski467f1712015-11-16 17:35:44 -0800676 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
677 // (aka, which package the AndroidManifest.xml is coming from).
678 // So we give it a package name so it can see local resources.
679 manifestXml->file.name.package = mContext.getCompilationPackage().toString();
680
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700681 XmlReferenceLinker manifestLinker;
682 if (manifestLinker.consume(&mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700683 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
684 manifestXml.get(),
685 &proguardKeepSet)) {
686 error = true;
687 }
688
Adam Lesinskica5638f2015-10-21 14:42:43 -0700689 if (mOptions.generateJavaClassPath) {
690 if (!writeManifestJavaFile(manifestXml.get())) {
691 error = true;
692 }
693 }
694
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700695 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
696 archiveWriter.get())) {
697 error = true;
698 }
699 } else {
700 error = true;
701 }
702 }
703
Adam Lesinski467f1712015-11-16 17:35:44 -0800704 if (error) {
705 mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest");
706 return 1;
707 }
708
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800709 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
710 const ResourceKeyRef& key = mergeEntry.first;
711 const FileToMerge& fileToMerge = mergeEntry.second;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800712
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800713 const StringPiece path = fileToMerge.file->getSource().path;
714
715 if (key.name.type != ResourceType::kRaw &&
716 (util::stringEndsWith<char>(path, ".xml.flat") ||
717 util::stringEndsWith<char>(path, ".xml"))) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700718 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800719 mContext.getDiagnostics()->note(DiagMessage() << "linking " << path);
720 }
721
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800722 io::IFile* file = fileToMerge.file;
723 std::unique_ptr<io::IData> data = file->openAsData();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800724 if (!data) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800725 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800726 << "failed to open file");
727 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700728 }
729
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800730 std::unique_ptr<xml::XmlResource> xmlRes;
731 if (util::stringEndsWith<char>(path, ".flat")) {
732 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
733 data->data(), data->size(),
734 mContext.getDiagnostics());
735 } else {
736 xmlRes = xml::inflate(data->data(), data->size(), mContext.getDiagnostics(),
737 file->getSource());
738 }
739
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700740 if (!xmlRes) {
741 return 1;
742 }
743
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800744 // Create the file description header.
745 xmlRes->file = ResourceFile{
746 key.name.toResourceName(),
747 key.config,
748 fileToMerge.originalSource,
749 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700750
751 XmlReferenceLinker xmlLinker;
752 if (xmlLinker.consume(&mContext, xmlRes.get())) {
753 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
754 &proguardKeepSet)) {
755 error = true;
756 }
757
758 Maybe<size_t> maxSdkLevel;
759 if (!mOptions.noAutoVersion) {
760 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
761 }
762
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800763 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700764 archiveWriter.get())) {
765 error = true;
766 }
767
768 if (!mOptions.noAutoVersion) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800769 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700770 xmlRes->file.name);
771 for (int sdkLevel : xmlLinker.getSdkLevels()) {
772 if (sdkLevel > xmlRes->file.config.sdkVersion &&
773 shouldGenerateVersionedResource(result.value().entry,
774 xmlRes->file.config,
775 sdkLevel)) {
776 xmlRes->file.config.sdkVersion = sdkLevel;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800777
778 std::string genResourcePath = ResourceUtils::buildResourceFileName(
779 xmlRes->file, mContext.getNameMangler());
780
Adam Lesinskia40e9722015-11-24 19:11:46 -0800781 bool added = mFinalTable.addFileReference(
782 xmlRes->file.name,
783 xmlRes->file.config,
784 xmlRes->file.source,
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800785 util::utf8ToUtf16(genResourcePath),
Adam Lesinskia40e9722015-11-24 19:11:46 -0800786 mContext.getDiagnostics());
787 if (!added) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700788 error = true;
789 continue;
790 }
791
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800792 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
793 archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700794 error = true;
795 }
796 }
797 }
798 }
799
800 } else {
801 error = true;
802 }
803 } else {
804 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800805 mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700806 }
807
Adam Lesinski52364f72016-01-11 13:10:24 -0800808 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700809 archiveWriter.get())) {
810 error = true;
811 }
812 }
813 }
814
815 if (error) {
816 mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources");
817 return 1;
818 }
819
820 if (!mOptions.noAutoVersion) {
821 AutoVersioner versioner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800822 if (!versioner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700823 mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
824 return 1;
825 }
826 }
827
Adam Lesinskifb48d292015-11-07 15:52:13 -0800828 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700829 mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
830 return 1;
831 }
832
833 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700834 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -0800835 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
836
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700837 if (mOptions.staticLib) {
838 options.useFinal = false;
839 }
840
Adam Lesinski52364f72016-01-11 13:10:24 -0800841 const StringPiece16 actualPackage = mContext.getCompilationPackage();
Adam Lesinski83f22552015-11-07 11:51:23 -0800842 StringPiece16 outputPackage = mContext.getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -0800843 if (mOptions.customJavaPackage) {
844 // Override the output java package to the custom one.
845 outputPackage = mOptions.customJavaPackage.value();
846 }
Adam Lesinski83f22552015-11-07 11:51:23 -0800847
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700848 if (mOptions.privateSymbols) {
849 // If we defined a private symbols package, we only emit Public symbols
850 // to the original package, and private and public symbols to the private package.
851
852 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800853 if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -0800854 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700855 return 1;
856 }
857
858 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -0800859 outputPackage = mOptions.privateSymbols.value();
860 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700861
Adam Lesinskifb48d292015-11-07 15:52:13 -0800862 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800863 return 1;
864 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700865
Adam Lesinski52364f72016-01-11 13:10:24 -0800866 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
867 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700868 return 1;
869 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700870 }
871 }
872
873 if (mOptions.generateProguardRulesPath) {
874 if (!writeProguardFile(proguardKeepSet)) {
875 return 1;
876 }
877 }
878
879 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800880 Debug::printTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700881 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700882 return 0;
883 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800884
885private:
886 LinkOptions mOptions;
887 LinkContext mContext;
888 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800889
890 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800891 std::unique_ptr<TableMerger> mTableMerger;
892
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800893 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -0800894 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800895
896 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800897 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700898};
899
900int link(const std::vector<StringPiece>& args) {
901 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700902 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800903 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -0800904 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
905 Maybe<std::string> versionCode, versionName;
906 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800907 std::vector<std::string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700908 Flags flags = Flags()
909 .requiredFlag("-o", "Output path", &options.outputPath)
910 .requiredFlag("--manifest", "Path to the Android manifest to build",
911 &options.manifestPath)
912 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -0800913 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -0800914 "The last conflicting resource given takes precedence.",
915 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700916 .optionalFlag("--java", "Directory in which to generate R.java",
917 &options.generateJavaClassPath)
918 .optionalFlag("--proguard", "Output file for generated Proguard rules",
919 &options.generateProguardRulesPath)
920 .optionalSwitch("--no-auto-version",
921 "Disables automatic style and layout SDK versioning",
922 &options.noAutoVersion)
923 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
924 "by -o",
925 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800926 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
927 "AndroidManifest.xml", &minSdkVersion)
928 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
929 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -0800930 .optionalFlag("--version-code", "Version code (integer) to inject into the "
931 "AndroidManifest.xml if none is present", &versionCode)
932 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
933 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700934 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700935 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800936 "private symbols.\n"
937 "If not specified, public and private symbols will use the application's "
938 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -0800939 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
940 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -0800941 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800942 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800943 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
944 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -0800945 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
946 &renameManifestPackage)
947 .optionalFlag("--rename-instrumentation-target-package",
948 "Changes the name of the target package for instrumentation. Most useful "
949 "when used\nin conjunction with --rename-manifest-package",
950 &renameInstrumentationTargetPackage)
951 .optionalFlagList("-0", "File extensions not to compress",
952 &options.extensionsToNotCompress)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700953 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
954
955 if (!flags.parse("aapt2 link", args, &std::cerr)) {
956 return 1;
957 }
958
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700959 if (privateSymbolsPackage) {
960 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
961 }
962
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800963 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800964 options.manifestFixerOptions.minSdkVersionDefault =
965 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800966 }
967
968 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800969 options.manifestFixerOptions.targetSdkVersionDefault =
970 util::utf8ToUtf16(targetSdkVersion.value());
971 }
972
973 if (renameManifestPackage) {
974 options.manifestFixerOptions.renameManifestPackage =
975 util::utf8ToUtf16(renameManifestPackage.value());
976 }
977
978 if (renameInstrumentationTargetPackage) {
979 options.manifestFixerOptions.renameInstrumentationTargetPackage =
980 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
981 }
982
983 if (versionCode) {
984 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
985 }
986
987 if (versionName) {
988 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
989 }
990
991 if (customJavaPackage) {
992 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800993 }
994
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800995 // Populate the set of extra packages for which to generate R.java.
996 for (std::string& extraPackage : extraJavaPackages) {
997 // A given package can actually be a colon separated list of packages.
998 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800999 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001000 }
1001 }
1002
Adam Lesinskifb48d292015-11-07 15:52:13 -08001003 LinkCommand cmd(options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001004 return cmd.run(flags.getArgs());
1005}
1006
1007} // namespace aapt