blob: ad701deac16f89e896223240ac85904feb073efe [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"
20#include "JavaClassGenerator.h"
21#include "NameMangler.h"
22#include "ProguardRules.h"
23#include "XmlDom.h"
24
25#include "compile/IdAssigner.h"
26#include "flatten/Archive.h"
27#include "flatten/TableFlattener.h"
28#include "flatten/XmlFlattener.h"
29#include "link/Linkers.h"
30#include "link/TableMerger.h"
31#include "process/IResourceTableConsumer.h"
32#include "process/SymbolTable.h"
33#include "unflatten/BinaryResourceParser.h"
34#include "unflatten/FileExportHeaderReader.h"
35#include "util/Files.h"
36#include "util/StringPiece.h"
37
38#include <fstream>
39#include <sys/stat.h>
40#include <utils/FileMap.h>
41#include <vector>
42
43namespace aapt {
44
45struct LinkOptions {
46 std::string outputPath;
47 std::string manifestPath;
48 std::vector<std::string> includePaths;
49 Maybe<std::string> generateJavaClassPath;
50 Maybe<std::string> generateProguardRulesPath;
51 bool noAutoVersion = false;
52 bool staticLib = false;
53 bool verbose = false;
54 bool outputToDirectory = false;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070055 Maybe<std::u16string> privateSymbols;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070056};
57
58struct LinkContext : public IAaptContext {
59 StdErrDiagnostics mDiagnostics;
60 std::unique_ptr<NameMangler> mNameMangler;
61 std::u16string mCompilationPackage;
62 uint8_t mPackageId;
63 std::unique_ptr<ISymbolTable> mSymbols;
64
65 IDiagnostics* getDiagnostics() override {
66 return &mDiagnostics;
67 }
68
69 NameMangler* getNameMangler() override {
70 return mNameMangler.get();
71 }
72
73 StringPiece16 getCompilationPackage() override {
74 return mCompilationPackage;
75 }
76
77 uint8_t getPackageId() override {
78 return mPackageId;
79 }
80
81 ISymbolTable* getExternalSymbols() override {
82 return mSymbols.get();
83 }
84};
85
86struct LinkCommand {
87 LinkOptions mOptions;
88 LinkContext mContext;
89
90 std::string buildResourceFileName(const ResourceFile& resFile) {
91 std::stringstream out;
92 out << "res/" << resFile.name.type;
93 if (resFile.config != ConfigDescription{}) {
94 out << "-" << resFile.config;
95 }
96 out << "/";
97
98 if (mContext.getNameMangler()->shouldMangle(resFile.name.package)) {
99 out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
100 } else {
101 out << resFile.name.entry;
102 }
103 out << file::getExtension(resFile.source.path);
104 return out.str();
105 }
106
107 /**
108 * Creates a SymbolTable that loads symbols from the various APKs and caches the
109 * results for faster lookup.
110 */
111 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
112 AssetManagerSymbolTableBuilder builder;
113 for (const std::string& path : mOptions.includePaths) {
114 if (mOptions.verbose) {
115 mContext.getDiagnostics()->note(
116 DiagMessage(Source{ path }) << "loading include path");
117 }
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(
124 DiagMessage(Source{ path }) << "failed to load include path");
125 return {};
126 }
127 builder.add(std::move(assetManager));
128 }
129 return builder.build();
130 }
131
132 /**
133 * Loads the resource table (not inside an apk) at the given path.
134 */
135 std::unique_ptr<ResourceTable> loadTable(const std::string& input) {
136 std::string errorStr;
137 Maybe<android::FileMap> map = file::mmapPath(input, &errorStr);
138 if (!map) {
139 mContext.getDiagnostics()->error(DiagMessage(Source{ input }) << errorStr);
140 return {};
141 }
142
143 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
144 BinaryResourceParser parser(&mContext, table.get(), Source{ input },
145 map.value().getDataPtr(), map.value().getDataLength());
146 if (!parser.parse()) {
147 return {};
148 }
149 return table;
150 }
151
152 /**
153 * Inflates an XML file from the source path.
154 */
155 std::unique_ptr<XmlResource> loadXml(const std::string& path) {
156 std::ifstream fin(path, std::ifstream::binary);
157 if (!fin) {
158 mContext.getDiagnostics()->error(DiagMessage(Source{ path }) << strerror(errno));
159 return {};
160 }
161
162 return xml::inflate(&fin, mContext.getDiagnostics(), Source{ path });
163 }
164
165 /**
166 * Inflates a binary XML file from the source path.
167 */
168 std::unique_ptr<XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) {
169 // Read header for symbol info and export info.
170 std::string errorStr;
171 Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
172 if (!maybeF) {
173 mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
174 return {};
175 }
176
177 ssize_t offset = getWrappedDataOffset(maybeF.value().getDataPtr(),
178 maybeF.value().getDataLength(), &errorStr);
179 if (offset < 0) {
180 mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
181 return {};
182 }
183
184 std::unique_ptr<XmlResource> xmlRes = xml::inflate(
185 (const uint8_t*) maybeF.value().getDataPtr() + (size_t) offset,
186 maybeF.value().getDataLength() - offset,
187 mContext.getDiagnostics(), Source(path));
188 if (!xmlRes) {
189 return {};
190 }
191 return xmlRes;
192 }
193
194 Maybe<ResourceFile> loadFileExportHeader(const std::string& path) {
195 // Read header for symbol info and export info.
196 std::string errorStr;
197 Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
198 if (!maybeF) {
199 mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
200 return {};
201 }
202
203 ResourceFile resFile;
204 ssize_t offset = unwrapFileExportHeader(maybeF.value().getDataPtr(),
205 maybeF.value().getDataLength(),
206 &resFile, &errorStr);
207 if (offset < 0) {
208 mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
209 return {};
210 }
211 return std::move(resFile);
212 }
213
214 bool copyFileToArchive(const std::string& path, const std::string& outPath, uint32_t flags,
215 IArchiveWriter* writer) {
216 std::string errorStr;
217 Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr);
218 if (!maybeF) {
219 mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
220 return false;
221 }
222
223 ssize_t offset = getWrappedDataOffset(maybeF.value().getDataPtr(),
224 maybeF.value().getDataLength(),
225 &errorStr);
226 if (offset < 0) {
227 mContext.getDiagnostics()->error(DiagMessage(path) << errorStr);
228 return false;
229 }
230
231 ArchiveEntry* entry = writer->writeEntry(outPath, flags, &maybeF.value(),
232 offset, maybeF.value().getDataLength() - offset);
233 if (!entry) {
234 mContext.getDiagnostics()->error(
235 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
236 return false;
237 }
238 return true;
239 }
240
241 Maybe<AppInfo> extractAppInfoFromManifest(XmlResource* xmlRes) {
242 xml::Node* node = xmlRes->root.get();
243
244 // Find the first xml::Element.
245 while (node && !xml::nodeCast<xml::Element>(node)) {
246 node = !node->children.empty() ? node->children.front().get() : nullptr;
247 }
248
249 // Make sure the first element is <manifest> with package attribute.
250 if (xml::Element* manifestEl = xml::nodeCast<xml::Element>(node)) {
251 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
252 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
253 return AppInfo{ packageAttr->value };
254 }
255 }
256 }
257 return {};
258 }
259
260 bool verifyNoExternalPackages(ResourceTable* table) {
261 bool error = false;
262 for (const auto& package : table->packages) {
263 if (mContext.getCompilationPackage() != package->name ||
264 !package->id || package->id.value() != mContext.getPackageId()) {
265 // We have a package that is not related to the one we're building!
266 for (const auto& type : package->types) {
267 for (const auto& entry : type->entries) {
268 for (const auto& configValue : entry->values) {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700269 mContext.getDiagnostics()->error(
270 DiagMessage(configValue.value->getSource())
271 << "defined resource '"
272 << ResourceNameRef(package->name,
273 type->type,
274 entry->name)
275 << "' for external package '"
276 << package->name << "'");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700277 error = true;
278 }
279 }
280 }
281 }
282 }
283 return !error;
284 }
285
286 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
287 if (mOptions.outputToDirectory) {
288 return createDirectoryArchiveWriter(mOptions.outputPath);
289 } else {
290 return createZipFileArchiveWriter(mOptions.outputPath);
291 }
292 }
293
294 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
295 BigBuffer buffer(1024);
296 TableFlattenerOptions options = {};
297 options.useExtendedChunks = mOptions.staticLib;
298 TableFlattener flattener(&buffer, options);
299 if (!flattener.consume(&mContext, table)) {
300 return false;
301 }
302
303 ArchiveEntry* entry = writer->writeEntry("resources.arsc", ArchiveEntry::kAlign, buffer);
304 if (!entry) {
305 mContext.getDiagnostics()->error(
306 DiagMessage() << "failed to write resources.arsc to archive");
307 return false;
308 }
309 return true;
310 }
311
312 bool flattenXml(XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
313 IArchiveWriter* writer) {
314 BigBuffer buffer(1024);
315 XmlFlattenerOptions options = {};
316 options.keepRawValues = mOptions.staticLib;
317 options.maxSdkLevel = maxSdkLevel;
318 XmlFlattener flattener(&buffer, options);
319 if (!flattener.consume(&mContext, xmlRes)) {
320 return false;
321 }
322
323 ArchiveEntry* entry = writer->writeEntry(path, ArchiveEntry::kCompress, buffer);
324 if (!entry) {
325 mContext.getDiagnostics()->error(
326 DiagMessage() << "failed to write " << path << " to archive");
327 return false;
328 }
329 return true;
330 }
331
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700332 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
333 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700334 if (!mOptions.generateJavaClassPath) {
335 return true;
336 }
337
338 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700339 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700340 file::mkdirs(outPath);
341 file::appendPath(&outPath, "R.java");
342
343 std::ofstream fout(outPath, std::ofstream::binary);
344 if (!fout) {
345 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
346 return false;
347 }
348
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700349 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700350 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700351 mContext.getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
352 return false;
353 }
354 return true;
355 }
356
357 bool writeProguardFile(const proguard::KeepSet& keepSet) {
358 if (!mOptions.generateProguardRulesPath) {
359 return true;
360 }
361
362 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
363 if (!fout) {
364 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
365 return false;
366 }
367
368 proguard::writeKeepSet(&fout, keepSet);
369 if (!fout) {
370 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
371 return false;
372 }
373 return true;
374 }
375
376 int run(const std::vector<std::string>& inputFiles) {
377 // Load the AndroidManifest.xml
378 std::unique_ptr<XmlResource> manifestXml = loadXml(mOptions.manifestPath);
379 if (!manifestXml) {
380 return 1;
381 }
382
383 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
384 mContext.mCompilationPackage = maybeAppInfo.value().package;
385 } else {
386 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
387 << "no package specified in <manifest> tag");
388 return 1;
389 }
390
391 if (!util::isJavaPackageName(mContext.mCompilationPackage)) {
392 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
393 << "invalid package name '"
394 << mContext.mCompilationPackage
395 << "'");
396 return 1;
397 }
398
399 mContext.mNameMangler = util::make_unique<NameMangler>(
400 NameManglerPolicy{ mContext.mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700401
402 if (mContext.mCompilationPackage == u"android") {
403 mContext.mPackageId = 0x01;
404 } else {
405 mContext.mPackageId = 0x7f;
406 }
407
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700408 mContext.mSymbols = createSymbolTableFromIncludePaths();
409 if (!mContext.mSymbols) {
410 return 1;
411 }
412
413 if (mOptions.verbose) {
414 mContext.getDiagnostics()->note(
415 DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
416 << "with package ID " << std::hex << (int) mContext.mPackageId);
417 }
418
419 ResourceTable mergedTable;
420 TableMerger tableMerger(&mContext, &mergedTable);
421
422 struct FilesToProcess {
423 Source source;
424 ResourceFile file;
425 };
426
427 bool error = false;
428 std::queue<FilesToProcess> filesToProcess;
429 for (const std::string& input : inputFiles) {
430 if (util::stringEndsWith<char>(input, ".apk")) {
431 // TODO(adamlesinski): Load resources from a static library APK
432 // Merge the table into TableMerger.
433
434 } else if (util::stringEndsWith<char>(input, ".arsc.flat")) {
435 if (mOptions.verbose) {
436 mContext.getDiagnostics()->note(DiagMessage() << "linking " << input);
437 }
438
439 std::unique_ptr<ResourceTable> table = loadTable(input);
440 if (!table) {
441 return 1;
442 }
443
444 if (!tableMerger.merge(Source(input), table.get())) {
445 return 1;
446 }
447
448 } else {
449 // Extract the exported IDs here so we can build the resource table.
450 if (Maybe<ResourceFile> maybeF = loadFileExportHeader(input)) {
451 ResourceFile& f = maybeF.value();
452
453 if (f.name.package.empty()) {
454 f.name.package = mContext.getCompilationPackage().toString();
455 }
456
457 Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(f.name);
458
459 // Add this file to the table.
460 if (!mergedTable.addFileReference(mangledName ? mangledName.value() : f.name,
461 f.config, f.source,
462 util::utf8ToUtf16(buildResourceFileName(f)),
463 mContext.getDiagnostics())) {
464 error = true;
465 }
466
467 // Add the exports of this file to the table.
468 for (SourcedResourceName& exportedSymbol : f.exportedSymbols) {
469 if (exportedSymbol.name.package.empty()) {
470 exportedSymbol.name.package = mContext.getCompilationPackage()
471 .toString();
472 }
473
474 Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
475 exportedSymbol.name);
Adam Lesinskie78fd612015-10-22 12:48:43 -0700476 std::unique_ptr<Id> id = util::make_unique<Id>();
477 id->setSource(f.source.withLine(exportedSymbol.line));
478 if (!mergedTable.addResourceAllowMangled(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700479 mangledName ? mangledName.value() : exportedSymbol.name,
Adam Lesinskie78fd612015-10-22 12:48:43 -0700480 {}, std::move(id), mContext.getDiagnostics())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700481 error = true;
482 }
483 }
484
485 filesToProcess.push(FilesToProcess{ Source(input), std::move(f) });
486 } else {
487 return 1;
488 }
489 }
490 }
491
492 if (error) {
493 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
494 return 1;
495 }
496
497 if (!verifyNoExternalPackages(&mergedTable)) {
498 return 1;
499 }
500
501 if (!mOptions.staticLib) {
502 PrivateAttributeMover mover;
503 if (!mover.consume(&mContext, &mergedTable)) {
504 mContext.getDiagnostics()->error(
505 DiagMessage() << "failed moving private attributes");
506 return 1;
507 }
508 }
509
510 {
511 IdAssigner idAssigner;
512 if (!idAssigner.consume(&mContext, &mergedTable)) {
513 mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
514 return 1;
515 }
516 }
517
518 mContext.mNameMangler = util::make_unique<NameMangler>(
519 NameManglerPolicy{ mContext.mCompilationPackage, tableMerger.getMergedPackages() });
520 mContext.mSymbols = JoinedSymbolTableBuilder()
521 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mergedTable))
522 .addSymbolTable(std::move(mContext.mSymbols))
523 .build();
524
525 {
526 ReferenceLinker linker;
527 if (!linker.consume(&mContext, &mergedTable)) {
528 mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
529 return 1;
530 }
531 }
532
533 proguard::KeepSet proguardKeepSet;
534
535 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
536 if (!archiveWriter) {
537 mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive");
538 return 1;
539 }
540
541 {
542 XmlReferenceLinker manifestLinker;
543 if (manifestLinker.consume(&mContext, manifestXml.get())) {
544
545 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
546 manifestXml.get(),
547 &proguardKeepSet)) {
548 error = true;
549 }
550
551 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
552 archiveWriter.get())) {
553 error = true;
554 }
555 } else {
556 error = true;
557 }
558 }
559
560 for (; !filesToProcess.empty(); filesToProcess.pop()) {
561 FilesToProcess& f = filesToProcess.front();
562 if (f.file.name.type != ResourceType::kRaw &&
563 util::stringEndsWith<char>(f.source.path, ".xml.flat")) {
564 if (mOptions.verbose) {
565 mContext.getDiagnostics()->note(DiagMessage() << "linking " << f.source.path);
566 }
567
568 std::unique_ptr<XmlResource> xmlRes = loadBinaryXmlSkipFileExport(f.source.path);
569 if (!xmlRes) {
570 return 1;
571 }
572
573 xmlRes->file = std::move(f.file);
574
575 XmlReferenceLinker xmlLinker;
576 if (xmlLinker.consume(&mContext, xmlRes.get())) {
577 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
578 &proguardKeepSet)) {
579 error = true;
580 }
581
582 Maybe<size_t> maxSdkLevel;
583 if (!mOptions.noAutoVersion) {
584 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
585 }
586
587 if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file), maxSdkLevel,
588 archiveWriter.get())) {
589 error = true;
590 }
591
592 if (!mOptions.noAutoVersion) {
593 Maybe<ResourceTable::SearchResult> result = mergedTable.findResource(
594 xmlRes->file.name);
595 for (int sdkLevel : xmlLinker.getSdkLevels()) {
596 if (sdkLevel > xmlRes->file.config.sdkVersion &&
597 shouldGenerateVersionedResource(result.value().entry,
598 xmlRes->file.config,
599 sdkLevel)) {
600 xmlRes->file.config.sdkVersion = sdkLevel;
601 if (!mergedTable.addFileReference(xmlRes->file.name,
602 xmlRes->file.config,
603 xmlRes->file.source,
604 util::utf8ToUtf16(
605 buildResourceFileName(xmlRes->file)),
606 mContext.getDiagnostics())) {
607 error = true;
608 continue;
609 }
610
611 if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file),
612 sdkLevel, archiveWriter.get())) {
613 error = true;
614 }
615 }
616 }
617 }
618
619 } else {
620 error = true;
621 }
622 } else {
623 if (mOptions.verbose) {
624 mContext.getDiagnostics()->note(DiagMessage() << "copying " << f.source.path);
625 }
626
627 if (!copyFileToArchive(f.source.path, buildResourceFileName(f.file), 0,
628 archiveWriter.get())) {
629 error = true;
630 }
631 }
632 }
633
634 if (error) {
635 mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources");
636 return 1;
637 }
638
639 if (!mOptions.noAutoVersion) {
640 AutoVersioner versioner;
641 if (!versioner.consume(&mContext, &mergedTable)) {
642 mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
643 return 1;
644 }
645 }
646
647 if (!flattenTable(&mergedTable, archiveWriter.get())) {
648 mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
649 return 1;
650 }
651
652 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700653 JavaClassGeneratorOptions options;
654 if (mOptions.staticLib) {
655 options.useFinal = false;
656 }
657
658 if (mOptions.privateSymbols) {
659 // If we defined a private symbols package, we only emit Public symbols
660 // to the original package, and private and public symbols to the private package.
661
662 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
663 if (!writeJavaFile(&mergedTable, mContext.getCompilationPackage(),
664 mContext.getCompilationPackage(), options)) {
665 return 1;
666 }
667
668 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
669 if (!writeJavaFile(&mergedTable, mContext.getCompilationPackage(),
670 mOptions.privateSymbols.value(), options)) {
671 return 1;
672 }
673
674 } else {
675 // Emit Everything.
676
677 if (!writeJavaFile(&mergedTable, mContext.getCompilationPackage(),
678 mContext.getCompilationPackage(), options)) {
679 return 1;
680 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700681 }
682 }
683
684 if (mOptions.generateProguardRulesPath) {
685 if (!writeProguardFile(proguardKeepSet)) {
686 return 1;
687 }
688 }
689
690 if (mOptions.verbose) {
691 Debug::printTable(&mergedTable);
692 for (; !tableMerger.getFileMergeQueue()->empty();
693 tableMerger.getFileMergeQueue()->pop()) {
694 const FileToMerge& f = tableMerger.getFileMergeQueue()->front();
695 mContext.getDiagnostics()->note(
696 DiagMessage() << f.srcPath << " -> " << f.dstPath << " from (0x"
697 << std::hex << (uintptr_t) f.srcTable << std::dec);
698 }
699 }
700
701 return 0;
702 }
703};
704
705int link(const std::vector<StringPiece>& args) {
706 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700707 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700708 Flags flags = Flags()
709 .requiredFlag("-o", "Output path", &options.outputPath)
710 .requiredFlag("--manifest", "Path to the Android manifest to build",
711 &options.manifestPath)
712 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
713 .optionalFlag("--java", "Directory in which to generate R.java",
714 &options.generateJavaClassPath)
715 .optionalFlag("--proguard", "Output file for generated Proguard rules",
716 &options.generateProguardRulesPath)
717 .optionalSwitch("--no-auto-version",
718 "Disables automatic style and layout SDK versioning",
719 &options.noAutoVersion)
720 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
721 "by -o",
722 &options.outputToDirectory)
723 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700724 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
725 "private symbols. If not specified, public and private symbols will "
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700726 "use the application's package name", &privateSymbolsPackage)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700727 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
728
729 if (!flags.parse("aapt2 link", args, &std::cerr)) {
730 return 1;
731 }
732
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700733 if (privateSymbolsPackage) {
734 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
735 }
736
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700737 LinkCommand cmd = { options };
738 return cmd.run(flags.getArgs());
739}
740
741} // namespace aapt