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