blob: 8a87d9691b5d8658bbe78059b635c0c7526156b5 [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 Lesinskifc9570e62015-11-16 15:07:54 -080054 std::set<std::string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070055 Maybe<std::string> generateProguardRulesPath;
56 bool noAutoVersion = false;
57 bool staticLib = false;
58 bool verbose = false;
59 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080060 bool autoAddOverlay = false;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070061 Maybe<std::u16string> privateSymbols;
Adam Lesinski2ae4a872015-11-02 16:10:55 -080062 Maybe<std::u16string> minSdkVersionDefault;
63 Maybe<std::u16string> targetSdkVersionDefault;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070064};
65
66struct LinkContext : public IAaptContext {
67 StdErrDiagnostics mDiagnostics;
68 std::unique_ptr<NameMangler> mNameMangler;
69 std::u16string mCompilationPackage;
70 uint8_t mPackageId;
71 std::unique_ptr<ISymbolTable> mSymbols;
72
73 IDiagnostics* getDiagnostics() override {
74 return &mDiagnostics;
75 }
76
77 NameMangler* getNameMangler() override {
78 return mNameMangler.get();
79 }
80
81 StringPiece16 getCompilationPackage() override {
82 return mCompilationPackage;
83 }
84
85 uint8_t getPackageId() override {
86 return mPackageId;
87 }
88
89 ISymbolTable* getExternalSymbols() override {
90 return mSymbols.get();
91 }
92};
93
Adam Lesinskifb48d292015-11-07 15:52:13 -080094class LinkCommand {
95public:
96 LinkCommand(const LinkOptions& options) :
Adam Lesinskia40e9722015-11-24 19:11:46 -080097 mOptions(options), mContext(), mFinalTable(), mFileCollection(nullptr) {
98 std::unique_ptr<io::FileCollection> fileCollection =
99 util::make_unique<io::FileCollection>();
100
101 // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
102 mFileCollection = fileCollection.get();
103
104 // Move it to the collection.
105 mCollections.push_back(std::move(fileCollection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800106 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700107
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700108 /**
109 * Creates a SymbolTable that loads symbols from the various APKs and caches the
110 * results for faster lookup.
111 */
112 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
113 AssetManagerSymbolTableBuilder builder;
114 for (const std::string& path : mOptions.includePaths) {
115 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800116 mContext.getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700117 }
118
119 std::unique_ptr<android::AssetManager> assetManager =
120 util::make_unique<android::AssetManager>();
121 int32_t cookie = 0;
122 if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
123 mContext.getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800124 DiagMessage(path) << "failed to load include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700125 return {};
126 }
127 builder.add(std::move(assetManager));
128 }
129 return builder.build();
130 }
131
Adam Lesinskia40e9722015-11-24 19:11:46 -0800132 std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700133 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800134 BinaryResourceParser parser(&mContext, table.get(), source, data, len);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700135 if (!parser.parse()) {
136 return {};
137 }
138 return table;
139 }
140
141 /**
142 * Inflates an XML file from the source path.
143 */
Adam Lesinskia40e9722015-11-24 19:11:46 -0800144 static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700145 std::ifstream fin(path, std::ifstream::binary);
146 if (!fin) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800147 diag->error(DiagMessage(path) << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700148 return {};
149 }
150
Adam Lesinskia40e9722015-11-24 19:11:46 -0800151 return xml::inflate(&fin, diag, Source(path));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700152 }
153
Adam Lesinskia40e9722015-11-24 19:11:46 -0800154 static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(
155 const Source& source,
156 const void* data, size_t len,
157 IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700158 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800159 ssize_t offset = getWrappedDataOffset(data, len, &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700160 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800161 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700162 return {};
163 }
164
Adam Lesinski467f1712015-11-16 17:35:44 -0800165 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800166 reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset),
167 len - static_cast<size_t>(offset),
168 diag,
169 source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700170 if (!xmlRes) {
171 return {};
172 }
173 return xmlRes;
174 }
175
Adam Lesinskia40e9722015-11-24 19:11:46 -0800176 static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
177 const void* data, size_t len,
178 IDiagnostics* diag) {
179 std::unique_ptr<ResourceFile> resFile = util::make_unique<ResourceFile>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700180 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800181 ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700182 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800183 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700184 return {};
185 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800186 return resFile;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700187 }
188
Adam Lesinskia40e9722015-11-24 19:11:46 -0800189 bool copyFileToArchive(io::IFile* file, const std::string& outPath, uint32_t flags,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190 IArchiveWriter* writer) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800191 std::unique_ptr<io::IData> data = file->openAsData();
192 if (!data) {
193 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
194 << "failed to open file");
195 return false;
196 }
197
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700198 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800199 ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700200 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800201 mContext.getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700202 return false;
203 }
204
Adam Lesinskia40e9722015-11-24 19:11:46 -0800205 if (writer->startEntry(outPath, flags)) {
206 if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
207 data->size() - static_cast<size_t>(offset))) {
208 if (writer->finishEntry()) {
209 return true;
210 }
211 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700212 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800213
214 mContext.getDiagnostics()->error(
215 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
216 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700217 }
218
Adam Lesinski467f1712015-11-16 17:35:44 -0800219 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700220 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800221 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700222 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
223 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
224 return AppInfo{ packageAttr->value };
225 }
226 }
227 }
228 return {};
229 }
230
Adam Lesinski979ccb22016-01-11 10:42:19 -0800231 /**
232 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
233 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
234 * is an error and false is returned.
235 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800236 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800237 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
238 return mContext.getCompilationPackage() != pkg->name ||
239 !pkg->id ||
240 pkg->id.value() != mContext.getPackageId();
241 };
242
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700243 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800244 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800245 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700246 // We have a package that is not related to the one we're building!
247 for (const auto& type : package->types) {
248 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800249 ResourceNameRef resName(package->name, type->type, entry->name);
250
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700251 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800252 // Special case the occurrence of an ID that is being generated for the
253 // 'android' package. This is due to legacy reasons.
254 if (valueCast<Id>(configValue.value.get()) &&
255 package->name == u"android") {
256 mContext.getDiagnostics()->warn(
257 DiagMessage(configValue.value->getSource())
258 << "generated id '" << resName
259 << "' for external package '" << package->name
260 << "'");
261 } else {
262 mContext.getDiagnostics()->error(
263 DiagMessage(configValue.value->getSource())
264 << "defined resource '" << resName
265 << "' for external package '" << package->name
266 << "'");
267 error = true;
268 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700269 }
270 }
271 }
272 }
273 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800274
275 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
276 isExtPackageFunc);
277 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700278 return !error;
279 }
280
281 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
282 if (mOptions.outputToDirectory) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800283 return createDirectoryArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700284 } else {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800285 return createZipFileArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700286 }
287 }
288
289 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
290 BigBuffer buffer(1024);
291 TableFlattenerOptions options = {};
292 options.useExtendedChunks = mOptions.staticLib;
293 TableFlattener flattener(&buffer, options);
294 if (!flattener.consume(&mContext, table)) {
295 return false;
296 }
297
Adam Lesinskia40e9722015-11-24 19:11:46 -0800298 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
299 if (writer->writeEntry(buffer)) {
300 if (writer->finishEntry()) {
301 return true;
302 }
303 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700304 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800305
306 mContext.getDiagnostics()->error(
307 DiagMessage() << "failed to write resources.arsc to archive");
308 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700309 }
310
Adam Lesinski467f1712015-11-16 17:35:44 -0800311 bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700312 IArchiveWriter* writer) {
313 BigBuffer buffer(1024);
314 XmlFlattenerOptions options = {};
315 options.keepRawValues = mOptions.staticLib;
316 options.maxSdkLevel = maxSdkLevel;
317 XmlFlattener flattener(&buffer, options);
318 if (!flattener.consume(&mContext, xmlRes)) {
319 return false;
320 }
321
Adam Lesinskia40e9722015-11-24 19:11:46 -0800322
323 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
324 if (writer->writeEntry(buffer)) {
325 if (writer->finishEntry()) {
326 return true;
327 }
328 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700329 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800330 mContext.getDiagnostics()->error(
331 DiagMessage() << "failed to write " << path << " to archive");
332 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700333 }
334
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700335 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
336 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700337 if (!mOptions.generateJavaClassPath) {
338 return true;
339 }
340
341 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700342 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700343 file::mkdirs(outPath);
344 file::appendPath(&outPath, "R.java");
345
346 std::ofstream fout(outPath, std::ofstream::binary);
347 if (!fout) {
348 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
349 return false;
350 }
351
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700352 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700353 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700354 mContext.getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
355 return false;
356 }
357 return true;
358 }
359
Adam Lesinski467f1712015-11-16 17:35:44 -0800360 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700361 if (!mOptions.generateJavaClassPath) {
362 return true;
363 }
364
365 std::string outPath = mOptions.generateJavaClassPath.value();
366 file::appendPath(&outPath,
367 file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage())));
368 file::mkdirs(outPath);
369 file::appendPath(&outPath, "Manifest.java");
370
371 std::ofstream fout(outPath, std::ofstream::binary);
372 if (!fout) {
373 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
374 return false;
375 }
376
377 ManifestClassGenerator generator;
378 if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(),
379 manifestXml, &fout)) {
380 return false;
381 }
382
383 if (!fout) {
384 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
385 return false;
386 }
387 return true;
388 }
389
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700390 bool writeProguardFile(const proguard::KeepSet& keepSet) {
391 if (!mOptions.generateProguardRulesPath) {
392 return true;
393 }
394
395 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
396 if (!fout) {
397 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
398 return false;
399 }
400
401 proguard::writeKeepSet(&fout, keepSet);
402 if (!fout) {
403 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
404 return false;
405 }
406 return true;
407 }
408
Adam Lesinskifb48d292015-11-07 15:52:13 -0800409 bool mergeStaticLibrary(const std::string& input) {
410 // TODO(adamlesinski): Load resources from a static library APK and merge the table into
411 // TableMerger.
412 mContext.getDiagnostics()->warn(DiagMessage()
413 << "linking static libraries not supported yet: "
414 << input);
415 return true;
416 }
417
Adam Lesinskia40e9722015-11-24 19:11:46 -0800418 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800419 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800420 mContext.getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800421 }
422
Adam Lesinskia40e9722015-11-24 19:11:46 -0800423 std::unique_ptr<io::IData> data = file->openAsData();
424 if (!data) {
425 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
426 << "failed to open file");
427 return false;
428 }
429
430 std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(),
431 data->size());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800432 if (!table) {
433 return false;
434 }
435
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800436 bool result = false;
437 if (override) {
438 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
439 } else {
440 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800441 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800442 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800443 }
444
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800445 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
446 if (mOptions.verbose) {
447 mContext.getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800448 }
449
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800450 bool result = false;
451 if (overlay) {
452 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800453 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800454 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800455 }
456
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800457 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800458 return false;
459 }
460
461 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800462 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800463 if (exportedSymbol.name.package.empty()) {
464 exportedSymbol.name.package = mContext.getCompilationPackage().toString();
465 }
466
467 ResourceNameRef resName = exportedSymbol.name;
468
469 Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
470 exportedSymbol.name);
471 if (mangledName) {
472 resName = mangledName.value();
473 }
474
475 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800476 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800477 bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
Adam Lesinskia40e9722015-11-24 19:11:46 -0800478 mContext.getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800479 if (!result) {
480 return false;
481 }
482 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800483 return true;
484 }
485
Adam Lesinskia40e9722015-11-24 19:11:46 -0800486 /**
487 * Creates an io::IFileCollection from the ZIP archive and processes the files within.
488 */
489 bool mergeArchive(const std::string& input, bool override) {
490 std::string errorStr;
491 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
492 input, &errorStr);
493 if (!collection) {
494 mContext.getDiagnostics()->error(DiagMessage(input) << errorStr);
495 return false;
496 }
497
498 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800499 for (auto iter = collection->iterator(); iter->hasNext(); ) {
500 if (!processFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800501 error = true;
502 }
503 }
504
505 // Make sure to move the collection into the set of IFileCollections.
506 mCollections.push_back(std::move(collection));
507 return !error;
508 }
509
510 bool processFile(const std::string& path, bool override) {
511 if (util::stringEndsWith<char>(path, ".flata")) {
512 return mergeArchive(path, override);
513 }
514
515 io::IFile* file = mFileCollection->insertFile(path);
516 return processFile(file, override);
517 }
518
519 bool processFile(io::IFile* file, bool override) {
520 const Source& src = file->getSource();
521 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
522 return mergeResourceTable(file, override);
523 } else {
524 // Try opening the file and looking for an Export header.
525 std::unique_ptr<io::IData> data = file->openAsData();
526 if (!data) {
527 mContext.getDiagnostics()->error(DiagMessage(src) << "failed to open");
528 return false;
529 }
530
531 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
532 src, data->data(), data->size(), mContext.getDiagnostics());
533 if (resourceFile) {
534 return mergeCompiledFile(file, std::move(resourceFile), override);
535 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800536 }
537 return false;
538 }
539
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700540 int run(const std::vector<std::string>& inputFiles) {
541 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800542 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
543 mContext.getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700544 if (!manifestXml) {
545 return 1;
546 }
547
548 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
549 mContext.mCompilationPackage = maybeAppInfo.value().package;
550 } else {
551 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
552 << "no package specified in <manifest> tag");
553 return 1;
554 }
555
556 if (!util::isJavaPackageName(mContext.mCompilationPackage)) {
557 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
558 << "invalid package name '"
559 << mContext.mCompilationPackage
560 << "'");
561 return 1;
562 }
563
564 mContext.mNameMangler = util::make_unique<NameMangler>(
565 NameManglerPolicy{ mContext.mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700566
567 if (mContext.mCompilationPackage == u"android") {
568 mContext.mPackageId = 0x01;
569 } else {
570 mContext.mPackageId = 0x7f;
571 }
572
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700573 mContext.mSymbols = createSymbolTableFromIncludePaths();
574 if (!mContext.mSymbols) {
575 return 1;
576 }
577
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800578 TableMergerOptions tableMergerOptions;
579 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
580 mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800581
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700582 if (mOptions.verbose) {
583 mContext.getDiagnostics()->note(
584 DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
585 << "with package ID " << std::hex << (int) mContext.mPackageId);
586 }
587
Adam Lesinskifb48d292015-11-07 15:52:13 -0800588
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700589 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800590 if (!processFile(input, false)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800591 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
592 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800593 }
594 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700595
Adam Lesinskifb48d292015-11-07 15:52:13 -0800596 for (const std::string& input : mOptions.overlayFiles) {
597 if (!processFile(input, true)) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800598 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
599 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700600 }
601 }
602
Adam Lesinskifb48d292015-11-07 15:52:13 -0800603 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700604 return 1;
605 }
606
607 if (!mOptions.staticLib) {
608 PrivateAttributeMover mover;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800609 if (!mover.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700610 mContext.getDiagnostics()->error(
611 DiagMessage() << "failed moving private attributes");
612 return 1;
613 }
614 }
615
616 {
617 IdAssigner idAssigner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800618 if (!idAssigner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700619 mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
620 return 1;
621 }
622 }
623
Adam Lesinskifb48d292015-11-07 15:52:13 -0800624 mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
625 mContext.mCompilationPackage, mTableMerger->getMergedPackages() });
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700626 mContext.mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800627 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700628 .addSymbolTable(std::move(mContext.mSymbols))
629 .build();
630
631 {
632 ReferenceLinker linker;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800633 if (!linker.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700634 mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
635 return 1;
636 }
637 }
638
639 proguard::KeepSet proguardKeepSet;
640
641 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
642 if (!archiveWriter) {
643 mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive");
644 return 1;
645 }
646
Adam Lesinski467f1712015-11-16 17:35:44 -0800647 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700648 {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800649 ManifestFixerOptions manifestFixerOptions;
650 manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
651 manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
652 ManifestFixer manifestFixer(manifestFixerOptions);
653 if (!manifestFixer.consume(&mContext, manifestXml.get())) {
654 error = true;
655 }
656
Adam Lesinski467f1712015-11-16 17:35:44 -0800657 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
658 // (aka, which package the AndroidManifest.xml is coming from).
659 // So we give it a package name so it can see local resources.
660 manifestXml->file.name.package = mContext.getCompilationPackage().toString();
661
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700662 XmlReferenceLinker manifestLinker;
663 if (manifestLinker.consume(&mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700664 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
665 manifestXml.get(),
666 &proguardKeepSet)) {
667 error = true;
668 }
669
Adam Lesinskica5638f2015-10-21 14:42:43 -0700670 if (mOptions.generateJavaClassPath) {
671 if (!writeManifestJavaFile(manifestXml.get())) {
672 error = true;
673 }
674 }
675
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700676 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
677 archiveWriter.get())) {
678 error = true;
679 }
680 } else {
681 error = true;
682 }
683 }
684
Adam Lesinski467f1712015-11-16 17:35:44 -0800685 if (error) {
686 mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest");
687 return 1;
688 }
689
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800690 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
691 const ResourceKeyRef& key = mergeEntry.first;
692 const FileToMerge& fileToMerge = mergeEntry.second;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800693
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800694 const StringPiece path = fileToMerge.file->getSource().path;
695
696 if (key.name.type != ResourceType::kRaw &&
697 (util::stringEndsWith<char>(path, ".xml.flat") ||
698 util::stringEndsWith<char>(path, ".xml"))) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700699 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800700 mContext.getDiagnostics()->note(DiagMessage() << "linking " << path);
701 }
702
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800703 io::IFile* file = fileToMerge.file;
704 std::unique_ptr<io::IData> data = file->openAsData();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800705 if (!data) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800706 mContext.getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800707 << "failed to open file");
708 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700709 }
710
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800711 std::unique_ptr<xml::XmlResource> xmlRes;
712 if (util::stringEndsWith<char>(path, ".flat")) {
713 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
714 data->data(), data->size(),
715 mContext.getDiagnostics());
716 } else {
717 xmlRes = xml::inflate(data->data(), data->size(), mContext.getDiagnostics(),
718 file->getSource());
719 }
720
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700721 if (!xmlRes) {
722 return 1;
723 }
724
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800725 // Create the file description header.
726 xmlRes->file = ResourceFile{
727 key.name.toResourceName(),
728 key.config,
729 fileToMerge.originalSource,
730 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700731
732 XmlReferenceLinker xmlLinker;
733 if (xmlLinker.consume(&mContext, xmlRes.get())) {
734 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
735 &proguardKeepSet)) {
736 error = true;
737 }
738
739 Maybe<size_t> maxSdkLevel;
740 if (!mOptions.noAutoVersion) {
741 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
742 }
743
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800744 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700745 archiveWriter.get())) {
746 error = true;
747 }
748
749 if (!mOptions.noAutoVersion) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800750 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700751 xmlRes->file.name);
752 for (int sdkLevel : xmlLinker.getSdkLevels()) {
753 if (sdkLevel > xmlRes->file.config.sdkVersion &&
754 shouldGenerateVersionedResource(result.value().entry,
755 xmlRes->file.config,
756 sdkLevel)) {
757 xmlRes->file.config.sdkVersion = sdkLevel;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800758
759 std::string genResourcePath = ResourceUtils::buildResourceFileName(
760 xmlRes->file, mContext.getNameMangler());
761
Adam Lesinskia40e9722015-11-24 19:11:46 -0800762 bool added = mFinalTable.addFileReference(
763 xmlRes->file.name,
764 xmlRes->file.config,
765 xmlRes->file.source,
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800766 util::utf8ToUtf16(genResourcePath),
Adam Lesinskia40e9722015-11-24 19:11:46 -0800767 mContext.getDiagnostics());
768 if (!added) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700769 error = true;
770 continue;
771 }
772
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800773 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
774 archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700775 error = true;
776 }
777 }
778 }
779 }
780
781 } else {
782 error = true;
783 }
784 } else {
785 if (mOptions.verbose) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800786 mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700787 }
788
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800789 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700790 archiveWriter.get())) {
791 error = true;
792 }
793 }
794 }
795
796 if (error) {
797 mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources");
798 return 1;
799 }
800
801 if (!mOptions.noAutoVersion) {
802 AutoVersioner versioner;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800803 if (!versioner.consume(&mContext, &mFinalTable)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700804 mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
805 return 1;
806 }
807 }
808
Adam Lesinskifb48d292015-11-07 15:52:13 -0800809 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700810 mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
811 return 1;
812 }
813
814 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700815 JavaClassGeneratorOptions options;
816 if (mOptions.staticLib) {
817 options.useFinal = false;
818 }
819
Adam Lesinski83f22552015-11-07 11:51:23 -0800820 StringPiece16 actualPackage = mContext.getCompilationPackage();
821 StringPiece16 outputPackage = mContext.getCompilationPackage();
822
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700823 if (mOptions.privateSymbols) {
824 // If we defined a private symbols package, we only emit Public symbols
825 // to the original package, and private and public symbols to the private package.
826
827 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800828 if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700829 mContext.getCompilationPackage(), options)) {
830 return 1;
831 }
832
833 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -0800834 outputPackage = mOptions.privateSymbols.value();
835 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700836
Adam Lesinskifb48d292015-11-07 15:52:13 -0800837 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800838 return 1;
839 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700840
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800841 for (const std::string& extraPackage : mOptions.extraJavaPackages) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800842 if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage),
Adam Lesinski83f22552015-11-07 11:51:23 -0800843 options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700844 return 1;
845 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700846 }
847 }
848
849 if (mOptions.generateProguardRulesPath) {
850 if (!writeProguardFile(proguardKeepSet)) {
851 return 1;
852 }
853 }
854
855 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800856 Debug::printTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700857 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700858 return 0;
859 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800860
861private:
862 LinkOptions mOptions;
863 LinkContext mContext;
864 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800865
866 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800867 std::unique_ptr<TableMerger> mTableMerger;
868
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800869 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -0800870 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800871
872 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800873 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700874};
875
876int link(const std::vector<StringPiece>& args) {
877 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700878 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800879 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800880 std::vector<std::string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700881 Flags flags = Flags()
882 .requiredFlag("-o", "Output path", &options.outputPath)
883 .requiredFlag("--manifest", "Path to the Android manifest to build",
884 &options.manifestPath)
885 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinskifb48d292015-11-07 15:52:13 -0800886 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. "
887 "The last conflicting resource given takes precedence.",
888 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700889 .optionalFlag("--java", "Directory in which to generate R.java",
890 &options.generateJavaClassPath)
891 .optionalFlag("--proguard", "Output file for generated Proguard rules",
892 &options.generateProguardRulesPath)
893 .optionalSwitch("--no-auto-version",
894 "Disables automatic style and layout SDK versioning",
895 &options.noAutoVersion)
896 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
897 "by -o",
898 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800899 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
900 "AndroidManifest.xml", &minSdkVersion)
901 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
902 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700903 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700904 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800905 "private symbols.\n"
906 "If not specified, public and private symbols will use the application's "
907 "package name", &privateSymbolsPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -0800908 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800909 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800910 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
911 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700912 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
913
914 if (!flags.parse("aapt2 link", args, &std::cerr)) {
915 return 1;
916 }
917
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700918 if (privateSymbolsPackage) {
919 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
920 }
921
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800922 if (minSdkVersion) {
923 options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
924 }
925
926 if (targetSdkVersion) {
927 options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
928 }
929
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800930 // Populate the set of extra packages for which to generate R.java.
931 for (std::string& extraPackage : extraJavaPackages) {
932 // A given package can actually be a colon separated list of packages.
933 for (StringPiece package : util::split(extraPackage, ':')) {
934 options.extraJavaPackages.insert(package.toString());
935 }
936 }
937
Adam Lesinskifb48d292015-11-07 15:52:13 -0800938 LinkCommand cmd(options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700939 return cmd.run(flags.getArgs());
940}
941
942} // namespace aapt