blob: 8e321798787a3de35b00d095de4f0c87b8db2232 [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 Lesinski59e04c62016-02-04 15:59:23 -080022#include "ResourceUtils.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023#include "compile/IdAssigner.h"
Adam Lesinski6a008172016-02-02 17:02:58 -080024#include "filter/ConfigFilter.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070025#include "flatten/Archive.h"
26#include "flatten/TableFlattener.h"
27#include "flatten/XmlFlattener.h"
Adam Lesinskia40e9722015-11-24 19:11:46 -080028#include "io/FileSystem.h"
29#include "io/ZipArchive.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070030#include "java/JavaClassGenerator.h"
31#include "java/ManifestClassGenerator.h"
32#include "java/ProguardRules.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033#include "link/Linkers.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080034#include "link/ReferenceLinker.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080035#include "link/ManifestFixer.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070036#include "link/TableMerger.h"
37#include "process/IResourceTableConsumer.h"
38#include "process/SymbolTable.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080039#include "proto/ProtoSerialize.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070040#include "unflatten/BinaryResourceParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070041#include "util/Files.h"
42#include "util/StringPiece.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080043#include "xml/XmlDom.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070044
Adam Lesinski59e04c62016-02-04 15:59:23 -080045#include <google/protobuf/io/coded_stream.h>
46
Adam Lesinski1ab598f2015-08-14 14:26:04 -070047#include <fstream>
48#include <sys/stat.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070049#include <vector>
50
51namespace aapt {
52
53struct LinkOptions {
54 std::string outputPath;
55 std::string manifestPath;
56 std::vector<std::string> includePaths;
Adam Lesinskifb48d292015-11-07 15:52:13 -080057 std::vector<std::string> overlayFiles;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070058 Maybe<std::string> generateJavaClassPath;
Adam Lesinski52364f72016-01-11 13:10:24 -080059 Maybe<std::u16string> customJavaPackage;
60 std::set<std::u16string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070061 Maybe<std::string> generateProguardRulesPath;
62 bool noAutoVersion = false;
63 bool staticLib = false;
Adam Lesinskief9c5012016-01-22 14:09:53 -080064 bool generateNonFinalIds = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070065 bool verbose = false;
66 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080067 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080068 bool doNotCompressAnything = false;
69 std::vector<std::string> extensionsToNotCompress;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070070 Maybe<std::u16string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080071 ManifestFixerOptions manifestFixerOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -080072 IConfigFilter* configFilter = nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070073};
74
75struct LinkContext : public IAaptContext {
76 StdErrDiagnostics mDiagnostics;
77 std::unique_ptr<NameMangler> mNameMangler;
78 std::u16string mCompilationPackage;
79 uint8_t mPackageId;
80 std::unique_ptr<ISymbolTable> mSymbols;
81
82 IDiagnostics* getDiagnostics() override {
83 return &mDiagnostics;
84 }
85
86 NameMangler* getNameMangler() override {
87 return mNameMangler.get();
88 }
89
90 StringPiece16 getCompilationPackage() override {
91 return mCompilationPackage;
92 }
93
94 uint8_t getPackageId() override {
95 return mPackageId;
96 }
97
98 ISymbolTable* getExternalSymbols() override {
99 return mSymbols.get();
100 }
101};
102
Adam Lesinskifb48d292015-11-07 15:52:13 -0800103class LinkCommand {
104public:
Adam Lesinski6a008172016-02-02 17:02:58 -0800105 LinkCommand(LinkContext* context, const LinkOptions& options) :
106 mOptions(options), mContext(context), mFinalTable(), mFileCollection(nullptr) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800107 std::unique_ptr<io::FileCollection> fileCollection =
108 util::make_unique<io::FileCollection>();
109
110 // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
111 mFileCollection = fileCollection.get();
112
113 // Move it to the collection.
114 mCollections.push_back(std::move(fileCollection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800115 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700116
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700117 /**
118 * Creates a SymbolTable that loads symbols from the various APKs and caches the
119 * results for faster lookup.
120 */
121 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
122 AssetManagerSymbolTableBuilder builder;
123 for (const std::string& path : mOptions.includePaths) {
124 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800125 mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700126 }
127
128 std::unique_ptr<android::AssetManager> assetManager =
129 util::make_unique<android::AssetManager>();
130 int32_t cookie = 0;
131 if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800132 mContext->getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800133 DiagMessage(path) << "failed to load include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700134 return {};
135 }
136 builder.add(std::move(assetManager));
137 }
138 return builder.build();
139 }
140
Adam Lesinskia40e9722015-11-24 19:11:46 -0800141 std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700142 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinski6a008172016-02-02 17:02:58 -0800143 BinaryResourceParser parser(mContext, table.get(), source, data, len);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700144 if (!parser.parse()) {
145 return {};
146 }
147 return table;
148 }
149
Adam Lesinski59e04c62016-02-04 15:59:23 -0800150 std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
151 const void* data, size_t len) {
152 pb::ResourceTable pbTable;
153 if (!pbTable.ParseFromArray(data, len)) {
154 mContext->getDiagnostics()->error(DiagMessage(source) << "invalid compiled table");
155 return {};
156 }
157
158 std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source,
159 mContext->getDiagnostics());
160 if (!table) {
161 return {};
162 }
163 return table;
164 }
165
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700166 /**
167 * Inflates an XML file from the source path.
168 */
Adam Lesinskia40e9722015-11-24 19:11:46 -0800169 static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700170 std::ifstream fin(path, std::ifstream::binary);
171 if (!fin) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800172 diag->error(DiagMessage(path) << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700173 return {};
174 }
175
Adam Lesinskia40e9722015-11-24 19:11:46 -0800176 return xml::inflate(&fin, diag, Source(path));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700177 }
178
Adam Lesinskia40e9722015-11-24 19:11:46 -0800179 static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(
180 const Source& source,
181 const void* data, size_t len,
182 IDiagnostics* diag) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800183 CompiledFileInputStream inputStream(data, len);
184 if (!inputStream.CompiledFile()) {
185 diag->error(DiagMessage(source) << "invalid compiled file header");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700186 return {};
187 }
188
Adam Lesinski59e04c62016-02-04 15:59:23 -0800189 const uint8_t* xmlData = reinterpret_cast<const uint8_t*>(inputStream.data());
190 const size_t xmlDataLen = inputStream.size();
191
192 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(xmlData, xmlDataLen, diag, source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700193 if (!xmlRes) {
194 return {};
195 }
196 return xmlRes;
197 }
198
Adam Lesinskia40e9722015-11-24 19:11:46 -0800199 static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
200 const void* data, size_t len,
201 IDiagnostics* diag) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800202 CompiledFileInputStream inputStream(data, len);
203 const pb::CompiledFile* pbFile = inputStream.CompiledFile();
204 if (!pbFile) {
205 diag->error(DiagMessage(source) << "invalid compiled file header");
206 return {};
207 }
208
209 std::unique_ptr<ResourceFile> resFile = deserializeCompiledFileFromPb(*pbFile, source,
210 diag);
211 if (!resFile) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700212 return {};
213 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800214 return resFile;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700215 }
216
Adam Lesinski52364f72016-01-11 13:10:24 -0800217 uint32_t getCompressionFlags(const StringPiece& str) {
218 if (mOptions.doNotCompressAnything) {
219 return 0;
220 }
221
222 for (const std::string& extension : mOptions.extensionsToNotCompress) {
223 if (util::stringEndsWith<char>(str, extension)) {
224 return 0;
225 }
226 }
227 return ArchiveEntry::kCompress;
228 }
229
230 bool copyFileToArchive(io::IFile* file, const std::string& outPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700231 IArchiveWriter* writer) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800232 std::unique_ptr<io::IData> data = file->openAsData();
233 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800234 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800235 << "failed to open file");
236 return false;
237 }
238
Adam Lesinski59e04c62016-02-04 15:59:23 -0800239 CompiledFileInputStream inputStream(data->data(), data->size());
240 if (!inputStream.CompiledFile()) {
241 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
242 << "invalid compiled file header");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700243 return false;
244 }
245
Adam Lesinski52364f72016-01-11 13:10:24 -0800246 if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800247 if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()),
248 inputStream.size())) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800249 if (writer->finishEntry()) {
250 return true;
251 }
252 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700253 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800254
Adam Lesinski6a008172016-02-02 17:02:58 -0800255 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800256 DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
257 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700258 }
259
Adam Lesinski467f1712015-11-16 17:35:44 -0800260 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700261 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800262 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700263 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
264 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
265 return AppInfo{ packageAttr->value };
266 }
267 }
268 }
269 return {};
270 }
271
Adam Lesinski979ccb22016-01-11 10:42:19 -0800272 /**
273 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
274 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
275 * is an error and false is returned.
276 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800277 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800278 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800279 return mContext->getCompilationPackage() != pkg->name ||
Adam Lesinski979ccb22016-01-11 10:42:19 -0800280 !pkg->id ||
Adam Lesinski6a008172016-02-02 17:02:58 -0800281 pkg->id.value() != mContext->getPackageId();
Adam Lesinski979ccb22016-01-11 10:42:19 -0800282 };
283
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700284 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800285 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800286 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700287 // We have a package that is not related to the one we're building!
288 for (const auto& type : package->types) {
289 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800290 ResourceNameRef resName(package->name, type->type, entry->name);
291
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700292 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800293 // Special case the occurrence of an ID that is being generated for the
294 // 'android' package. This is due to legacy reasons.
295 if (valueCast<Id>(configValue.value.get()) &&
296 package->name == u"android") {
Adam Lesinski6a008172016-02-02 17:02:58 -0800297 mContext->getDiagnostics()->warn(
Adam Lesinski979ccb22016-01-11 10:42:19 -0800298 DiagMessage(configValue.value->getSource())
299 << "generated id '" << resName
300 << "' for external package '" << package->name
301 << "'");
302 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800303 mContext->getDiagnostics()->error(
Adam Lesinski979ccb22016-01-11 10:42:19 -0800304 DiagMessage(configValue.value->getSource())
305 << "defined resource '" << resName
306 << "' for external package '" << package->name
307 << "'");
308 error = true;
309 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700310 }
311 }
312 }
313 }
314 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800315
316 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
317 isExtPackageFunc);
318 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700319 return !error;
320 }
321
322 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
323 if (mOptions.outputToDirectory) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800324 return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700325 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800326 return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700327 }
328 }
329
330 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
331 BigBuffer buffer(1024);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800332 TableFlattener flattener(&buffer);
Adam Lesinski6a008172016-02-02 17:02:58 -0800333 if (!flattener.consume(mContext, table)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700334 return false;
335 }
336
Adam Lesinskia40e9722015-11-24 19:11:46 -0800337 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
338 if (writer->writeEntry(buffer)) {
339 if (writer->finishEntry()) {
340 return true;
341 }
342 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700343 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800344
Adam Lesinski6a008172016-02-02 17:02:58 -0800345 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800346 DiagMessage() << "failed to write resources.arsc to archive");
347 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700348 }
349
Adam Lesinski467f1712015-11-16 17:35:44 -0800350 bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700351 IArchiveWriter* writer) {
352 BigBuffer buffer(1024);
353 XmlFlattenerOptions options = {};
354 options.keepRawValues = mOptions.staticLib;
355 options.maxSdkLevel = maxSdkLevel;
356 XmlFlattener flattener(&buffer, options);
Adam Lesinski6a008172016-02-02 17:02:58 -0800357 if (!flattener.consume(mContext, xmlRes)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700358 return false;
359 }
360
Adam Lesinskia40e9722015-11-24 19:11:46 -0800361 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
362 if (writer->writeEntry(buffer)) {
363 if (writer->finishEntry()) {
364 return true;
365 }
366 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700367 }
Adam Lesinski6a008172016-02-02 17:02:58 -0800368 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800369 DiagMessage() << "failed to write " << path << " to archive");
370 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700371 }
372
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700373 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
374 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700375 if (!mOptions.generateJavaClassPath) {
376 return true;
377 }
378
379 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700380 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700381 file::mkdirs(outPath);
382 file::appendPath(&outPath, "R.java");
383
384 std::ofstream fout(outPath, std::ofstream::binary);
385 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800386 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700387 return false;
388 }
389
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700390 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700391 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800392 mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700393 return false;
394 }
395 return true;
396 }
397
Adam Lesinski467f1712015-11-16 17:35:44 -0800398 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700399 if (!mOptions.generateJavaClassPath) {
400 return true;
401 }
402
403 std::string outPath = mOptions.generateJavaClassPath.value();
404 file::appendPath(&outPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800405 file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700406 file::mkdirs(outPath);
407 file::appendPath(&outPath, "Manifest.java");
408
409 std::ofstream fout(outPath, std::ofstream::binary);
410 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800411 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700412 return false;
413 }
414
415 ManifestClassGenerator generator;
Adam Lesinski6a008172016-02-02 17:02:58 -0800416 if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(),
Adam Lesinskica5638f2015-10-21 14:42:43 -0700417 manifestXml, &fout)) {
418 return false;
419 }
420
421 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800422 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700423 return false;
424 }
425 return true;
426 }
427
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700428 bool writeProguardFile(const proguard::KeepSet& keepSet) {
429 if (!mOptions.generateProguardRulesPath) {
430 return true;
431 }
432
433 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
434 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800435 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700436 return false;
437 }
438
439 proguard::writeKeepSet(&fout, keepSet);
440 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800441 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700442 return false;
443 }
444 return true;
445 }
446
Adam Lesinskifb48d292015-11-07 15:52:13 -0800447 bool mergeStaticLibrary(const std::string& input) {
448 // TODO(adamlesinski): Load resources from a static library APK and merge the table into
449 // TableMerger.
Adam Lesinski6a008172016-02-02 17:02:58 -0800450 mContext->getDiagnostics()->warn(DiagMessage()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800451 << "linking static libraries not supported yet: "
452 << input);
453 return true;
454 }
455
Adam Lesinskia40e9722015-11-24 19:11:46 -0800456 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800457 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800458 mContext->getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800459 }
460
Adam Lesinskia40e9722015-11-24 19:11:46 -0800461 std::unique_ptr<io::IData> data = file->openAsData();
462 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800463 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800464 << "failed to open file");
465 return false;
466 }
467
Adam Lesinski59e04c62016-02-04 15:59:23 -0800468 std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(), data->data(),
469 data->size());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800470 if (!table) {
471 return false;
472 }
473
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800474 bool result = false;
475 if (override) {
476 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
477 } else {
478 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800479 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800480 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800481 }
482
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800483 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
484 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800485 mContext->getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800486 }
487
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800488 bool result = false;
489 if (overlay) {
490 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800491 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800492 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800493 }
494
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800495 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800496 return false;
497 }
498
499 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800500 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800501 if (exportedSymbol.name.package.empty()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800502 exportedSymbol.name.package = mContext->getCompilationPackage().toString();
Adam Lesinskifb48d292015-11-07 15:52:13 -0800503 }
504
505 ResourceNameRef resName = exportedSymbol.name;
506
Adam Lesinski6a008172016-02-02 17:02:58 -0800507 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800508 exportedSymbol.name);
509 if (mangledName) {
510 resName = mangledName.value();
511 }
512
513 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800514 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800515 bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
Adam Lesinski6a008172016-02-02 17:02:58 -0800516 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800517 if (!result) {
518 return false;
519 }
520 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800521 return true;
522 }
523
Adam Lesinskia40e9722015-11-24 19:11:46 -0800524 /**
525 * Creates an io::IFileCollection from the ZIP archive and processes the files within.
526 */
527 bool mergeArchive(const std::string& input, bool override) {
528 std::string errorStr;
529 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
530 input, &errorStr);
531 if (!collection) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800532 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800533 return false;
534 }
535
536 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800537 for (auto iter = collection->iterator(); iter->hasNext(); ) {
538 if (!processFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800539 error = true;
540 }
541 }
542
543 // Make sure to move the collection into the set of IFileCollections.
544 mCollections.push_back(std::move(collection));
545 return !error;
546 }
547
548 bool processFile(const std::string& path, bool override) {
Adam Lesinski656a5772016-01-14 15:17:41 -0800549 if (util::stringEndsWith<char>(path, ".flata") ||
550 util::stringEndsWith<char>(path, ".jar") ||
551 util::stringEndsWith<char>(path, ".jack") ||
552 util::stringEndsWith<char>(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800553 return mergeArchive(path, override);
554 }
555
556 io::IFile* file = mFileCollection->insertFile(path);
557 return processFile(file, override);
558 }
559
560 bool processFile(io::IFile* file, bool override) {
561 const Source& src = file->getSource();
562 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
563 return mergeResourceTable(file, override);
Adam Lesinski52364f72016-01-11 13:10:24 -0800564 } else if (util::stringEndsWith<char>(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -0800565 // Try opening the file and looking for an Export header.
566 std::unique_ptr<io::IData> data = file->openAsData();
567 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800568 mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
Adam Lesinskia40e9722015-11-24 19:11:46 -0800569 return false;
570 }
571
572 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
Adam Lesinski6a008172016-02-02 17:02:58 -0800573 src, data->data(), data->size(), mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800574 if (resourceFile) {
575 return mergeCompiledFile(file, std::move(resourceFile), override);
576 }
Adam Lesinskic446a732016-01-21 11:04:46 -0800577
578 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800579 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800580
Adam Lesinskic446a732016-01-21 11:04:46 -0800581 // Ignore non .flat files. This could be classes.dex or something else that happens
582 // to be in an archive.
583 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800584 }
585
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700586 int run(const std::vector<std::string>& inputFiles) {
587 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800588 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800589 mContext->getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700590 if (!manifestXml) {
591 return 1;
592 }
593
594 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800595 mContext->mCompilationPackage = maybeAppInfo.value().package;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700596 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800597 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700598 << "no package specified in <manifest> tag");
599 return 1;
600 }
601
Adam Lesinski6a008172016-02-02 17:02:58 -0800602 if (!util::isJavaPackageName(mContext->mCompilationPackage)) {
603 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700604 << "invalid package name '"
Adam Lesinski6a008172016-02-02 17:02:58 -0800605 << mContext->mCompilationPackage
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700606 << "'");
607 return 1;
608 }
609
Adam Lesinski6a008172016-02-02 17:02:58 -0800610 mContext->mNameMangler = util::make_unique<NameMangler>(
611 NameManglerPolicy{ mContext->mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700612
Adam Lesinski6a008172016-02-02 17:02:58 -0800613 if (mContext->mCompilationPackage == u"android") {
614 mContext->mPackageId = 0x01;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700615 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800616 mContext->mPackageId = 0x7f;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700617 }
618
Adam Lesinski6a008172016-02-02 17:02:58 -0800619 mContext->mSymbols = createSymbolTableFromIncludePaths();
620 if (!mContext->mSymbols) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700621 return 1;
622 }
623
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800624 TableMergerOptions tableMergerOptions;
625 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
Adam Lesinski6a008172016-02-02 17:02:58 -0800626 tableMergerOptions.filter = mOptions.configFilter;
627 mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800628
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700629 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800630 mContext->getDiagnostics()->note(
631 DiagMessage() << "linking package '" << mContext->mCompilationPackage << "' "
632 << "with package ID " << std::hex << (int) mContext->mPackageId);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700633 }
634
Adam Lesinskifb48d292015-11-07 15:52:13 -0800635
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700636 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800637 if (!processFile(input, false)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800638 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
Adam Lesinski467f1712015-11-16 17:35:44 -0800639 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800640 }
641 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700642
Adam Lesinskifb48d292015-11-07 15:52:13 -0800643 for (const std::string& input : mOptions.overlayFiles) {
644 if (!processFile(input, true)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800645 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
Adam Lesinski467f1712015-11-16 17:35:44 -0800646 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700647 }
648 }
649
Adam Lesinskifb48d292015-11-07 15:52:13 -0800650 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700651 return 1;
652 }
653
654 if (!mOptions.staticLib) {
655 PrivateAttributeMover mover;
Adam Lesinski6a008172016-02-02 17:02:58 -0800656 if (!mover.consume(mContext, &mFinalTable)) {
657 mContext->getDiagnostics()->error(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700658 DiagMessage() << "failed moving private attributes");
659 return 1;
660 }
661 }
662
663 {
664 IdAssigner idAssigner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800665 if (!idAssigner.consume(mContext, &mFinalTable)) {
666 mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700667 return 1;
668 }
669 }
670
Adam Lesinski6a008172016-02-02 17:02:58 -0800671 mContext->mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
672 mContext->mCompilationPackage, mTableMerger->getMergedPackages() });
673 mContext->mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800674 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski6a008172016-02-02 17:02:58 -0800675 .addSymbolTable(std::move(mContext->mSymbols))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700676 .build();
677
678 {
679 ReferenceLinker linker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800680 if (!linker.consume(mContext, &mFinalTable)) {
681 mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700682 return 1;
683 }
684 }
685
686 proguard::KeepSet proguardKeepSet;
687
688 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
689 if (!archiveWriter) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800690 mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700691 return 1;
692 }
693
Adam Lesinski467f1712015-11-16 17:35:44 -0800694 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700695 {
Adam Lesinski52364f72016-01-11 13:10:24 -0800696 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski6a008172016-02-02 17:02:58 -0800697 if (!manifestFixer.consume(mContext, manifestXml.get())) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800698 error = true;
699 }
700
Adam Lesinski467f1712015-11-16 17:35:44 -0800701 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
702 // (aka, which package the AndroidManifest.xml is coming from).
703 // So we give it a package name so it can see local resources.
Adam Lesinski6a008172016-02-02 17:02:58 -0800704 manifestXml->file.name.package = mContext->getCompilationPackage().toString();
Adam Lesinski467f1712015-11-16 17:35:44 -0800705
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700706 XmlReferenceLinker manifestLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800707 if (manifestLinker.consume(mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700708 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
709 manifestXml.get(),
710 &proguardKeepSet)) {
711 error = true;
712 }
713
Adam Lesinskica5638f2015-10-21 14:42:43 -0700714 if (mOptions.generateJavaClassPath) {
715 if (!writeManifestJavaFile(manifestXml.get())) {
716 error = true;
717 }
718 }
719
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700720 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
721 archiveWriter.get())) {
722 error = true;
723 }
724 } else {
725 error = true;
726 }
727 }
728
Adam Lesinski467f1712015-11-16 17:35:44 -0800729 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800730 mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
Adam Lesinski467f1712015-11-16 17:35:44 -0800731 return 1;
732 }
733
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800734 for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
735 const ResourceKeyRef& key = mergeEntry.first;
736 const FileToMerge& fileToMerge = mergeEntry.second;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800737
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800738 const StringPiece path = fileToMerge.file->getSource().path;
739
740 if (key.name.type != ResourceType::kRaw &&
741 (util::stringEndsWith<char>(path, ".xml.flat") ||
742 util::stringEndsWith<char>(path, ".xml"))) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700743 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800744 mContext->getDiagnostics()->note(DiagMessage() << "linking " << path);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800745 }
746
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800747 io::IFile* file = fileToMerge.file;
748 std::unique_ptr<io::IData> data = file->openAsData();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800749 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800750 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800751 << "failed to open file");
752 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700753 }
754
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800755 std::unique_ptr<xml::XmlResource> xmlRes;
756 if (util::stringEndsWith<char>(path, ".flat")) {
757 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
758 data->data(), data->size(),
Adam Lesinski6a008172016-02-02 17:02:58 -0800759 mContext->getDiagnostics());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800760 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800761 xmlRes = xml::inflate(data->data(), data->size(), mContext->getDiagnostics(),
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800762 file->getSource());
763 }
764
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700765 if (!xmlRes) {
766 return 1;
767 }
768
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800769 // Create the file description header.
770 xmlRes->file = ResourceFile{
771 key.name.toResourceName(),
772 key.config,
773 fileToMerge.originalSource,
774 };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700775
776 XmlReferenceLinker xmlLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800777 if (xmlLinker.consume(mContext, xmlRes.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700778 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
779 &proguardKeepSet)) {
780 error = true;
781 }
782
783 Maybe<size_t> maxSdkLevel;
784 if (!mOptions.noAutoVersion) {
785 maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
786 }
787
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800788 if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700789 archiveWriter.get())) {
790 error = true;
791 }
792
793 if (!mOptions.noAutoVersion) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800794 Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700795 xmlRes->file.name);
796 for (int sdkLevel : xmlLinker.getSdkLevels()) {
797 if (sdkLevel > xmlRes->file.config.sdkVersion &&
798 shouldGenerateVersionedResource(result.value().entry,
799 xmlRes->file.config,
800 sdkLevel)) {
801 xmlRes->file.config.sdkVersion = sdkLevel;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800802
803 std::string genResourcePath = ResourceUtils::buildResourceFileName(
Adam Lesinski6a008172016-02-02 17:02:58 -0800804 xmlRes->file, mContext->getNameMangler());
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800805
Adam Lesinskia40e9722015-11-24 19:11:46 -0800806 bool added = mFinalTable.addFileReference(
807 xmlRes->file.name,
808 xmlRes->file.config,
809 xmlRes->file.source,
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800810 util::utf8ToUtf16(genResourcePath),
Adam Lesinski6a008172016-02-02 17:02:58 -0800811 mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800812 if (!added) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700813 error = true;
814 continue;
815 }
816
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800817 if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
818 archiveWriter.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700819 error = true;
820 }
821 }
822 }
823 }
824
825 } else {
826 error = true;
827 }
828 } else {
829 if (mOptions.verbose) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800830 mContext->getDiagnostics()->note(DiagMessage() << "copying " << path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700831 }
832
Adam Lesinski52364f72016-01-11 13:10:24 -0800833 if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700834 archiveWriter.get())) {
835 error = true;
836 }
837 }
838 }
839
840 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800841 mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700842 return 1;
843 }
844
845 if (!mOptions.noAutoVersion) {
846 AutoVersioner versioner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800847 if (!versioner.consume(mContext, &mFinalTable)) {
848 mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700849 return 1;
850 }
851 }
852
Adam Lesinskifb48d292015-11-07 15:52:13 -0800853 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800854 mContext->getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700855 return 1;
856 }
857
858 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700859 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -0800860 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
861
Adam Lesinskief9c5012016-01-22 14:09:53 -0800862 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700863 options.useFinal = false;
864 }
865
Adam Lesinski6a008172016-02-02 17:02:58 -0800866 const StringPiece16 actualPackage = mContext->getCompilationPackage();
867 StringPiece16 outputPackage = mContext->getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -0800868 if (mOptions.customJavaPackage) {
869 // Override the output java package to the custom one.
870 outputPackage = mOptions.customJavaPackage.value();
871 }
Adam Lesinski83f22552015-11-07 11:51:23 -0800872
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700873 if (mOptions.privateSymbols) {
874 // If we defined a private symbols package, we only emit Public symbols
875 // to the original package, and private and public symbols to the private package.
876
877 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinski6a008172016-02-02 17:02:58 -0800878 if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -0800879 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700880 return 1;
881 }
882
883 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -0800884 outputPackage = mOptions.privateSymbols.value();
885 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700886
Adam Lesinskifb48d292015-11-07 15:52:13 -0800887 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -0800888 return 1;
889 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700890
Adam Lesinski52364f72016-01-11 13:10:24 -0800891 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
892 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700893 return 1;
894 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700895 }
896 }
897
898 if (mOptions.generateProguardRulesPath) {
899 if (!writeProguardFile(proguardKeepSet)) {
900 return 1;
901 }
902 }
903
904 if (mOptions.verbose) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800905 Debug::printTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700906 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700907 return 0;
908 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800909
910private:
911 LinkOptions mOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -0800912 LinkContext* mContext;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800913 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800914
915 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800916 std::unique_ptr<TableMerger> mTableMerger;
917
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800918 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -0800919 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800920
921 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800922 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700923};
924
925int link(const std::vector<StringPiece>& args) {
926 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700927 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800928 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -0800929 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
930 Maybe<std::string> versionCode, versionName;
931 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800932 std::vector<std::string> extraJavaPackages;
Adam Lesinski6a008172016-02-02 17:02:58 -0800933 Maybe<std::string> configs;
Adam Lesinski8900aa82016-01-25 22:48:15 -0800934 bool legacyXFlag = false;
935 bool requireLocalization = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700936 Flags flags = Flags()
937 .requiredFlag("-o", "Output path", &options.outputPath)
938 .requiredFlag("--manifest", "Path to the Android manifest to build",
939 &options.manifestPath)
940 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -0800941 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -0800942 "The last conflicting resource given takes precedence.",
943 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700944 .optionalFlag("--java", "Directory in which to generate R.java",
945 &options.generateJavaClassPath)
946 .optionalFlag("--proguard", "Output file for generated Proguard rules",
947 &options.generateProguardRulesPath)
948 .optionalSwitch("--no-auto-version",
949 "Disables automatic style and layout SDK versioning",
950 &options.noAutoVersion)
Adam Lesinski8900aa82016-01-25 22:48:15 -0800951 .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
952 &legacyXFlag)
953 .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
954 &requireLocalization)
Adam Lesinski6a008172016-02-02 17:02:58 -0800955 .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
956 "is all configurations", &configs)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700957 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
958 "by -o",
959 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800960 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
961 "AndroidManifest.xml", &minSdkVersion)
962 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
963 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -0800964 .optionalFlag("--version-code", "Version code (integer) to inject into the "
965 "AndroidManifest.xml if none is present", &versionCode)
966 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
967 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700968 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinskief9c5012016-01-22 14:09:53 -0800969 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
970 "This is implied when --static-lib is specified.",
971 &options.generateNonFinalIds)
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700972 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800973 "private symbols.\n"
974 "If not specified, public and private symbols will use the application's "
975 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -0800976 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
977 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -0800978 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -0800979 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800980 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
981 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -0800982 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
983 &renameManifestPackage)
984 .optionalFlag("--rename-instrumentation-target-package",
985 "Changes the name of the target package for instrumentation. Most useful "
986 "when used\nin conjunction with --rename-manifest-package",
987 &renameInstrumentationTargetPackage)
988 .optionalFlagList("-0", "File extensions not to compress",
989 &options.extensionsToNotCompress)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700990 .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
991
992 if (!flags.parse("aapt2 link", args, &std::cerr)) {
993 return 1;
994 }
995
Adam Lesinski6a008172016-02-02 17:02:58 -0800996 LinkContext context;
997
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700998 if (privateSymbolsPackage) {
999 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
1000 }
1001
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001002 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001003 options.manifestFixerOptions.minSdkVersionDefault =
1004 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001005 }
1006
1007 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001008 options.manifestFixerOptions.targetSdkVersionDefault =
1009 util::utf8ToUtf16(targetSdkVersion.value());
1010 }
1011
1012 if (renameManifestPackage) {
1013 options.manifestFixerOptions.renameManifestPackage =
1014 util::utf8ToUtf16(renameManifestPackage.value());
1015 }
1016
1017 if (renameInstrumentationTargetPackage) {
1018 options.manifestFixerOptions.renameInstrumentationTargetPackage =
1019 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
1020 }
1021
1022 if (versionCode) {
1023 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
1024 }
1025
1026 if (versionName) {
1027 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
1028 }
1029
1030 if (customJavaPackage) {
1031 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001032 }
1033
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001034 // Populate the set of extra packages for which to generate R.java.
1035 for (std::string& extraPackage : extraJavaPackages) {
1036 // A given package can actually be a colon separated list of packages.
1037 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001038 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001039 }
1040 }
1041
Adam Lesinski6a008172016-02-02 17:02:58 -08001042 AxisConfigFilter filter;
1043 if (configs) {
1044 for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
1045 ConfigDescription config;
1046 LocaleValue lv;
1047 if (lv.initFromFilterString(configStr)) {
1048 lv.writeTo(&config);
1049 } else if (!ConfigDescription::parse(configStr, &config)) {
1050 context.getDiagnostics()->error(
1051 DiagMessage() << "invalid config '" << configStr << "' for -c option");
1052 return 1;
1053 }
1054
1055 if (config.density != 0) {
1056 context.getDiagnostics()->warn(
1057 DiagMessage() << "ignoring density '" << config << "' for -c option");
1058 } else {
1059 filter.addConfig(config);
1060 }
1061 }
1062
1063 options.configFilter = &filter;
1064 }
1065
1066 LinkCommand cmd(&context, options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001067 return cmd.run(flags.getArgs());
1068}
1069
1070} // namespace aapt