blob: fd76e887ab2aa7ecdb062d2c046f197ef654e81c [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 Lesinski6a008172016-02-02 17:02:58 -080020#include "Locale.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "NameMangler.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070022#include "compile/IdAssigner.h"
Adam Lesinski6a008172016-02-02 17:02:58 -080023#include "filter/ConfigFilter.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024#include "flatten/Archive.h"
25#include "flatten/TableFlattener.h"
26#include "flatten/XmlFlattener.h"
Adam Lesinskia40e9722015-11-24 19:11:46 -080027#include "io/FileSystem.h"
28#include "io/ZipArchive.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070029#include "java/JavaClassGenerator.h"
30#include "java/ManifestClassGenerator.h"
31#include "java/ProguardRules.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070032#include "link/Linkers.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080033#include "link/ReferenceLinker.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080034#include "link/ManifestFixer.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070035#include "link/TableMerger.h"
36#include "process/IResourceTableConsumer.h"
37#include "process/SymbolTable.h"
38#include "unflatten/BinaryResourceParser.h"
39#include "unflatten/FileExportHeaderReader.h"
40#include "util/Files.h"
41#include "util/StringPiece.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080042#include "xml/XmlDom.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043
44#include <fstream>
45#include <sys/stat.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046#include <vector>
47
48namespace aapt {
49
50struct LinkOptions {
51 std::string outputPath;
52 std::string manifestPath;
53 std::vector<std::string> includePaths;
Adam Lesinskifb48d292015-11-07 15:52:13 -080054 std::vector<std::string> overlayFiles;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070055 Maybe<std::string> generateJavaClassPath;
Adam Lesinski52364f72016-01-11 13:10:24 -080056 Maybe<std::u16string> customJavaPackage;
57 std::set<std::u16string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070058 Maybe<std::string> generateProguardRulesPath;
59 bool noAutoVersion = false;
60 bool staticLib = false;
Adam Lesinskief9c5012016-01-22 14:09:53 -080061 bool generateNonFinalIds = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 bool verbose = false;
63 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080064 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080065 bool doNotCompressAnything = false;
66 std::vector<std::string> extensionsToNotCompress;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070067 Maybe<std::u16string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080068 ManifestFixerOptions manifestFixerOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -080069 IConfigFilter* configFilter = nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070070};
71
72struct LinkContext : public IAaptContext {
73 StdErrDiagnostics mDiagnostics;
74 std::unique_ptr<NameMangler> mNameMangler;
75 std::u16string mCompilationPackage;
76 uint8_t mPackageId;
77 std::unique_ptr<ISymbolTable> mSymbols;
78
79 IDiagnostics* getDiagnostics() override {
80 return &mDiagnostics;
81 }
82
83 NameMangler* getNameMangler() override {
84 return mNameMangler.get();
85 }
86
87 StringPiece16 getCompilationPackage() override {
88 return mCompilationPackage;
89 }
90
91 uint8_t getPackageId() override {
92 return mPackageId;
93 }
94
95 ISymbolTable* getExternalSymbols() override {
96 return mSymbols.get();
97 }
98};
99
Adam Lesinskifb48d292015-11-07 15:52:13 -0800100class LinkCommand {
101public:
Adam Lesinski6a008172016-02-02 17:02:58 -0800102 LinkCommand(LinkContext* context, const LinkOptions& options) :
103 mOptions(options), mContext(context), mFinalTable(), mFileCollection(nullptr) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800104 std::unique_ptr<io::FileCollection> fileCollection =
105 util::make_unique<io::FileCollection>();
106
107 // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
108 mFileCollection = fileCollection.get();
109
110 // Move it to the collection.
111 mCollections.push_back(std::move(fileCollection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800112 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700113
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700114 /**
115 * Creates a SymbolTable that loads symbols from the various APKs and caches the
116 * results for faster lookup.
117 */
118 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
119 AssetManagerSymbolTableBuilder builder;
120 for (const std::string& path : mOptions.includePaths) {
121 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800122 mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700123 }
124
125 std::unique_ptr<android::AssetManager> assetManager =
126 util::make_unique<android::AssetManager>();
127 int32_t cookie = 0;
128 if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800129 mContext->getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800130 DiagMessage(path) << "failed to load include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700131 return {};
132 }
133 builder.add(std::move(assetManager));
134 }
135 return builder.build();
136 }
137
Adam Lesinskia40e9722015-11-24 19:11:46 -0800138 std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700139 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinski6a008172016-02-02 17:02:58 -0800140 BinaryResourceParser parser(mContext, table.get(), source, data, len);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700141 if (!parser.parse()) {
142 return {};
143 }
144 return table;
145 }
146
147 /**
148 * Inflates an XML file from the source path.
149 */
Adam Lesinskia40e9722015-11-24 19:11:46 -0800150 static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700151 std::ifstream fin(path, std::ifstream::binary);
152 if (!fin) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800153 diag->error(DiagMessage(path) << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700154 return {};
155 }
156
Adam Lesinskia40e9722015-11-24 19:11:46 -0800157 return xml::inflate(&fin, diag, Source(path));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700158 }
159
Adam Lesinskia40e9722015-11-24 19:11:46 -0800160 static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(
161 const Source& source,
162 const void* data, size_t len,
163 IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700164 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800165 ssize_t offset = getWrappedDataOffset(data, len, &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700166 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800167 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700168 return {};
169 }
170
Adam Lesinski467f1712015-11-16 17:35:44 -0800171 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800172 reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset),
173 len - static_cast<size_t>(offset),
174 diag,
175 source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700176 if (!xmlRes) {
177 return {};
178 }
179 return xmlRes;
180 }
181
Adam Lesinskia40e9722015-11-24 19:11:46 -0800182 static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
183 const void* data, size_t len,
184 IDiagnostics* diag) {
185 std::unique_ptr<ResourceFile> resFile = util::make_unique<ResourceFile>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700186 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800187 ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188 if (offset < 0) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800189 diag->error(DiagMessage(source) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190 return {};
191 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800192 return resFile;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700193 }
194
Adam Lesinski52364f72016-01-11 13:10:24 -0800195 uint32_t getCompressionFlags(const StringPiece& str) {
196 if (mOptions.doNotCompressAnything) {
197 return 0;
198 }
199
200 for (const std::string& extension : mOptions.extensionsToNotCompress) {
201 if (util::stringEndsWith<char>(str, extension)) {
202 return 0;
203 }
204 }
205 return ArchiveEntry::kCompress;
206 }
207
208 bool copyFileToArchive(io::IFile* file, const std::string& outPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700209 IArchiveWriter* writer) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800210 std::unique_ptr<io::IData> data = file->openAsData();
211 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800212 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800213 << "failed to open file");
214 return false;
215 }
216
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700217 std::string errorStr;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800218 ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700219 if (offset < 0) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800220 mContext->getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700221 return false;
222 }
223
Adam Lesinski52364f72016-01-11 13:10:24 -0800224 if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800225 if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
226 data->size() - static_cast<size_t>(offset))) {
227 if (writer->finishEntry()) {
228 return true;
229 }
230 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700231 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800232
Adam Lesinski6a008172016-02-02 17:02:58 -0800233 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800234 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
235 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700236 }
237
Adam Lesinski467f1712015-11-16 17:35:44 -0800238 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700239 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800240 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700241 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
242 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
243 return AppInfo{ packageAttr->value };
244 }
245 }
246 }
247 return {};
248 }
249
Adam Lesinski979ccb22016-01-11 10:42:19 -0800250 /**
251 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
252 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
253 * is an error and false is returned.
254 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800255 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800256 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800257 return mContext->getCompilationPackage() != pkg->name ||
Adam Lesinski979ccb22016-01-11 10:42:19 -0800258 !pkg->id ||
Adam Lesinski6a008172016-02-02 17:02:58 -0800259 pkg->id.value() != mContext->getPackageId();
Adam Lesinski979ccb22016-01-11 10:42:19 -0800260 };
261
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700262 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800263 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800264 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700265 // We have a package that is not related to the one we're building!
266 for (const auto& type : package->types) {
267 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800268 ResourceNameRef resName(package->name, type->type, entry->name);
269
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700270 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800271 // Special case the occurrence of an ID that is being generated for the
272 // 'android' package. This is due to legacy reasons.
273 if (valueCast<Id>(configValue.value.get()) &&
274 package->name == u"android") {
Adam Lesinski6a008172016-02-02 17:02:58 -0800275 mContext->getDiagnostics()->warn(
Adam Lesinski979ccb22016-01-11 10:42:19 -0800276 DiagMessage(configValue.value->getSource())
277 << "generated id '" << resName
278 << "' for external package '" << package->name
279 << "'");
280 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800281 mContext->getDiagnostics()->error(
Adam Lesinski979ccb22016-01-11 10:42:19 -0800282 DiagMessage(configValue.value->getSource())
283 << "defined resource '" << resName
284 << "' for external package '" << package->name
285 << "'");
286 error = true;
287 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700288 }
289 }
290 }
291 }
292 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800293
294 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
295 isExtPackageFunc);
296 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700297 return !error;
298 }
299
300 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
301 if (mOptions.outputToDirectory) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800302 return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700303 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800304 return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700305 }
306 }
307
308 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
309 BigBuffer buffer(1024);
310 TableFlattenerOptions options = {};
311 options.useExtendedChunks = mOptions.staticLib;
312 TableFlattener flattener(&buffer, options);
Adam Lesinski6a008172016-02-02 17:02:58 -0800313 if (!flattener.consume(mContext, table)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700314 return false;
315 }
316
Adam Lesinskia40e9722015-11-24 19:11:46 -0800317 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
318 if (writer->writeEntry(buffer)) {
319 if (writer->finishEntry()) {
320 return true;
321 }
322 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700323 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800324
Adam Lesinski6a008172016-02-02 17:02:58 -0800325 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800326 DiagMessage() << "failed to write resources.arsc to archive");
327 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700328 }
329
Adam Lesinski467f1712015-11-16 17:35:44 -0800330 bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700331 IArchiveWriter* writer) {
332 BigBuffer buffer(1024);
333 XmlFlattenerOptions options = {};
334 options.keepRawValues = mOptions.staticLib;
335 options.maxSdkLevel = maxSdkLevel;
336 XmlFlattener flattener(&buffer, options);
Adam Lesinski6a008172016-02-02 17:02:58 -0800337 if (!flattener.consume(mContext, xmlRes)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700338 return false;
339 }
340
Adam Lesinskia40e9722015-11-24 19:11:46 -0800341 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
342 if (writer->writeEntry(buffer)) {
343 if (writer->finishEntry()) {
344 return true;
345 }
346 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700347 }
Adam Lesinski6a008172016-02-02 17:02:58 -0800348 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800349 DiagMessage() << "failed to write " << path << " to archive");
350 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700351 }
352
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700353 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
354 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700355 if (!mOptions.generateJavaClassPath) {
356 return true;
357 }
358
359 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700360 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700361 file::mkdirs(outPath);
362 file::appendPath(&outPath, "R.java");
363
364 std::ofstream fout(outPath, std::ofstream::binary);
365 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800366 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700367 return false;
368 }
369
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700370 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700371 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800372 mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700373 return false;
374 }
375 return true;
376 }
377
Adam Lesinski467f1712015-11-16 17:35:44 -0800378 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700379 if (!mOptions.generateJavaClassPath) {
380 return true;
381 }
382
383 std::string outPath = mOptions.generateJavaClassPath.value();
384 file::appendPath(&outPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800385 file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700386 file::mkdirs(outPath);
387 file::appendPath(&outPath, "Manifest.java");
388
389 std::ofstream fout(outPath, std::ofstream::binary);
390 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800391 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700392 return false;
393 }
394
395 ManifestClassGenerator generator;
Adam Lesinski6a008172016-02-02 17:02:58 -0800396 if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(),
Adam Lesinskica5638f2015-10-21 14:42:43 -0700397 manifestXml, &fout)) {
398 return false;
399 }
400
401 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800402 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700403 return false;
404 }
405 return true;
406 }
407
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700408 bool writeProguardFile(const proguard::KeepSet& keepSet) {
409 if (!mOptions.generateProguardRulesPath) {
410 return true;
411 }
412
413 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
414 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800415 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700416 return false;
417 }
418
419 proguard::writeKeepSet(&fout, keepSet);
420 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800421 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700422 return false;
423 }
424 return true;
425 }
426
Adam Lesinskifb48d292015-11-07 15:52:13 -0800427 bool mergeStaticLibrary(const std::string& input) {
428 // TODO(adamlesinski): Load resources from a static library APK and merge the table into
429 // TableMerger.
Adam Lesinski6a008172016-02-02 17:02:58 -0800430 mContext->getDiagnostics()->warn(DiagMessage()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800431 << "linking static libraries not supported yet: "
432 << input);
433 return true;
434 }
435
Adam Lesinskia40e9722015-11-24 19:11:46 -0800436 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800437 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800438 mContext->getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800439 }
440
Adam Lesinskia40e9722015-11-24 19:11:46 -0800441 std::unique_ptr<io::IData> data = file->openAsData();
442 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800443 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800444 << "failed to open file");
445 return false;
446 }
447
448 std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(),
449 data->size());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800450 if (!table) {
451 return false;
452 }
453
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800454 bool result = false;
455 if (override) {
456 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
457 } else {
458 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800459 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800460 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800461 }
462
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800463 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
464 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800465 mContext->getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800466 }
467
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800468 bool result = false;
469 if (overlay) {
470 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800471 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800472 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800473 }
474
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800475 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800476 return false;
477 }
478
479 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800480 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800481 if (exportedSymbol.name.package.empty()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800482 exportedSymbol.name.package = mContext->getCompilationPackage().toString();
Adam Lesinskifb48d292015-11-07 15:52:13 -0800483 }
484
485 ResourceNameRef resName = exportedSymbol.name;
486
Adam Lesinski6a008172016-02-02 17:02:58 -0800487 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800488 exportedSymbol.name);
489 if (mangledName) {
490 resName = mangledName.value();
491 }
492
493 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800494 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800495 bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
Adam Lesinski6a008172016-02-02 17:02:58 -0800496 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800497 if (!result) {
498 return false;
499 }
500 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800501 return true;
502 }
503
Adam Lesinskia40e9722015-11-24 19:11:46 -0800504 /**
505 * Creates an io::IFileCollection from the ZIP archive and processes the files within.
506 */
507 bool mergeArchive(const std::string& input, bool override) {
508 std::string errorStr;
509 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
510 input, &errorStr);
511 if (!collection) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800512 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800513 return false;
514 }
515
516 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800517 for (auto iter = collection->iterator(); iter->hasNext(); ) {
518 if (!processFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800519 error = true;
520 }
521 }
522
523 // Make sure to move the collection into the set of IFileCollections.
524 mCollections.push_back(std::move(collection));
525 return !error;
526 }
527
528 bool processFile(const std::string& path, bool override) {
Adam Lesinski656a5772016-01-14 15:17:41 -0800529 if (util::stringEndsWith<char>(path, ".flata") ||
530 util::stringEndsWith<char>(path, ".jar") ||
531 util::stringEndsWith<char>(path, ".jack") ||
532 util::stringEndsWith<char>(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800533 return mergeArchive(path, override);
534 }
535
536 io::IFile* file = mFileCollection->insertFile(path);
537 return processFile(file, override);
538 }
539
540 bool processFile(io::IFile* file, bool override) {
541 const Source& src = file->getSource();
542 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
543 return mergeResourceTable(file, override);
Adam Lesinski52364f72016-01-11 13:10:24 -0800544 } else if (util::stringEndsWith<char>(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -0800545 // Try opening the file and looking for an Export header.
546 std::unique_ptr<io::IData> data = file->openAsData();
547 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800548 mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
Adam Lesinskia40e9722015-11-24 19:11:46 -0800549 return false;
550 }
551
552 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
Adam Lesinski6a008172016-02-02 17:02:58 -0800553 src, data->data(), data->size(), mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800554 if (resourceFile) {
555 return mergeCompiledFile(file, std::move(resourceFile), override);
556 }
Adam Lesinskic446a732016-01-21 11:04:46 -0800557
558 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800559 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800560
Adam Lesinskic446a732016-01-21 11:04:46 -0800561 // Ignore non .flat files. This could be classes.dex or something else that happens
562 // to be in an archive.
563 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800564 }
565
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700566 int run(const std::vector<std::string>& inputFiles) {
567 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800568 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800569 mContext->getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700570 if (!manifestXml) {
571 return 1;
572 }
573
574 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800575 mContext->mCompilationPackage = maybeAppInfo.value().package;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700576 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800577 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700578 << "no package specified in <manifest> tag");
579 return 1;
580 }
581
Adam Lesinski6a008172016-02-02 17:02:58 -0800582 if (!util::isJavaPackageName(mContext->mCompilationPackage)) {
583 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700584 << "invalid package name '"
Adam Lesinski6a008172016-02-02 17:02:58 -0800585 << mContext->mCompilationPackage
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700586 << "'");
587 return 1;
588 }
589
Adam Lesinski6a008172016-02-02 17:02:58 -0800590 mContext->mNameMangler = util::make_unique<NameMangler>(
591 NameManglerPolicy{ mContext->mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700592
Adam Lesinski6a008172016-02-02 17:02:58 -0800593 if (mContext->mCompilationPackage == u"android") {
594 mContext->mPackageId = 0x01;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700595 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800596 mContext->mPackageId = 0x7f;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700597 }
598
Adam Lesinski6a008172016-02-02 17:02:58 -0800599 mContext->mSymbols = createSymbolTableFromIncludePaths();
600 if (!mContext->mSymbols) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700601 return 1;
602 }
603
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800604 TableMergerOptions tableMergerOptions;
605 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
Adam Lesinski6a008172016-02-02 17:02:58 -0800606 tableMergerOptions.filter = mOptions.configFilter;
607 mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800608
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700609 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800610 mContext->getDiagnostics()->note(
611 DiagMessage() << "linking package '" << mContext->mCompilationPackage << "' "
612 << "with package ID " << std::hex << (int) mContext->mPackageId);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700613 }
614
Adam Lesinskifb48d292015-11-07 15:52:13 -0800615
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700616 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800617 if (!processFile(input, false)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800618 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
Adam Lesinski467f1712015-11-16 17:35:44 -0800619 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800620 }
621 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700622
Adam Lesinskifb48d292015-11-07 15:52:13 -0800623 for (const std::string& input : mOptions.overlayFiles) {
624 if (!processFile(input, true)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800625 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
Adam Lesinski467f1712015-11-16 17:35:44 -0800626 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700627 }
628 }
629
Adam Lesinskifb48d292015-11-07 15:52:13 -0800630 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700631 return 1;
632 }
633
634 if (!mOptions.staticLib) {
635 PrivateAttributeMover mover;
Adam Lesinski6a008172016-02-02 17:02:58 -0800636 if (!mover.consume(mContext, &mFinalTable)) {
637 mContext->getDiagnostics()->error(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700638 DiagMessage() << "failed moving private attributes");
639 return 1;
640 }
641 }
642
643 {
644 IdAssigner idAssigner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800645 if (!idAssigner.consume(mContext, &mFinalTable)) {
646 mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700647 return 1;
648 }
649 }
650
Adam Lesinski6a008172016-02-02 17:02:58 -0800651 mContext->mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
652 mContext->mCompilationPackage, mTableMerger->getMergedPackages() });
653 mContext->mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800654 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski6a008172016-02-02 17:02:58 -0800655 .addSymbolTable(std::move(mContext->mSymbols))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700656 .build();
657
658 {
659 ReferenceLinker linker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800660 if (!linker.consume(mContext, &mFinalTable)) {
661 mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700662 return 1;
663 }
664 }
665
666 proguard::KeepSet proguardKeepSet;
667
668 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
669 if (!archiveWriter) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800670 mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700671 return 1;
672 }
673
Adam Lesinski467f1712015-11-16 17:35:44 -0800674 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700675 {
Adam Lesinski52364f72016-01-11 13:10:24 -0800676 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski6a008172016-02-02 17:02:58 -0800677 if (!manifestFixer.consume(mContext, manifestXml.get())) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800678 error = true;
679 }
680
Adam Lesinski467f1712015-11-16 17:35:44 -0800681 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
682 // (aka, which package the AndroidManifest.xml is coming from).
683 // So we give it a package name so it can see local resources.
Adam Lesinski6a008172016-02-02 17:02:58 -0800684 manifestXml->file.name.package = mContext->getCompilationPackage().toString();
Adam Lesinski467f1712015-11-16 17:35:44 -0800685
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700686 XmlReferenceLinker manifestLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800687 if (manifestLinker.consume(mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700688 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
689 manifestXml.get(),
690 &proguardKeepSet)) {
691 error = true;
692 }
693
Adam Lesinskica5638f2015-10-21 14:42:43 -0700694 if (mOptions.generateJavaClassPath) {
695 if (!writeManifestJavaFile(manifestXml.get())) {
696 error = true;
697 }
698 }
699
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700700 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
701 archiveWriter.get())) {
702 error = true;
703 }
704 } else {
705 error = true;
706 }
707 }
708
Adam Lesinski467f1712015-11-16 17:35:44 -0800709 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800710 mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
Adam Lesinski467f1712015-11-16 17:35:44 -0800711 return 1;
712 }
713
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800714 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
715 const ResourceKeyRef& key = mergeEntry.first;
716 const FileToMerge& fileToMerge = mergeEntry.second;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800717
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800718 const StringPiece path = fileToMerge.file->getSource().path;
719
720 if (key.name.type != ResourceType::kRaw &&
721 (util::stringEndsWith<char>(path, ".xml.flat") ||
722 util::stringEndsWith<char>(path, ".xml"))) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700723 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800724 mContext->getDiagnostics()->note(DiagMessage() << "linking " << path);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800725 }
726
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800727 io::IFile* file = fileToMerge.file;
728 std::unique_ptr<io::IData> data = file->openAsData();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800729 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800730 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800731 << "failed to open file");
732 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700733 }
734
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800735 std::unique_ptr<xml::XmlResource> xmlRes;
736 if (util::stringEndsWith<char>(path, ".flat")) {
737 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
738 data->data(), data->size(),
Adam Lesinski6a008172016-02-02 17:02:58 -0800739 mContext->getDiagnostics());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800740 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800741 xmlRes = xml::inflate(data->data(), data->size(), mContext->getDiagnostics(),
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800742 file->getSource());
743 }
744
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700745 if (!xmlRes) {
746 return 1;
747 }
748
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800749 // Create the file description header.
750 xmlRes->file = ResourceFile{
751 key.name.toResourceName(),
752 key.config,
753 fileToMerge.originalSource,
754 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700755
756 XmlReferenceLinker xmlLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800757 if (xmlLinker.consume(mContext, xmlRes.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700758 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
759 &proguardKeepSet)) {
760 error = true;
761 }
762
763 Maybe<size_t> maxSdkLevel;
764 if (!mOptions.noAutoVersion) {
765 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
766 }
767
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800768 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700769 archiveWriter.get())) {
770 error = true;
771 }
772
773 if (!mOptions.noAutoVersion) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800774 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700775 xmlRes->file.name);
776 for (int sdkLevel : xmlLinker.getSdkLevels()) {
777 if (sdkLevel > xmlRes->file.config.sdkVersion &&
778 shouldGenerateVersionedResource(result.value().entry,
779 xmlRes->file.config,
780 sdkLevel)) {
781 xmlRes->file.config.sdkVersion = sdkLevel;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800782
783 std::string genResourcePath = ResourceUtils::buildResourceFileName(
Adam Lesinski6a008172016-02-02 17:02:58 -0800784 xmlRes->file, mContext->getNameMangler());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800785
Adam Lesinskia40e9722015-11-24 19:11:46 -0800786 bool added = mFinalTable.addFileReference(
787 xmlRes->file.name,
788 xmlRes->file.config,
789 xmlRes->file.source,
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800790 util::utf8ToUtf16(genResourcePath),
Adam Lesinski6a008172016-02-02 17:02:58 -0800791 mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800792 if (!added) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700793 error = true;
794 continue;
795 }
796
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800797 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
798 archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700799 error = true;
800 }
801 }
802 }
803 }
804
805 } else {
806 error = true;
807 }
808 } else {
809 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800810 mContext->getDiagnostics()->note(DiagMessage() << "copying " << path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700811 }
812
Adam Lesinski52364f72016-01-11 13:10:24 -0800813 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700814 archiveWriter.get())) {
815 error = true;
816 }
817 }
818 }
819
820 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800821 mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700822 return 1;
823 }
824
825 if (!mOptions.noAutoVersion) {
826 AutoVersioner versioner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800827 if (!versioner.consume(mContext, &mFinalTable)) {
828 mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700829 return 1;
830 }
831 }
832
Adam Lesinskifb48d292015-11-07 15:52:13 -0800833 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800834 mContext->getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700835 return 1;
836 }
837
838 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700839 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -0800840 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
841
Adam Lesinskief9c5012016-01-22 14:09:53 -0800842 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700843 options.useFinal = false;
844 }
845
Adam Lesinski6a008172016-02-02 17:02:58 -0800846 const StringPiece16 actualPackage = mContext->getCompilationPackage();
847 StringPiece16 outputPackage = mContext->getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -0800848 if (mOptions.customJavaPackage) {
849 // Override the output java package to the custom one.
850 outputPackage = mOptions.customJavaPackage.value();
851 }
Adam Lesinski83f22552015-11-07 11:51:23 -0800852
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700853 if (mOptions.privateSymbols) {
854 // If we defined a private symbols package, we only emit Public symbols
855 // to the original package, and private and public symbols to the private package.
856
857 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinski6a008172016-02-02 17:02:58 -0800858 if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -0800859 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700860 return 1;
861 }
862
863 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -0800864 outputPackage = mOptions.privateSymbols.value();
865 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700866
Adam Lesinskifb48d292015-11-07 15:52:13 -0800867 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800868 return 1;
869 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700870
Adam Lesinski52364f72016-01-11 13:10:24 -0800871 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
872 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700873 return 1;
874 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700875 }
876 }
877
878 if (mOptions.generateProguardRulesPath) {
879 if (!writeProguardFile(proguardKeepSet)) {
880 return 1;
881 }
882 }
883
884 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800885 Debug::printTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700886 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700887 return 0;
888 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800889
890private:
891 LinkOptions mOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -0800892 LinkContext* mContext;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800893 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800894
895 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800896 std::unique_ptr<TableMerger> mTableMerger;
897
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800898 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -0800899 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800900
901 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800902 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700903};
904
905int link(const std::vector<StringPiece>& args) {
906 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700907 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800908 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -0800909 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
910 Maybe<std::string> versionCode, versionName;
911 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800912 std::vector<std::string> extraJavaPackages;
Adam Lesinski6a008172016-02-02 17:02:58 -0800913 Maybe<std::string> configs;
Adam Lesinski8900aa82016-01-25 22:48:15 -0800914 bool legacyXFlag = false;
915 bool requireLocalization = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700916 Flags flags = Flags()
917 .requiredFlag("-o", "Output path", &options.outputPath)
918 .requiredFlag("--manifest", "Path to the Android manifest to build",
919 &options.manifestPath)
920 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -0800921 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -0800922 "The last conflicting resource given takes precedence.",
923 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700924 .optionalFlag("--java", "Directory in which to generate R.java",
925 &options.generateJavaClassPath)
926 .optionalFlag("--proguard", "Output file for generated Proguard rules",
927 &options.generateProguardRulesPath)
928 .optionalSwitch("--no-auto-version",
929 "Disables automatic style and layout SDK versioning",
930 &options.noAutoVersion)
Adam Lesinski8900aa82016-01-25 22:48:15 -0800931 .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
932 &legacyXFlag)
933 .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
934 &requireLocalization)
Adam Lesinski6a008172016-02-02 17:02:58 -0800935 .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
936 "is all configurations", &configs)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700937 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
938 "by -o",
939 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800940 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
941 "AndroidManifest.xml", &minSdkVersion)
942 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
943 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -0800944 .optionalFlag("--version-code", "Version code (integer) to inject into the "
945 "AndroidManifest.xml if none is present", &versionCode)
946 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
947 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700948 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinskief9c5012016-01-22 14:09:53 -0800949 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
950 "This is implied when --static-lib is specified.",
951 &options.generateNonFinalIds)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700952 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800953 "private symbols.\n"
954 "If not specified, public and private symbols will use the application's "
955 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -0800956 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
957 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -0800958 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800959 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800960 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
961 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -0800962 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
963 &renameManifestPackage)
964 .optionalFlag("--rename-instrumentation-target-package",
965 "Changes the name of the target package for instrumentation. Most useful "
966 "when used\nin conjunction with --rename-manifest-package",
967 &renameInstrumentationTargetPackage)
968 .optionalFlagList("-0", "File extensions not to compress",
969 &options.extensionsToNotCompress)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700970 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
971
972 if (!flags.parse("aapt2 link", args, &std::cerr)) {
973 return 1;
974 }
975
Adam Lesinski6a008172016-02-02 17:02:58 -0800976 LinkContext context;
977
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700978 if (privateSymbolsPackage) {
979 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
980 }
981
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800982 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800983 options.manifestFixerOptions.minSdkVersionDefault =
984 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800985 }
986
987 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -0800988 options.manifestFixerOptions.targetSdkVersionDefault =
989 util::utf8ToUtf16(targetSdkVersion.value());
990 }
991
992 if (renameManifestPackage) {
993 options.manifestFixerOptions.renameManifestPackage =
994 util::utf8ToUtf16(renameManifestPackage.value());
995 }
996
997 if (renameInstrumentationTargetPackage) {
998 options.manifestFixerOptions.renameInstrumentationTargetPackage =
999 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
1000 }
1001
1002 if (versionCode) {
1003 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
1004 }
1005
1006 if (versionName) {
1007 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
1008 }
1009
1010 if (customJavaPackage) {
1011 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001012 }
1013
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001014 // Populate the set of extra packages for which to generate R.java.
1015 for (std::string& extraPackage : extraJavaPackages) {
1016 // A given package can actually be a colon separated list of packages.
1017 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001018 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001019 }
1020 }
1021
Adam Lesinski6a008172016-02-02 17:02:58 -08001022 AxisConfigFilter filter;
1023 if (configs) {
1024 for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
1025 ConfigDescription config;
1026 LocaleValue lv;
1027 if (lv.initFromFilterString(configStr)) {
1028 lv.writeTo(&config);
1029 } else if (!ConfigDescription::parse(configStr, &config)) {
1030 context.getDiagnostics()->error(
1031 DiagMessage() << "invalid config '" << configStr << "' for -c option");
1032 return 1;
1033 }
1034
1035 if (config.density != 0) {
1036 context.getDiagnostics()->warn(
1037 DiagMessage() << "ignoring density '" << config << "' for -c option");
1038 } else {
1039 filter.addConfig(config);
1040 }
1041 }
1042
1043 options.configFilter = &filter;
1044 }
1045
1046 LinkCommand cmd(&context, options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001047 return cmd.run(flags.getArgs());
1048}
1049
1050} // namespace aapt