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