Link.cpp revision a6fe345be955368a13aea76aefb4db821aad11df
1/* 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 "NameMangler.h" 21#include "compile/IdAssigner.h" 22#include "flatten/Archive.h" 23#include "flatten/TableFlattener.h" 24#include "flatten/XmlFlattener.h" 25#include "io/FileSystem.h" 26#include "io/ZipArchive.h" 27#include "java/JavaClassGenerator.h" 28#include "java/ManifestClassGenerator.h" 29#include "java/ProguardRules.h" 30#include "link/Linkers.h" 31#include "link/ReferenceLinker.h" 32#include "link/ManifestFixer.h" 33#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" 40#include "xml/XmlDom.h" 41 42#include <fstream> 43#include <sys/stat.h> 44#include <vector> 45 46namespace aapt { 47 48struct LinkOptions { 49 std::string outputPath; 50 std::string manifestPath; 51 std::vector<std::string> includePaths; 52 std::vector<std::string> overlayFiles; 53 Maybe<std::string> generateJavaClassPath; 54 std::set<std::string> extraJavaPackages; 55 Maybe<std::string> generateProguardRulesPath; 56 bool noAutoVersion = false; 57 bool staticLib = false; 58 bool verbose = false; 59 bool outputToDirectory = false; 60 bool autoAddOverlay = false; 61 Maybe<std::u16string> privateSymbols; 62 Maybe<std::u16string> minSdkVersionDefault; 63 Maybe<std::u16string> targetSdkVersionDefault; 64}; 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 94class LinkCommand { 95public: 96 LinkCommand(const LinkOptions& options) : 97 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)); 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(DiagMessage(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(path) << "failed to load include path"); 125 return {}; 126 } 127 builder.add(std::move(assetManager)); 128 } 129 return builder.build(); 130 } 131 132 std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) { 133 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); 134 BinaryResourceParser parser(&mContext, table.get(), source, data, len); 135 if (!parser.parse()) { 136 return {}; 137 } 138 return table; 139 } 140 141 /** 142 * Inflates an XML file from the source path. 143 */ 144 static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) { 145 std::ifstream fin(path, std::ifstream::binary); 146 if (!fin) { 147 diag->error(DiagMessage(path) << strerror(errno)); 148 return {}; 149 } 150 151 return xml::inflate(&fin, diag, Source(path)); 152 } 153 154 static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport( 155 const Source& source, 156 const void* data, size_t len, 157 IDiagnostics* diag) { 158 std::string errorStr; 159 ssize_t offset = getWrappedDataOffset(data, len, &errorStr); 160 if (offset < 0) { 161 diag->error(DiagMessage(source) << errorStr); 162 return {}; 163 } 164 165 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate( 166 reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset), 167 len - static_cast<size_t>(offset), 168 diag, 169 source); 170 if (!xmlRes) { 171 return {}; 172 } 173 return xmlRes; 174 } 175 176 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>(); 180 std::string errorStr; 181 ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr); 182 if (offset < 0) { 183 diag->error(DiagMessage(source) << errorStr); 184 return {}; 185 } 186 return resFile; 187 } 188 189 bool copyFileToArchive(io::IFile* file, const std::string& outPath, uint32_t flags, 190 IArchiveWriter* writer) { 191 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 198 std::string errorStr; 199 ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr); 200 if (offset < 0) { 201 mContext.getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr); 202 return false; 203 } 204 205 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 } 212 } 213 214 mContext.getDiagnostics()->error( 215 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath); 216 return false; 217 } 218 219 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) { 220 // Make sure the first element is <manifest> with package attribute. 221 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) { 222 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 231 bool verifyNoExternalPackages() { 232 bool error = false; 233 for (const auto& package : mFinalTable.packages) { 234 if (mContext.getCompilationPackage() != package->name || 235 !package->id || package->id.value() != mContext.getPackageId()) { 236 // We have a package that is not related to the one we're building! 237 for (const auto& type : package->types) { 238 for (const auto& entry : type->entries) { 239 for (const auto& configValue : entry->values) { 240 mContext.getDiagnostics()->error( 241 DiagMessage(configValue.value->getSource()) 242 << "defined resource '" 243 << ResourceNameRef(package->name, 244 type->type, 245 entry->name) 246 << "' for external package '" 247 << package->name << "'"); 248 error = true; 249 } 250 } 251 } 252 } 253 } 254 return !error; 255 } 256 257 std::unique_ptr<IArchiveWriter> makeArchiveWriter() { 258 if (mOptions.outputToDirectory) { 259 return createDirectoryArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath); 260 } else { 261 return createZipFileArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath); 262 } 263 } 264 265 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) { 266 BigBuffer buffer(1024); 267 TableFlattenerOptions options = {}; 268 options.useExtendedChunks = mOptions.staticLib; 269 TableFlattener flattener(&buffer, options); 270 if (!flattener.consume(&mContext, table)) { 271 return false; 272 } 273 274 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) { 275 if (writer->writeEntry(buffer)) { 276 if (writer->finishEntry()) { 277 return true; 278 } 279 } 280 } 281 282 mContext.getDiagnostics()->error( 283 DiagMessage() << "failed to write resources.arsc to archive"); 284 return false; 285 } 286 287 bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel, 288 IArchiveWriter* writer) { 289 BigBuffer buffer(1024); 290 XmlFlattenerOptions options = {}; 291 options.keepRawValues = mOptions.staticLib; 292 options.maxSdkLevel = maxSdkLevel; 293 XmlFlattener flattener(&buffer, options); 294 if (!flattener.consume(&mContext, xmlRes)) { 295 return false; 296 } 297 298 299 if (writer->startEntry(path, ArchiveEntry::kCompress)) { 300 if (writer->writeEntry(buffer)) { 301 if (writer->finishEntry()) { 302 return true; 303 } 304 } 305 } 306 mContext.getDiagnostics()->error( 307 DiagMessage() << "failed to write " << path << " to archive"); 308 return false; 309 } 310 311 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate, 312 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) { 313 if (!mOptions.generateJavaClassPath) { 314 return true; 315 } 316 317 std::string outPath = mOptions.generateJavaClassPath.value(); 318 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage))); 319 file::mkdirs(outPath); 320 file::appendPath(&outPath, "R.java"); 321 322 std::ofstream fout(outPath, std::ofstream::binary); 323 if (!fout) { 324 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno)); 325 return false; 326 } 327 328 JavaClassGenerator generator(table, javaOptions); 329 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) { 330 mContext.getDiagnostics()->error(DiagMessage(outPath) << generator.getError()); 331 return false; 332 } 333 return true; 334 } 335 336 bool writeManifestJavaFile(xml::XmlResource* manifestXml) { 337 if (!mOptions.generateJavaClassPath) { 338 return true; 339 } 340 341 std::string outPath = mOptions.generateJavaClassPath.value(); 342 file::appendPath(&outPath, 343 file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage()))); 344 file::mkdirs(outPath); 345 file::appendPath(&outPath, "Manifest.java"); 346 347 std::ofstream fout(outPath, std::ofstream::binary); 348 if (!fout) { 349 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno)); 350 return false; 351 } 352 353 ManifestClassGenerator generator; 354 if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(), 355 manifestXml, &fout)) { 356 return false; 357 } 358 359 if (!fout) { 360 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno)); 361 return false; 362 } 363 return true; 364 } 365 366 bool writeProguardFile(const proguard::KeepSet& keepSet) { 367 if (!mOptions.generateProguardRulesPath) { 368 return true; 369 } 370 371 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary); 372 if (!fout) { 373 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno)); 374 return false; 375 } 376 377 proguard::writeKeepSet(&fout, keepSet); 378 if (!fout) { 379 mContext.getDiagnostics()->error(DiagMessage() << strerror(errno)); 380 return false; 381 } 382 return true; 383 } 384 385 bool mergeStaticLibrary(const std::string& input) { 386 // TODO(adamlesinski): Load resources from a static library APK and merge the table into 387 // TableMerger. 388 mContext.getDiagnostics()->warn(DiagMessage() 389 << "linking static libraries not supported yet: " 390 << input); 391 return true; 392 } 393 394 bool mergeResourceTable(io::IFile* file, bool override) { 395 if (mOptions.verbose) { 396 mContext.getDiagnostics()->note(DiagMessage() << "linking " << file->getSource()); 397 } 398 399 std::unique_ptr<io::IData> data = file->openAsData(); 400 if (!data) { 401 mContext.getDiagnostics()->error(DiagMessage(file->getSource()) 402 << "failed to open file"); 403 return false; 404 } 405 406 std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(), 407 data->size()); 408 if (!table) { 409 return false; 410 } 411 412 bool result = false; 413 if (override) { 414 result = mTableMerger->mergeOverlay(file->getSource(), table.get()); 415 } else { 416 result = mTableMerger->merge(file->getSource(), table.get()); 417 } 418 return result; 419 } 420 421 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) { 422 if (mOptions.verbose) { 423 mContext.getDiagnostics()->note(DiagMessage() << "adding " << file->getSource()); 424 } 425 426 bool result = false; 427 if (overlay) { 428 result = mTableMerger->mergeFileOverlay(*fileDesc, file); 429 } else { 430 result = mTableMerger->mergeFile(*fileDesc, file); 431 } 432 433 if (!result) { 434 return false; 435 } 436 437 // Add the exports of this file to the table. 438 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) { 439 if (exportedSymbol.name.package.empty()) { 440 exportedSymbol.name.package = mContext.getCompilationPackage().toString(); 441 } 442 443 ResourceNameRef resName = exportedSymbol.name; 444 445 Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName( 446 exportedSymbol.name); 447 if (mangledName) { 448 resName = mangledName.value(); 449 } 450 451 std::unique_ptr<Id> id = util::make_unique<Id>(); 452 id->setSource(fileDesc->source.withLine(exportedSymbol.line)); 453 bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id), 454 mContext.getDiagnostics()); 455 if (!result) { 456 return false; 457 } 458 } 459 return true; 460 } 461 462 /** 463 * Creates an io::IFileCollection from the ZIP archive and processes the files within. 464 */ 465 bool mergeArchive(const std::string& input, bool override) { 466 std::string errorStr; 467 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create( 468 input, &errorStr); 469 if (!collection) { 470 mContext.getDiagnostics()->error(DiagMessage(input) << errorStr); 471 return false; 472 } 473 474 bool error = false; 475 for (auto iter = collection->iterator(); iter->hasNext(); ) { 476 if (!processFile(iter->next(), override)) { 477 error = true; 478 } 479 } 480 481 // Make sure to move the collection into the set of IFileCollections. 482 mCollections.push_back(std::move(collection)); 483 return !error; 484 } 485 486 bool processFile(const std::string& path, bool override) { 487 if (util::stringEndsWith<char>(path, ".flata")) { 488 return mergeArchive(path, override); 489 } 490 491 io::IFile* file = mFileCollection->insertFile(path); 492 return processFile(file, override); 493 } 494 495 bool processFile(io::IFile* file, bool override) { 496 const Source& src = file->getSource(); 497 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) { 498 return mergeResourceTable(file, override); 499 } else { 500 // Try opening the file and looking for an Export header. 501 std::unique_ptr<io::IData> data = file->openAsData(); 502 if (!data) { 503 mContext.getDiagnostics()->error(DiagMessage(src) << "failed to open"); 504 return false; 505 } 506 507 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader( 508 src, data->data(), data->size(), mContext.getDiagnostics()); 509 if (resourceFile) { 510 return mergeCompiledFile(file, std::move(resourceFile), override); 511 } 512 } 513 return false; 514 } 515 516 int run(const std::vector<std::string>& inputFiles) { 517 // Load the AndroidManifest.xml 518 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath, 519 mContext.getDiagnostics()); 520 if (!manifestXml) { 521 return 1; 522 } 523 524 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) { 525 mContext.mCompilationPackage = maybeAppInfo.value().package; 526 } else { 527 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath) 528 << "no package specified in <manifest> tag"); 529 return 1; 530 } 531 532 if (!util::isJavaPackageName(mContext.mCompilationPackage)) { 533 mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath) 534 << "invalid package name '" 535 << mContext.mCompilationPackage 536 << "'"); 537 return 1; 538 } 539 540 mContext.mNameMangler = util::make_unique<NameMangler>( 541 NameManglerPolicy{ mContext.mCompilationPackage }); 542 543 if (mContext.mCompilationPackage == u"android") { 544 mContext.mPackageId = 0x01; 545 } else { 546 mContext.mPackageId = 0x7f; 547 } 548 549 mContext.mSymbols = createSymbolTableFromIncludePaths(); 550 if (!mContext.mSymbols) { 551 return 1; 552 } 553 554 TableMergerOptions tableMergerOptions; 555 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay; 556 mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable, tableMergerOptions); 557 558 if (mOptions.verbose) { 559 mContext.getDiagnostics()->note( 560 DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' " 561 << "with package ID " << std::hex << (int) mContext.mPackageId); 562 } 563 564 565 for (const std::string& input : inputFiles) { 566 if (!processFile(input, false)) { 567 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input"); 568 return 1; 569 } 570 } 571 572 for (const std::string& input : mOptions.overlayFiles) { 573 if (!processFile(input, true)) { 574 mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays"); 575 return 1; 576 } 577 } 578 579 if (!verifyNoExternalPackages()) { 580 return 1; 581 } 582 583 if (!mOptions.staticLib) { 584 PrivateAttributeMover mover; 585 if (!mover.consume(&mContext, &mFinalTable)) { 586 mContext.getDiagnostics()->error( 587 DiagMessage() << "failed moving private attributes"); 588 return 1; 589 } 590 } 591 592 { 593 IdAssigner idAssigner; 594 if (!idAssigner.consume(&mContext, &mFinalTable)) { 595 mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs"); 596 return 1; 597 } 598 } 599 600 mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{ 601 mContext.mCompilationPackage, mTableMerger->getMergedPackages() }); 602 mContext.mSymbols = JoinedSymbolTableBuilder() 603 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable)) 604 .addSymbolTable(std::move(mContext.mSymbols)) 605 .build(); 606 607 { 608 ReferenceLinker linker; 609 if (!linker.consume(&mContext, &mFinalTable)) { 610 mContext.getDiagnostics()->error(DiagMessage() << "failed linking references"); 611 return 1; 612 } 613 } 614 615 proguard::KeepSet proguardKeepSet; 616 617 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(); 618 if (!archiveWriter) { 619 mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive"); 620 return 1; 621 } 622 623 bool error = false; 624 { 625 ManifestFixerOptions manifestFixerOptions; 626 manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault; 627 manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault; 628 ManifestFixer manifestFixer(manifestFixerOptions); 629 if (!manifestFixer.consume(&mContext, manifestXml.get())) { 630 error = true; 631 } 632 633 // AndroidManifest.xml has no resource name, but the CallSite is built from the name 634 // (aka, which package the AndroidManifest.xml is coming from). 635 // So we give it a package name so it can see local resources. 636 manifestXml->file.name.package = mContext.getCompilationPackage().toString(); 637 638 XmlReferenceLinker manifestLinker; 639 if (manifestLinker.consume(&mContext, manifestXml.get())) { 640 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath), 641 manifestXml.get(), 642 &proguardKeepSet)) { 643 error = true; 644 } 645 646 if (mOptions.generateJavaClassPath) { 647 if (!writeManifestJavaFile(manifestXml.get())) { 648 error = true; 649 } 650 } 651 652 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {}, 653 archiveWriter.get())) { 654 error = true; 655 } 656 } else { 657 error = true; 658 } 659 } 660 661 if (error) { 662 mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest"); 663 return 1; 664 } 665 666 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) { 667 const ResourceKeyRef& key = mergeEntry.first; 668 const FileToMerge& fileToMerge = mergeEntry.second; 669 670 const StringPiece path = fileToMerge.file->getSource().path; 671 672 if (key.name.type != ResourceType::kRaw && 673 (util::stringEndsWith<char>(path, ".xml.flat") || 674 util::stringEndsWith<char>(path, ".xml"))) { 675 if (mOptions.verbose) { 676 mContext.getDiagnostics()->note(DiagMessage() << "linking " << path); 677 } 678 679 io::IFile* file = fileToMerge.file; 680 std::unique_ptr<io::IData> data = file->openAsData(); 681 if (!data) { 682 mContext.getDiagnostics()->error(DiagMessage(file->getSource()) 683 << "failed to open file"); 684 return 1; 685 } 686 687 std::unique_ptr<xml::XmlResource> xmlRes; 688 if (util::stringEndsWith<char>(path, ".flat")) { 689 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(), 690 data->data(), data->size(), 691 mContext.getDiagnostics()); 692 } else { 693 xmlRes = xml::inflate(data->data(), data->size(), mContext.getDiagnostics(), 694 file->getSource()); 695 } 696 697 if (!xmlRes) { 698 return 1; 699 } 700 701 // Create the file description header. 702 xmlRes->file = ResourceFile{ 703 key.name.toResourceName(), 704 key.config, 705 fileToMerge.originalSource, 706 }; 707 708 XmlReferenceLinker xmlLinker; 709 if (xmlLinker.consume(&mContext, xmlRes.get())) { 710 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(), 711 &proguardKeepSet)) { 712 error = true; 713 } 714 715 Maybe<size_t> maxSdkLevel; 716 if (!mOptions.noAutoVersion) { 717 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u); 718 } 719 720 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel, 721 archiveWriter.get())) { 722 error = true; 723 } 724 725 if (!mOptions.noAutoVersion) { 726 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource( 727 xmlRes->file.name); 728 for (int sdkLevel : xmlLinker.getSdkLevels()) { 729 if (sdkLevel > xmlRes->file.config.sdkVersion && 730 shouldGenerateVersionedResource(result.value().entry, 731 xmlRes->file.config, 732 sdkLevel)) { 733 xmlRes->file.config.sdkVersion = sdkLevel; 734 735 std::string genResourcePath = ResourceUtils::buildResourceFileName( 736 xmlRes->file, mContext.getNameMangler()); 737 738 bool added = mFinalTable.addFileReference( 739 xmlRes->file.name, 740 xmlRes->file.config, 741 xmlRes->file.source, 742 util::utf8ToUtf16(genResourcePath), 743 mContext.getDiagnostics()); 744 if (!added) { 745 error = true; 746 continue; 747 } 748 749 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel, 750 archiveWriter.get())) { 751 error = true; 752 } 753 } 754 } 755 } 756 757 } else { 758 error = true; 759 } 760 } else { 761 if (mOptions.verbose) { 762 mContext.getDiagnostics()->note(DiagMessage() << "copying " << path); 763 } 764 765 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0, 766 archiveWriter.get())) { 767 error = true; 768 } 769 } 770 } 771 772 if (error) { 773 mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources"); 774 return 1; 775 } 776 777 if (!mOptions.noAutoVersion) { 778 AutoVersioner versioner; 779 if (!versioner.consume(&mContext, &mFinalTable)) { 780 mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles"); 781 return 1; 782 } 783 } 784 785 if (!flattenTable(&mFinalTable, archiveWriter.get())) { 786 mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc"); 787 return 1; 788 } 789 790 if (mOptions.generateJavaClassPath) { 791 JavaClassGeneratorOptions options; 792 if (mOptions.staticLib) { 793 options.useFinal = false; 794 } 795 796 StringPiece16 actualPackage = mContext.getCompilationPackage(); 797 StringPiece16 outputPackage = mContext.getCompilationPackage(); 798 799 if (mOptions.privateSymbols) { 800 // If we defined a private symbols package, we only emit Public symbols 801 // to the original package, and private and public symbols to the private package. 802 803 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic; 804 if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(), 805 mContext.getCompilationPackage(), options)) { 806 return 1; 807 } 808 809 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate; 810 outputPackage = mOptions.privateSymbols.value(); 811 } 812 813 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) { 814 return 1; 815 } 816 817 for (const std::string& extraPackage : mOptions.extraJavaPackages) { 818 if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage), 819 options)) { 820 return 1; 821 } 822 } 823 } 824 825 if (mOptions.generateProguardRulesPath) { 826 if (!writeProguardFile(proguardKeepSet)) { 827 return 1; 828 } 829 } 830 831 if (mOptions.verbose) { 832 Debug::printTable(&mFinalTable); 833 } 834 return 0; 835 } 836 837private: 838 LinkOptions mOptions; 839 LinkContext mContext; 840 ResourceTable mFinalTable; 841 842 ResourceTable mLocalFileTable; 843 std::unique_ptr<TableMerger> mTableMerger; 844 845 // A pointer to the FileCollection representing the filesystem (not archives). 846 io::FileCollection* mFileCollection; 847 848 // A vector of IFileCollections. This is mainly here to keep ownership of the collections. 849 std::vector<std::unique_ptr<io::IFileCollection>> mCollections; 850}; 851 852int link(const std::vector<StringPiece>& args) { 853 LinkOptions options; 854 Maybe<std::string> privateSymbolsPackage; 855 Maybe<std::string> minSdkVersion, targetSdkVersion; 856 std::vector<std::string> extraJavaPackages; 857 Flags flags = Flags() 858 .requiredFlag("-o", "Output path", &options.outputPath) 859 .requiredFlag("--manifest", "Path to the Android manifest to build", 860 &options.manifestPath) 861 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths) 862 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. " 863 "The last conflicting resource given takes precedence.", 864 &options.overlayFiles) 865 .optionalFlag("--java", "Directory in which to generate R.java", 866 &options.generateJavaClassPath) 867 .optionalFlag("--proguard", "Output file for generated Proguard rules", 868 &options.generateProguardRulesPath) 869 .optionalSwitch("--no-auto-version", 870 "Disables automatic style and layout SDK versioning", 871 &options.noAutoVersion) 872 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified " 873 "by -o", 874 &options.outputToDirectory) 875 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for " 876 "AndroidManifest.xml", &minSdkVersion) 877 .optionalFlag("--target-sdk-version", "Default target SDK version to use for " 878 "AndroidManifest.xml", &targetSdkVersion) 879 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib) 880 .optionalFlag("--private-symbols", "Package name to use when generating R.java for " 881 "private symbols.\n" 882 "If not specified, public and private symbols will use the application's " 883 "package name", &privateSymbolsPackage) 884 .optionalFlagList("--extra-packages", "Generate the same R.java but with different " 885 "package names", &extraJavaPackages) 886 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in " 887 "overlays without <add-resource> tags", &options.autoAddOverlay) 888 .optionalSwitch("-v", "Enables verbose logging", &options.verbose); 889 890 if (!flags.parse("aapt2 link", args, &std::cerr)) { 891 return 1; 892 } 893 894 if (privateSymbolsPackage) { 895 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value()); 896 } 897 898 if (minSdkVersion) { 899 options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value()); 900 } 901 902 if (targetSdkVersion) { 903 options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value()); 904 } 905 906 // Populate the set of extra packages for which to generate R.java. 907 for (std::string& extraPackage : extraJavaPackages) { 908 // A given package can actually be a colon separated list of packages. 909 for (StringPiece package : util::split(extraPackage, ':')) { 910 options.extraJavaPackages.insert(package.toString()); 911 } 912 } 913 914 LinkCommand cmd(options); 915 return cmd.run(flags.getArgs()); 916} 917 918} // namespace aapt 919