blob: 364a55b5323531c60190daa1273eb70b608e0f3d [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 Lesinskic446a732016-01-21 11:04:46 -0800554
555 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800556 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800557
Adam Lesinskic446a732016-01-21 11:04:46 -0800558 // Ignore non .flat files. This could be classes.dex or something else that happens
559 // to be in an archive.
560 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800561 }
562
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700563 int run(const std::vector<std::string>& inputFiles) {
564 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800565 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
566 mContext.getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700567 if (!manifestXml) {
568 return 1;
569 }
570
571 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
572 mContext.mCompilationPackage = maybeAppInfo.value().package;
573 } else {
574 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
575 << "no package specified in <manifest> tag");
576 return 1;
577 }
578
579 if (!util::isJavaPackageName(mContext.mCompilationPackage)) {
580 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
581 << "invalid package name '"
582 << mContext.mCompilationPackage
583 << "'");
584 return 1;
585 }
586
587 mContext.mNameMangler = util::make_unique<NameMangler>(
588 NameManglerPolicy{ mContext.mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700589
590 if (mContext.mCompilationPackage == u"android") {
591 mContext.mPackageId = 0x01;
592 } else {
593 mContext.mPackageId = 0x7f;
594 }
595
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700596 mContext.mSymbols = createSymbolTableFromIncludePaths();
597 if (!mContext.mSymbols) {
598 return 1;
599 }
600
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800601 TableMergerOptions tableMergerOptions;
602 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
603 mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800604
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700605 if (mOptions.verbose) {
606 mContext.getDiagnostics()->note(
607 DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
608 << "with package ID " << std::hex << (int) mContext.mPackageId);
609 }
610
Adam Lesinskifb48d292015-11-07 15:52:13 -0800611
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700612 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800613 if (!processFile(input, false)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800614 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
615 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800616 }
617 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700618
Adam Lesinskifb48d292015-11-07 15:52:13 -0800619 for (const std::string& input : mOptions.overlayFiles) {
620 if (!processFile(input, true)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800621 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
622 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700623 }
624 }
625
Adam Lesinskifb48d292015-11-07 15:52:13 -0800626 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700627 return 1;
628 }
629
630 if (!mOptions.staticLib) {
631 PrivateAttributeMover mover;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800632 if (!mover.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700633 mContext.getDiagnostics()->error(
634 DiagMessage() << "failed moving private attributes");
635 return 1;
636 }
637 }
638
639 {
640 IdAssigner idAssigner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800641 if (!idAssigner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700642 mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
643 return 1;
644 }
645 }
646
Adam Lesinskifb48d292015-11-07 15:52:13 -0800647 mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
648 mContext.mCompilationPackage, mTableMerger->getMergedPackages() });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700649 mContext.mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800650 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700651 .addSymbolTable(std::move(mContext.mSymbols))
652 .build();
653
654 {
655 ReferenceLinker linker;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800656 if (!linker.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700657 mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
658 return 1;
659 }
660 }
661
662 proguard::KeepSet proguardKeepSet;
663
664 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
665 if (!archiveWriter) {
666 mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive");
667 return 1;
668 }
669
Adam Lesinski467f1712015-11-16 17:35:44 -0800670 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700671 {
Adam Lesinski52364f72016-01-11 13:10:24 -0800672 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800673 if (!manifestFixer.consume(&mContext, manifestXml.get())) {
674 error = true;
675 }
676
Adam Lesinski467f1712015-11-16 17:35:44 -0800677 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
678 // (aka, which package the AndroidManifest.xml is coming from).
679 // So we give it a package name so it can see local resources.
680 manifestXml->file.name.package = mContext.getCompilationPackage().toString();
681
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700682 XmlReferenceLinker manifestLinker;
683 if (manifestLinker.consume(&mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700684 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
685 manifestXml.get(),
686 &proguardKeepSet)) {
687 error = true;
688 }
689
Adam Lesinskica5638f2015-10-21 14:42:43 -0700690 if (mOptions.generateJavaClassPath) {
691 if (!writeManifestJavaFile(manifestXml.get())) {
692 error = true;
693 }
694 }
695
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700696 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
697 archiveWriter.get())) {
698 error = true;
699 }
700 } else {
701 error = true;
702 }
703 }
704
Adam Lesinski467f1712015-11-16 17:35:44 -0800705 if (error) {
706 mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest");
707 return 1;
708 }
709
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800710 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
711 const ResourceKeyRef& key = mergeEntry.first;
712 const FileToMerge& fileToMerge = mergeEntry.second;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800713
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800714 const StringPiece path = fileToMerge.file->getSource().path;
715
716 if (key.name.type != ResourceType::kRaw &&
717 (util::stringEndsWith<char>(path, ".xml.flat") ||
718 util::stringEndsWith<char>(path, ".xml"))) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700719 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800720 mContext.getDiagnostics()->note(DiagMessage() << "linking " << path);
721 }
722
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800723 io::IFile* file = fileToMerge.file;
724 std::unique_ptr<io::IData> data = file->openAsData();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800725 if (!data) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800726 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800727 << "failed to open file");
728 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700729 }
730
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800731 std::unique_ptr<xml::XmlResource> xmlRes;
732 if (util::stringEndsWith<char>(path, ".flat")) {
733 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
734 data->data(), data->size(),
735 mContext.getDiagnostics());
736 } else {
737 xmlRes = xml::inflate(data->data(), data->size(), mContext.getDiagnostics(),
738 file->getSource());
739 }
740
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700741 if (!xmlRes) {
742 return 1;
743 }
744
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800745 // Create the file description header.
746 xmlRes->file = ResourceFile{
747 key.name.toResourceName(),
748 key.config,
749 fileToMerge.originalSource,
750 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700751
752 XmlReferenceLinker xmlLinker;
753 if (xmlLinker.consume(&mContext, xmlRes.get())) {
754 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
755 &proguardKeepSet)) {
756 error = true;
757 }
758
759 Maybe<size_t> maxSdkLevel;
760 if (!mOptions.noAutoVersion) {
761 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
762 }
763
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800764 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700765 archiveWriter.get())) {
766 error = true;
767 }
768
769 if (!mOptions.noAutoVersion) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800770 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700771 xmlRes->file.name);
772 for (int sdkLevel : xmlLinker.getSdkLevels()) {
773 if (sdkLevel > xmlRes->file.config.sdkVersion &&
774 shouldGenerateVersionedResource(result.value().entry,
775 xmlRes->file.config,
776 sdkLevel)) {
777 xmlRes->file.config.sdkVersion = sdkLevel;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800778
779 std::string genResourcePath = ResourceUtils::buildResourceFileName(
780 xmlRes->file, mContext.getNameMangler());
781
Adam Lesinskia40e9722015-11-24 19:11:46 -0800782 bool added = mFinalTable.addFileReference(
783 xmlRes->file.name,
784 xmlRes->file.config,
785 xmlRes->file.source,
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800786 util::utf8ToUtf16(genResourcePath),
Adam Lesinskia40e9722015-11-24 19:11:46 -0800787 mContext.getDiagnostics());
788 if (!added) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700789 error = true;
790 continue;
791 }
792
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800793 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
794 archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700795 error = true;
796 }
797 }
798 }
799 }
800
801 } else {
802 error = true;
803 }
804 } else {
805 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800806 mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700807 }
808
Adam Lesinski52364f72016-01-11 13:10:24 -0800809 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700810 archiveWriter.get())) {
811 error = true;
812 }
813 }
814 }
815
816 if (error) {
817 mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources");
818 return 1;
819 }
820
821 if (!mOptions.noAutoVersion) {
822 AutoVersioner versioner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800823 if (!versioner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700824 mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
825 return 1;
826 }
827 }
828
Adam Lesinskifb48d292015-11-07 15:52:13 -0800829 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700830 mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
831 return 1;
832 }
833
834 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700835 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -0800836 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
837
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700838 if (mOptions.staticLib) {
839 options.useFinal = false;
840 }
841
Adam Lesinski52364f72016-01-11 13:10:24 -0800842 const StringPiece16 actualPackage = mContext.getCompilationPackage();
Adam Lesinski83f22552015-11-07 11:51:23 -0800843 StringPiece16 outputPackage = mContext.getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -0800844 if (mOptions.customJavaPackage) {
845 // Override the output java package to the custom one.
846 outputPackage = mOptions.customJavaPackage.value();
847 }
Adam Lesinski83f22552015-11-07 11:51:23 -0800848
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700849 if (mOptions.privateSymbols) {
850 // If we defined a private symbols package, we only emit Public symbols
851 // to the original package, and private and public symbols to the private package.
852
853 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800854 if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -0800855 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700856 return 1;
857 }
858
859 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -0800860 outputPackage = mOptions.privateSymbols.value();
861 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700862
Adam Lesinskifb48d292015-11-07 15:52:13 -0800863 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800864 return 1;
865 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700866
Adam Lesinski52364f72016-01-11 13:10:24 -0800867 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
868 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700869 return 1;
870 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700871 }
872 }
873
874 if (mOptions.generateProguardRulesPath) {
875 if (!writeProguardFile(proguardKeepSet)) {
876 return 1;
877 }
878 }
879
880 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800881 Debug::printTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700882 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700883 return 0;
884 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800885
886private:
887 LinkOptions mOptions;
888 LinkContext mContext;
889 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800890
891 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800892 std::unique_ptr<TableMerger> mTableMerger;
893
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800894 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -0800895 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800896
897 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800898 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700899};
900
901int link(const std::vector<StringPiece>& args) {
902 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700903 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800904 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -0800905 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
906 Maybe<std::string> versionCode, versionName;
907 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800908 std::vector<std::string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700909 Flags flags = Flags()
910 .requiredFlag("-o", "Output path", &options.outputPath)
911 .requiredFlag("--manifest", "Path to the Android manifest to build",
912 &options.manifestPath)
913 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -0800914 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -0800915 "The last conflicting resource given takes precedence.",
916 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700917 .optionalFlag("--java", "Directory in which to generate R.java",
918 &options.generateJavaClassPath)
919 .optionalFlag("--proguard", "Output file for generated Proguard rules",
920 &options.generateProguardRulesPath)
921 .optionalSwitch("--no-auto-version",
922 "Disables automatic style and layout SDK versioning",
923 &options.noAutoVersion)
924 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
925 "by -o",
926 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800927 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
928 "AndroidManifest.xml", &minSdkVersion)
929 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
930 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -0800931 .optionalFlag("--version-code", "Version code (integer) to inject into the "
932 "AndroidManifest.xml if none is present", &versionCode)
933 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
934 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700935 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700936 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800937 "private symbols.\n"
938 "If not specified, public and private symbols will use the application's "
939 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -0800940 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
941 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -0800942 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800943 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800944 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
945 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -0800946 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
947 &renameManifestPackage)
948 .optionalFlag("--rename-instrumentation-target-package",
949 "Changes the name of the target package for instrumentation. Most useful "
950 "when used\nin conjunction with --rename-manifest-package",
951 &renameInstrumentationTargetPackage)
952 .optionalFlagList("-0", "File extensions not to compress",
953 &options.extensionsToNotCompress)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700954 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
955
956 if (!flags.parse("aapt2 link", args, &std::cerr)) {
957 return 1;
958 }
959
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700960 if (privateSymbolsPackage) {
961 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
962 }
963
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800964 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800965 options.manifestFixerOptions.minSdkVersionDefault =
966 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800967 }
968
969 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800970 options.manifestFixerOptions.targetSdkVersionDefault =
971 util::utf8ToUtf16(targetSdkVersion.value());
972 }
973
974 if (renameManifestPackage) {
975 options.manifestFixerOptions.renameManifestPackage =
976 util::utf8ToUtf16(renameManifestPackage.value());
977 }
978
979 if (renameInstrumentationTargetPackage) {
980 options.manifestFixerOptions.renameInstrumentationTargetPackage =
981 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
982 }
983
984 if (versionCode) {
985 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
986 }
987
988 if (versionName) {
989 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
990 }
991
992 if (customJavaPackage) {
993 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800994 }
995
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800996 // Populate the set of extra packages for which to generate R.java.
997 for (std::string& extraPackage : extraJavaPackages) {
998 // A given package can actually be a colon separated list of packages.
999 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001000 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001001 }
1002 }
1003
Adam Lesinskifb48d292015-11-07 15:52:13 -08001004 LinkCommand cmd(options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001005 return cmd.run(flags.getArgs());
1006}
1007
1008} // namespace aapt