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