blob: d83f6def890cd362215184ac69efe7a26a22844b [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 Lesinskie4bb9eb2016-02-12 22:18:51 -080034#include "link/ProductFilter.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080035#include "link/ReferenceLinker.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080036#include "link/ManifestFixer.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070037#include "link/TableMerger.h"
38#include "process/IResourceTableConsumer.h"
39#include "process/SymbolTable.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080040#include "proto/ProtoSerialize.h"
Adam Lesinski355f2852016-02-13 20:26:45 -080041#include "split/TableSplitter.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070042#include "unflatten/BinaryResourceParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043#include "util/Files.h"
44#include "util/StringPiece.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080045#include "xml/XmlDom.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046
Adam Lesinski59e04c62016-02-04 15:59:23 -080047#include <google/protobuf/io/coded_stream.h>
48
Adam Lesinski1ab598f2015-08-14 14:26:04 -070049#include <fstream>
50#include <sys/stat.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070051#include <vector>
52
53namespace aapt {
54
55struct LinkOptions {
56 std::string outputPath;
57 std::string manifestPath;
58 std::vector<std::string> includePaths;
Adam Lesinskifb48d292015-11-07 15:52:13 -080059 std::vector<std::string> overlayFiles;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070060 Maybe<std::string> generateJavaClassPath;
Adam Lesinski52364f72016-01-11 13:10:24 -080061 Maybe<std::u16string> customJavaPackage;
62 std::set<std::u16string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070063 Maybe<std::string> generateProguardRulesPath;
64 bool noAutoVersion = false;
65 bool staticLib = false;
Adam Lesinskief9c5012016-01-22 14:09:53 -080066 bool generateNonFinalIds = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070067 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080068 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080069 bool doNotCompressAnything = false;
70 std::vector<std::string> extensionsToNotCompress;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070071 Maybe<std::u16string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080072 ManifestFixerOptions manifestFixerOptions;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -080073 std::unordered_set<std::string> products;
Adam Lesinski355f2852016-02-13 20:26:45 -080074 TableSplitterOptions tableSplitterOptions;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070075};
76
77struct LinkContext : public IAaptContext {
78 StdErrDiagnostics mDiagnostics;
79 std::unique_ptr<NameMangler> mNameMangler;
80 std::u16string mCompilationPackage;
81 uint8_t mPackageId;
82 std::unique_ptr<ISymbolTable> mSymbols;
Adam Lesinski355f2852016-02-13 20:26:45 -080083 bool mVerbose = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070084
85 IDiagnostics* getDiagnostics() override {
86 return &mDiagnostics;
87 }
88
89 NameMangler* getNameMangler() override {
90 return mNameMangler.get();
91 }
92
93 StringPiece16 getCompilationPackage() override {
94 return mCompilationPackage;
95 }
96
97 uint8_t getPackageId() override {
98 return mPackageId;
99 }
100
101 ISymbolTable* getExternalSymbols() override {
102 return mSymbols.get();
103 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800104
105 bool verbose() override {
106 return mVerbose;
107 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700108};
109
Adam Lesinski355f2852016-02-13 20:26:45 -0800110static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
111 uint32_t compressionFlags,
112 IArchiveWriter* writer, IAaptContext* context) {
113 std::unique_ptr<io::IData> data = file->openAsData();
114 if (!data) {
115 context->getDiagnostics()->error(DiagMessage(file->getSource())
116 << "failed to open file");
117 return false;
118 }
119
120 CompiledFileInputStream inputStream(data->data(), data->size());
121 if (!inputStream.CompiledFile()) {
122 context->getDiagnostics()->error(DiagMessage(file->getSource())
123 << "invalid compiled file header");
124 return false;
125 }
126
127 if (context->verbose()) {
128 context->getDiagnostics()->note(DiagMessage() << "writing " << outPath << " to archive");
129 }
130
131 if (writer->startEntry(outPath, compressionFlags)) {
132 if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()),
133 inputStream.size())) {
134 if (writer->finishEntry()) {
135 return true;
136 }
137 }
138 }
139
140 context->getDiagnostics()->error(DiagMessage() << "failed to write file " << outPath);
141 return false;
142}
143
144static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
145 bool keepRawValues, IArchiveWriter* writer, IAaptContext* context) {
146 BigBuffer buffer(1024);
147 XmlFlattenerOptions options = {};
148 options.keepRawValues = keepRawValues;
149 options.maxSdkLevel = maxSdkLevel;
150 XmlFlattener flattener(&buffer, options);
151 if (!flattener.consume(context, xmlRes)) {
152 return false;
153 }
154
155 if (context->verbose()) {
156 DiagMessage msg;
157 msg << "writing " << path << " to archive";
158 if (maxSdkLevel) {
159 msg << " maxSdkLevel=" << maxSdkLevel.value();
160 }
161 context->getDiagnostics()->note(msg);
162 }
163
164 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
165 if (writer->writeEntry(buffer)) {
166 if (writer->finishEntry()) {
167 return true;
168 }
169 }
170 }
171 context->getDiagnostics()->error(DiagMessage() << "failed to write " << path << " to archive");
172 return false;
173}
174
175/*static std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len,
176 IDiagnostics* diag) {
177 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
178 BinaryResourceParser parser(diag, table.get(), source, data, len);
179 if (!parser.parse()) {
180 return {};
181 }
182 return table;
183}*/
184
185static std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
186 const void* data, size_t len,
187 IDiagnostics* diag) {
188 pb::ResourceTable pbTable;
189 if (!pbTable.ParseFromArray(data, len)) {
190 diag->error(DiagMessage(source) << "invalid compiled table");
191 return {};
192 }
193
194 std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source, diag);
195 if (!table) {
196 return {};
197 }
198 return table;
199}
200
201/**
202 * Inflates an XML file from the source path.
203 */
204static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
205 std::ifstream fin(path, std::ifstream::binary);
206 if (!fin) {
207 diag->error(DiagMessage(path) << strerror(errno));
208 return {};
209 }
210 return xml::inflate(&fin, diag, Source(path));
211}
212
213static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(const Source& source,
214 const void* data, size_t len,
215 IDiagnostics* diag) {
216 CompiledFileInputStream inputStream(data, len);
217 if (!inputStream.CompiledFile()) {
218 diag->error(DiagMessage(source) << "invalid compiled file header");
219 return {};
220 }
221
222 const uint8_t* xmlData = reinterpret_cast<const uint8_t*>(inputStream.data());
223 const size_t xmlDataLen = inputStream.size();
224
225 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(xmlData, xmlDataLen, diag, source);
226 if (!xmlRes) {
227 return {};
228 }
229 return xmlRes;
230}
231
232static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
233 const void* data, size_t len,
234 IDiagnostics* diag) {
235 CompiledFileInputStream inputStream(data, len);
236 const pb::CompiledFile* pbFile = inputStream.CompiledFile();
237 if (!pbFile) {
238 diag->error(DiagMessage(source) << "invalid compiled file header");
239 return {};
240 }
241
242 std::unique_ptr<ResourceFile> resFile = deserializeCompiledFileFromPb(*pbFile, source, diag);
243 if (!resFile) {
244 return {};
245 }
246 return resFile;
247}
248
249struct ResourceFileFlattenerOptions {
250 bool noAutoVersion = false;
251 bool keepRawValues = false;
252 bool doNotCompressAnything = false;
253 std::vector<std::string> extensionsToNotCompress;
254};
255
256class ResourceFileFlattener {
257public:
258 ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
259 IAaptContext* context, proguard::KeepSet* keepSet) :
260 mOptions(options), mContext(context), mKeepSet(keepSet) {
261 }
262
263 bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
264
265private:
266 struct FileOperation {
267 io::IFile* fileToCopy;
268 std::unique_ptr<xml::XmlResource> xmlToFlatten;
269 std::string dstPath;
270 };
271
272 uint32_t getCompressionFlags(const StringPiece& str);
273
274 std::unique_ptr<xml::XmlResource> linkAndVersionXmlFile(const ResourceEntry* entry,
275 const ResourceFile& fileDesc,
276 io::IFile* file,
277 ResourceTable* table);
278
279 ResourceFileFlattenerOptions mOptions;
280 IAaptContext* mContext;
281 proguard::KeepSet* mKeepSet;
282};
283
284uint32_t ResourceFileFlattener::getCompressionFlags(const StringPiece& str) {
285 if (mOptions.doNotCompressAnything) {
286 return 0;
287 }
288
289 for (const std::string& extension : mOptions.extensionsToNotCompress) {
290 if (util::stringEndsWith<char>(str, extension)) {
291 return 0;
292 }
293 }
294 return ArchiveEntry::kCompress;
295}
296
297std::unique_ptr<xml::XmlResource> ResourceFileFlattener::linkAndVersionXmlFile(
298 const ResourceEntry* entry,
299 const ResourceFile& fileDesc,
300 io::IFile* file,
301 ResourceTable* table) {
302 const StringPiece srcPath = file->getSource().path;
303 if (mContext->verbose()) {
304 mContext->getDiagnostics()->note(DiagMessage() << "linking " << srcPath);
305 }
306
307 std::unique_ptr<io::IData> data = file->openAsData();
308 if (!data) {
309 mContext->getDiagnostics()->error(DiagMessage(file->getSource()) << "failed to open file");
310 return {};
311 }
312
313 std::unique_ptr<xml::XmlResource> xmlRes;
314 if (util::stringEndsWith<char>(srcPath, ".flat")) {
315 xmlRes = loadBinaryXmlSkipFileExport(file->getSource(), data->data(), data->size(),
316 mContext->getDiagnostics());
317 } else {
318 xmlRes = xml::inflate(data->data(), data->size(), mContext->getDiagnostics(),
319 file->getSource());
320 }
321
322 if (!xmlRes) {
323 return {};
324 }
325
326 // Copy the the file description header.
327 xmlRes->file = fileDesc;
328
329 XmlReferenceLinker xmlLinker;
330 if (!xmlLinker.consume(mContext, xmlRes.get())) {
331 return {};
332 }
333
334 if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(), mKeepSet)) {
335 return {};
336 }
337
338 if (!mOptions.noAutoVersion) {
339 // Find the first SDK level used that is higher than this defined config and
340 // not superseded by a lower or equal SDK level resource.
341 for (int sdkLevel : xmlLinker.getSdkLevels()) {
342 if (sdkLevel > xmlRes->file.config.sdkVersion) {
343 if (!shouldGenerateVersionedResource(entry, xmlRes->file.config, sdkLevel)) {
344 // If we shouldn't generate a versioned resource, stop checking.
345 break;
346 }
347
348 ResourceFile versionedFileDesc = xmlRes->file;
349 versionedFileDesc.config.sdkVersion = sdkLevel;
350
351 if (mContext->verbose()) {
352 mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
353 << "auto-versioning resource from config '"
354 << xmlRes->file.config << "' -> '"
355 << versionedFileDesc.config << "'");
356 }
357
358 std::u16string genPath = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(
359 versionedFileDesc, mContext->getNameMangler()));
360
361 bool added = table->addFileReferenceAllowMangled(versionedFileDesc.name,
362 versionedFileDesc.config,
363 versionedFileDesc.source,
364 genPath,
365 file,
366 mContext->getDiagnostics());
367 if (!added) {
368 return {};
369 }
370 break;
371 }
372 }
373 }
374 return xmlRes;
375}
376
377/**
378 * Do not insert or remove any resources while executing in this function. It will
379 * corrupt the iteration order.
380 */
381bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiveWriter) {
382 bool error = false;
383 std::map<std::pair<ConfigDescription, StringPiece16>, FileOperation> configSortedFiles;
384
385 for (auto& pkg : table->packages) {
386 for (auto& type : pkg->types) {
387 // Sort by config and name, so that we get better locality in the zip file.
388 configSortedFiles.clear();
389 for (auto& entry : type->entries) {
390 // Iterate via indices because auto generated values can be inserted ahead of
391 // the value being processed.
392 for (size_t i = 0; i < entry->values.size(); i++) {
393 ResourceConfigValue* configValue = entry->values[i].get();
394
395 FileReference* fileRef = valueCast<FileReference>(configValue->value.get());
396 if (!fileRef) {
397 continue;
398 }
399
400 io::IFile* file = fileRef->file;
401 if (!file) {
402 mContext->getDiagnostics()->error(DiagMessage(fileRef->getSource())
403 << "file not found");
404 return false;
405 }
406
407 FileOperation fileOp;
408 fileOp.dstPath = util::utf16ToUtf8(*fileRef->path);
409
410 const StringPiece srcPath = file->getSource().path;
411 if (type->type != ResourceType::kRaw &&
412 (util::stringEndsWith<char>(srcPath, ".xml.flat") ||
413 util::stringEndsWith<char>(srcPath, ".xml"))) {
414 ResourceFile fileDesc;
415 fileDesc.config = configValue->config;
416 fileDesc.name = ResourceName(pkg->name, type->type, entry->name);
417 fileDesc.source = fileRef->getSource();
418 fileOp.xmlToFlatten = linkAndVersionXmlFile(entry.get(), fileDesc,
419 file, table);
420 if (!fileOp.xmlToFlatten) {
421 error = true;
422 continue;
423 }
424
425 } else {
426 fileOp.fileToCopy = file;
427 }
428
429 // NOTE(adamlesinski): Explicitly construct a StringPiece16 here, or else
430 // we end up copying the string in the std::make_pair() method, then creating
431 // a StringPiece16 from the copy, which would cause us to end up referencing
432 // garbage in the map.
433 const StringPiece16 entryName(entry->name);
434 configSortedFiles[std::make_pair(configValue->config, entryName)] =
435 std::move(fileOp);
436 }
437 }
438
439 if (error) {
440 return false;
441 }
442
443 // Now flatten the sorted values.
444 for (auto& mapEntry : configSortedFiles) {
445 const ConfigDescription& config = mapEntry.first.first;
446 const FileOperation& fileOp = mapEntry.second;
447
448 if (fileOp.xmlToFlatten) {
449 Maybe<size_t> maxSdkLevel;
450 if (!mOptions.noAutoVersion) {
451 maxSdkLevel = std::max<size_t>(config.sdkVersion, 1u);
452 }
453
454 bool result = flattenXml(fileOp.xmlToFlatten.get(), fileOp.dstPath, maxSdkLevel,
455 mOptions.keepRawValues,
456 archiveWriter, mContext);
457 if (!result) {
458 error = true;
459 }
460 } else {
461 bool result = copyFileToArchive(fileOp.fileToCopy, fileOp.dstPath,
462 getCompressionFlags(fileOp.dstPath),
463 archiveWriter, mContext);
464 if (!result) {
465 error = true;
466 }
467 }
468 }
469 }
470 }
471 return !error;
472}
473
Adam Lesinskifb48d292015-11-07 15:52:13 -0800474class LinkCommand {
475public:
Adam Lesinski6a008172016-02-02 17:02:58 -0800476 LinkCommand(LinkContext* context, const LinkOptions& options) :
477 mOptions(options), mContext(context), mFinalTable(), mFileCollection(nullptr) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800478 std::unique_ptr<io::FileCollection> fileCollection =
479 util::make_unique<io::FileCollection>();
480
481 // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
482 mFileCollection = fileCollection.get();
483
484 // Move it to the collection.
485 mCollections.push_back(std::move(fileCollection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800486 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700487
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700488 /**
489 * Creates a SymbolTable that loads symbols from the various APKs and caches the
490 * results for faster lookup.
491 */
492 std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
493 AssetManagerSymbolTableBuilder builder;
494 for (const std::string& path : mOptions.includePaths) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800495 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800496 mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700497 }
498
499 std::unique_ptr<android::AssetManager> assetManager =
500 util::make_unique<android::AssetManager>();
501 int32_t cookie = 0;
502 if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800503 mContext->getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800504 DiagMessage(path) << "failed to load include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700505 return {};
506 }
507 builder.add(std::move(assetManager));
508 }
509 return builder.build();
510 }
511
Adam Lesinski467f1712015-11-16 17:35:44 -0800512 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700513 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800514 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700515 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
516 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
517 return AppInfo{ packageAttr->value };
518 }
519 }
520 }
521 return {};
522 }
523
Adam Lesinski979ccb22016-01-11 10:42:19 -0800524 /**
525 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
526 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
527 * is an error and false is returned.
528 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800529 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800530 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800531 return mContext->getCompilationPackage() != pkg->name ||
Adam Lesinski979ccb22016-01-11 10:42:19 -0800532 !pkg->id ||
Adam Lesinski6a008172016-02-02 17:02:58 -0800533 pkg->id.value() != mContext->getPackageId();
Adam Lesinski979ccb22016-01-11 10:42:19 -0800534 };
535
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700536 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800537 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800538 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700539 // We have a package that is not related to the one we're building!
540 for (const auto& type : package->types) {
541 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800542 ResourceNameRef resName(package->name, type->type, entry->name);
543
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700544 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800545 // Special case the occurrence of an ID that is being generated for the
546 // 'android' package. This is due to legacy reasons.
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800547 if (valueCast<Id>(configValue->value.get()) &&
Adam Lesinski979ccb22016-01-11 10:42:19 -0800548 package->name == u"android") {
Adam Lesinski6a008172016-02-02 17:02:58 -0800549 mContext->getDiagnostics()->warn(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800550 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800551 << "generated id '" << resName
552 << "' for external package '" << package->name
553 << "'");
554 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800555 mContext->getDiagnostics()->error(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800556 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800557 << "defined resource '" << resName
558 << "' for external package '" << package->name
559 << "'");
560 error = true;
561 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700562 }
563 }
564 }
565 }
566 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800567
568 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
569 isExtPackageFunc);
570 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700571 return !error;
572 }
573
574 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
575 if (mOptions.outputToDirectory) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800576 return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700577 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800578 return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700579 }
580 }
581
582 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
583 BigBuffer buffer(1024);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800584 TableFlattener flattener(&buffer);
Adam Lesinski6a008172016-02-02 17:02:58 -0800585 if (!flattener.consume(mContext, table)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700586 return false;
587 }
588
Adam Lesinskia40e9722015-11-24 19:11:46 -0800589 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
590 if (writer->writeEntry(buffer)) {
591 if (writer->finishEntry()) {
592 return true;
593 }
594 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700595 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800596
Adam Lesinski6a008172016-02-02 17:02:58 -0800597 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800598 DiagMessage() << "failed to write resources.arsc to archive");
599 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700600 }
601
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700602
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700603 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
604 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700605 if (!mOptions.generateJavaClassPath) {
606 return true;
607 }
608
609 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700610 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700611 file::mkdirs(outPath);
612 file::appendPath(&outPath, "R.java");
613
614 std::ofstream fout(outPath, std::ofstream::binary);
615 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800616 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700617 return false;
618 }
619
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700620 JavaClassGenerator generator(table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700621 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800622 mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700623 return false;
624 }
625 return true;
626 }
627
Adam Lesinski467f1712015-11-16 17:35:44 -0800628 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700629 if (!mOptions.generateJavaClassPath) {
630 return true;
631 }
632
633 std::string outPath = mOptions.generateJavaClassPath.value();
634 file::appendPath(&outPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800635 file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700636 file::mkdirs(outPath);
637 file::appendPath(&outPath, "Manifest.java");
638
639 std::ofstream fout(outPath, std::ofstream::binary);
640 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800641 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700642 return false;
643 }
644
645 ManifestClassGenerator generator;
Adam Lesinski6a008172016-02-02 17:02:58 -0800646 if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(),
Adam Lesinskica5638f2015-10-21 14:42:43 -0700647 manifestXml, &fout)) {
648 return false;
649 }
650
651 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800652 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700653 return false;
654 }
655 return true;
656 }
657
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700658 bool writeProguardFile(const proguard::KeepSet& keepSet) {
659 if (!mOptions.generateProguardRulesPath) {
660 return true;
661 }
662
663 std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
664 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800665 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700666 return false;
667 }
668
669 proguard::writeKeepSet(&fout, keepSet);
670 if (!fout) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800671 mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700672 return false;
673 }
674 return true;
675 }
676
Adam Lesinskifb48d292015-11-07 15:52:13 -0800677 bool mergeStaticLibrary(const std::string& input) {
678 // TODO(adamlesinski): Load resources from a static library APK and merge the table into
679 // TableMerger.
Adam Lesinski6a008172016-02-02 17:02:58 -0800680 mContext->getDiagnostics()->warn(DiagMessage()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800681 << "linking static libraries not supported yet: "
682 << input);
683 return true;
684 }
685
Adam Lesinskia40e9722015-11-24 19:11:46 -0800686 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800687 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800688 mContext->getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800689 }
690
Adam Lesinskia40e9722015-11-24 19:11:46 -0800691 std::unique_ptr<io::IData> data = file->openAsData();
692 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800693 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800694 << "failed to open file");
695 return false;
696 }
697
Adam Lesinski355f2852016-02-13 20:26:45 -0800698 std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(),
699 data->data(), data->size(),
700 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800701 if (!table) {
702 return false;
703 }
704
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800705 bool result = false;
706 if (override) {
707 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
708 } else {
709 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800710 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800711 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800712 }
713
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800714 bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800715 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800716 mContext->getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800717 }
718
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800719 bool result = false;
720 if (overlay) {
721 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800722 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800723 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800724 }
725
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800726 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800727 return false;
728 }
729
730 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800731 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800732 if (exportedSymbol.name.package.empty()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800733 exportedSymbol.name.package = mContext->getCompilationPackage().toString();
Adam Lesinskifb48d292015-11-07 15:52:13 -0800734 }
735
736 ResourceNameRef resName = exportedSymbol.name;
737
Adam Lesinski6a008172016-02-02 17:02:58 -0800738 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800739 exportedSymbol.name);
740 if (mangledName) {
741 resName = mangledName.value();
742 }
743
744 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800745 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800746 bool result = mFinalTable.addResourceAllowMangled(resName,
747 ConfigDescription::defaultConfig(),
748 std::string(),
749 std::move(id),
Adam Lesinski6a008172016-02-02 17:02:58 -0800750 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800751 if (!result) {
752 return false;
753 }
754 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800755 return true;
756 }
757
Adam Lesinskia40e9722015-11-24 19:11:46 -0800758 /**
759 * Creates an io::IFileCollection from the ZIP archive and processes the files within.
760 */
761 bool mergeArchive(const std::string& input, bool override) {
762 std::string errorStr;
763 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
764 input, &errorStr);
765 if (!collection) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800766 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800767 return false;
768 }
769
770 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800771 for (auto iter = collection->iterator(); iter->hasNext(); ) {
772 if (!processFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800773 error = true;
774 }
775 }
776
777 // Make sure to move the collection into the set of IFileCollections.
778 mCollections.push_back(std::move(collection));
779 return !error;
780 }
781
782 bool processFile(const std::string& path, bool override) {
Adam Lesinski656a5772016-01-14 15:17:41 -0800783 if (util::stringEndsWith<char>(path, ".flata") ||
784 util::stringEndsWith<char>(path, ".jar") ||
785 util::stringEndsWith<char>(path, ".jack") ||
786 util::stringEndsWith<char>(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -0800787 return mergeArchive(path, override);
788 }
789
790 io::IFile* file = mFileCollection->insertFile(path);
791 return processFile(file, override);
792 }
793
794 bool processFile(io::IFile* file, bool override) {
795 const Source& src = file->getSource();
796 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
797 return mergeResourceTable(file, override);
Adam Lesinski52364f72016-01-11 13:10:24 -0800798 } else if (util::stringEndsWith<char>(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -0800799 // Try opening the file and looking for an Export header.
800 std::unique_ptr<io::IData> data = file->openAsData();
801 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800802 mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
Adam Lesinskia40e9722015-11-24 19:11:46 -0800803 return false;
804 }
805
806 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
Adam Lesinski6a008172016-02-02 17:02:58 -0800807 src, data->data(), data->size(), mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800808 if (resourceFile) {
809 return mergeCompiledFile(file, std::move(resourceFile), override);
810 }
Adam Lesinskic446a732016-01-21 11:04:46 -0800811
812 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800813 }
Adam Lesinski52364f72016-01-11 13:10:24 -0800814
Adam Lesinskic446a732016-01-21 11:04:46 -0800815 // Ignore non .flat files. This could be classes.dex or something else that happens
816 // to be in an archive.
817 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800818 }
819
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700820 int run(const std::vector<std::string>& inputFiles) {
821 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -0800822 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
Adam Lesinski6a008172016-02-02 17:02:58 -0800823 mContext->getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700824 if (!manifestXml) {
825 return 1;
826 }
827
828 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800829 mContext->mCompilationPackage = maybeAppInfo.value().package;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700830 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800831 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700832 << "no package specified in <manifest> tag");
833 return 1;
834 }
835
Adam Lesinski6a008172016-02-02 17:02:58 -0800836 if (!util::isJavaPackageName(mContext->mCompilationPackage)) {
837 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700838 << "invalid package name '"
Adam Lesinski6a008172016-02-02 17:02:58 -0800839 << mContext->mCompilationPackage
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700840 << "'");
841 return 1;
842 }
843
Adam Lesinski6a008172016-02-02 17:02:58 -0800844 mContext->mNameMangler = util::make_unique<NameMangler>(
845 NameManglerPolicy{ mContext->mCompilationPackage });
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700846
Adam Lesinski6a008172016-02-02 17:02:58 -0800847 if (mContext->mCompilationPackage == u"android") {
848 mContext->mPackageId = 0x01;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700849 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800850 mContext->mPackageId = 0x7f;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700851 }
852
Adam Lesinski6a008172016-02-02 17:02:58 -0800853 mContext->mSymbols = createSymbolTableFromIncludePaths();
854 if (!mContext->mSymbols) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700855 return 1;
856 }
857
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800858 TableMergerOptions tableMergerOptions;
859 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
Adam Lesinski6a008172016-02-02 17:02:58 -0800860 mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800861
Adam Lesinski355f2852016-02-13 20:26:45 -0800862 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800863 mContext->getDiagnostics()->note(
864 DiagMessage() << "linking package '" << mContext->mCompilationPackage << "' "
865 << "with package ID " << std::hex << (int) mContext->mPackageId);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700866 }
867
Adam Lesinskifb48d292015-11-07 15:52:13 -0800868
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700869 for (const std::string& input : inputFiles) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800870 if (!processFile(input, false)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800871 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
Adam Lesinski467f1712015-11-16 17:35:44 -0800872 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800873 }
874 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700875
Adam Lesinskifb48d292015-11-07 15:52:13 -0800876 for (const std::string& input : mOptions.overlayFiles) {
877 if (!processFile(input, true)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800878 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
Adam Lesinski467f1712015-11-16 17:35:44 -0800879 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700880 }
881 }
882
Adam Lesinskifb48d292015-11-07 15:52:13 -0800883 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700884 return 1;
885 }
886
887 if (!mOptions.staticLib) {
888 PrivateAttributeMover mover;
Adam Lesinski6a008172016-02-02 17:02:58 -0800889 if (!mover.consume(mContext, &mFinalTable)) {
890 mContext->getDiagnostics()->error(
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700891 DiagMessage() << "failed moving private attributes");
892 return 1;
893 }
894 }
895
896 {
897 IdAssigner idAssigner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800898 if (!idAssigner.consume(mContext, &mFinalTable)) {
899 mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700900 return 1;
901 }
902 }
903
Adam Lesinski6a008172016-02-02 17:02:58 -0800904 mContext->mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
905 mContext->mCompilationPackage, mTableMerger->getMergedPackages() });
906 mContext->mSymbols = JoinedSymbolTableBuilder()
Adam Lesinskifb48d292015-11-07 15:52:13 -0800907 .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
Adam Lesinski6a008172016-02-02 17:02:58 -0800908 .addSymbolTable(std::move(mContext->mSymbols))
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700909 .build();
910
911 {
912 ReferenceLinker linker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800913 if (!linker.consume(mContext, &mFinalTable)) {
914 mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700915 return 1;
916 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800917
918 ProductFilter productFilter(mOptions.products);
919 if (!productFilter.consume(mContext, &mFinalTable)) {
920 mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
921 return 1;
922 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800923
924 // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
925 // level.
926 TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
927 if (!tableSplitter.verifySplitConstraints(mContext)) {
928 return 1;
929 }
930
931 tableSplitter.splitTable(&mFinalTable);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700932 }
933
934 proguard::KeepSet proguardKeepSet;
935
936 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
937 if (!archiveWriter) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800938 mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700939 return 1;
940 }
941
Adam Lesinski467f1712015-11-16 17:35:44 -0800942 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700943 {
Adam Lesinski52364f72016-01-11 13:10:24 -0800944 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski6a008172016-02-02 17:02:58 -0800945 if (!manifestFixer.consume(mContext, manifestXml.get())) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800946 error = true;
947 }
948
Adam Lesinski467f1712015-11-16 17:35:44 -0800949 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
950 // (aka, which package the AndroidManifest.xml is coming from).
951 // So we give it a package name so it can see local resources.
Adam Lesinski6a008172016-02-02 17:02:58 -0800952 manifestXml->file.name.package = mContext->getCompilationPackage().toString();
Adam Lesinski467f1712015-11-16 17:35:44 -0800953
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700954 XmlReferenceLinker manifestLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -0800955 if (manifestLinker.consume(mContext, manifestXml.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700956 if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
957 manifestXml.get(),
958 &proguardKeepSet)) {
959 error = true;
960 }
961
Adam Lesinskica5638f2015-10-21 14:42:43 -0700962 if (mOptions.generateJavaClassPath) {
963 if (!writeManifestJavaFile(manifestXml.get())) {
964 error = true;
965 }
966 }
967
Adam Lesinski355f2852016-02-13 20:26:45 -0800968 const bool keepRawValues = mOptions.staticLib;
969 bool result = flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
970 keepRawValues, archiveWriter.get(), mContext);
971 if (!result) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700972 error = true;
973 }
974 } else {
975 error = true;
976 }
977 }
978
Adam Lesinski467f1712015-11-16 17:35:44 -0800979 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800980 mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
Adam Lesinski467f1712015-11-16 17:35:44 -0800981 return 1;
982 }
983
Adam Lesinski355f2852016-02-13 20:26:45 -0800984 ResourceFileFlattenerOptions fileFlattenerOptions;
985 fileFlattenerOptions.keepRawValues = mOptions.staticLib;
986 fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
987 fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
988 fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
989 ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800990
Adam Lesinski355f2852016-02-13 20:26:45 -0800991 if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800992 mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700993 return 1;
994 }
995
996 if (!mOptions.noAutoVersion) {
997 AutoVersioner versioner;
Adam Lesinski6a008172016-02-02 17:02:58 -0800998 if (!versioner.consume(mContext, &mFinalTable)) {
999 mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001000 return 1;
1001 }
1002 }
1003
Adam Lesinskifb48d292015-11-07 15:52:13 -08001004 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001005 mContext->getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001006 return 1;
1007 }
1008
1009 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001010 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -08001011 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
1012
Adam Lesinskief9c5012016-01-22 14:09:53 -08001013 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001014 options.useFinal = false;
1015 }
1016
Adam Lesinski6a008172016-02-02 17:02:58 -08001017 const StringPiece16 actualPackage = mContext->getCompilationPackage();
1018 StringPiece16 outputPackage = mContext->getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -08001019 if (mOptions.customJavaPackage) {
1020 // Override the output java package to the custom one.
1021 outputPackage = mOptions.customJavaPackage.value();
1022 }
Adam Lesinski83f22552015-11-07 11:51:23 -08001023
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001024 if (mOptions.privateSymbols) {
1025 // If we defined a private symbols package, we only emit Public symbols
1026 // to the original package, and private and public symbols to the private package.
1027
1028 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinski6a008172016-02-02 17:02:58 -08001029 if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -08001030 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001031 return 1;
1032 }
1033
1034 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -08001035 outputPackage = mOptions.privateSymbols.value();
1036 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001037
Adam Lesinskifb48d292015-11-07 15:52:13 -08001038 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -08001039 return 1;
1040 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001041
Adam Lesinski52364f72016-01-11 13:10:24 -08001042 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
1043 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001044 return 1;
1045 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001046 }
1047 }
1048
1049 if (mOptions.generateProguardRulesPath) {
1050 if (!writeProguardFile(proguardKeepSet)) {
1051 return 1;
1052 }
1053 }
1054
Adam Lesinski355f2852016-02-13 20:26:45 -08001055 if (mContext->verbose()) {
1056 DebugPrintTableOptions debugPrintTableOptions;
1057 debugPrintTableOptions.showSources = true;
1058 Debug::printTable(&mFinalTable, debugPrintTableOptions);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001059 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001060 return 0;
1061 }
Adam Lesinskifb48d292015-11-07 15:52:13 -08001062
1063private:
1064 LinkOptions mOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -08001065 LinkContext* mContext;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001066 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001067
1068 ResourceTable mLocalFileTable;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001069 std::unique_ptr<TableMerger> mTableMerger;
1070
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001071 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinskia40e9722015-11-24 19:11:46 -08001072 io::FileCollection* mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001073
1074 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001075 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001076};
1077
1078int link(const std::vector<StringPiece>& args) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001079 LinkContext context;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001080 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001081 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001082 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -08001083 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
1084 Maybe<std::string> versionCode, versionName;
1085 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001086 std::vector<std::string> extraJavaPackages;
Adam Lesinski6a008172016-02-02 17:02:58 -08001087 Maybe<std::string> configs;
Adam Lesinski355f2852016-02-13 20:26:45 -08001088 Maybe<std::string> preferredDensity;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001089 Maybe<std::string> productList;
Adam Lesinski8900aa82016-01-25 22:48:15 -08001090 bool legacyXFlag = false;
1091 bool requireLocalization = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001092 Flags flags = Flags()
1093 .requiredFlag("-o", "Output path", &options.outputPath)
1094 .requiredFlag("--manifest", "Path to the Android manifest to build",
1095 &options.manifestPath)
1096 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -08001097 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -08001098 "The last conflicting resource given takes precedence.",
1099 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001100 .optionalFlag("--java", "Directory in which to generate R.java",
1101 &options.generateJavaClassPath)
1102 .optionalFlag("--proguard", "Output file for generated Proguard rules",
1103 &options.generateProguardRulesPath)
1104 .optionalSwitch("--no-auto-version",
1105 "Disables automatic style and layout SDK versioning",
1106 &options.noAutoVersion)
Adam Lesinski8900aa82016-01-25 22:48:15 -08001107 .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
1108 &legacyXFlag)
1109 .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
1110 &requireLocalization)
Adam Lesinski6a008172016-02-02 17:02:58 -08001111 .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
1112 "is all configurations", &configs)
Adam Lesinski355f2852016-02-13 20:26:45 -08001113 .optionalFlag("--preferred-density",
1114 "Selects the closest matching density and strips out all others.",
1115 &preferredDensity)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001116 .optionalFlag("--product", "Comma separated list of product names to keep",
1117 &productList)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001118 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
1119 "by -o",
1120 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001121 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
1122 "AndroidManifest.xml", &minSdkVersion)
1123 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
1124 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -08001125 .optionalFlag("--version-code", "Version code (integer) to inject into the "
1126 "AndroidManifest.xml if none is present", &versionCode)
1127 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
1128 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001129 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinskief9c5012016-01-22 14:09:53 -08001130 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
1131 "This is implied when --static-lib is specified.",
1132 &options.generateNonFinalIds)
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001133 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001134 "private symbols.\n"
1135 "If not specified, public and private symbols will use the application's "
1136 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -08001137 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
1138 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -08001139 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001140 "package names", &extraJavaPackages)
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001141 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
1142 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -08001143 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
1144 &renameManifestPackage)
1145 .optionalFlag("--rename-instrumentation-target-package",
1146 "Changes the name of the target package for instrumentation. Most useful "
1147 "when used\nin conjunction with --rename-manifest-package",
1148 &renameInstrumentationTargetPackage)
1149 .optionalFlagList("-0", "File extensions not to compress",
1150 &options.extensionsToNotCompress)
Adam Lesinski355f2852016-02-13 20:26:45 -08001151 .optionalSwitch("-v", "Enables verbose logging", &context.mVerbose);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001152
1153 if (!flags.parse("aapt2 link", args, &std::cerr)) {
1154 return 1;
1155 }
1156
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001157 if (privateSymbolsPackage) {
1158 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
1159 }
1160
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001161 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001162 options.manifestFixerOptions.minSdkVersionDefault =
1163 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001164 }
1165
1166 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001167 options.manifestFixerOptions.targetSdkVersionDefault =
1168 util::utf8ToUtf16(targetSdkVersion.value());
1169 }
1170
1171 if (renameManifestPackage) {
1172 options.manifestFixerOptions.renameManifestPackage =
1173 util::utf8ToUtf16(renameManifestPackage.value());
1174 }
1175
1176 if (renameInstrumentationTargetPackage) {
1177 options.manifestFixerOptions.renameInstrumentationTargetPackage =
1178 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
1179 }
1180
1181 if (versionCode) {
1182 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
1183 }
1184
1185 if (versionName) {
1186 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
1187 }
1188
1189 if (customJavaPackage) {
1190 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001191 }
1192
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001193 // Populate the set of extra packages for which to generate R.java.
1194 for (std::string& extraPackage : extraJavaPackages) {
1195 // A given package can actually be a colon separated list of packages.
1196 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001197 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001198 }
1199 }
1200
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001201 if (productList) {
1202 for (StringPiece product : util::tokenize<char>(productList.value(), ',')) {
1203 if (product != "" && product != "default") {
1204 options.products.insert(product.toString());
1205 }
1206 }
1207 }
1208
Adam Lesinski6a008172016-02-02 17:02:58 -08001209 AxisConfigFilter filter;
1210 if (configs) {
1211 for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
1212 ConfigDescription config;
1213 LocaleValue lv;
1214 if (lv.initFromFilterString(configStr)) {
1215 lv.writeTo(&config);
1216 } else if (!ConfigDescription::parse(configStr, &config)) {
1217 context.getDiagnostics()->error(
1218 DiagMessage() << "invalid config '" << configStr << "' for -c option");
1219 return 1;
1220 }
1221
1222 if (config.density != 0) {
1223 context.getDiagnostics()->warn(
1224 DiagMessage() << "ignoring density '" << config << "' for -c option");
1225 } else {
1226 filter.addConfig(config);
1227 }
1228 }
1229
Adam Lesinski355f2852016-02-13 20:26:45 -08001230 options.tableSplitterOptions.configFilter = &filter;
1231 }
1232
1233 if (preferredDensity) {
1234 ConfigDescription preferredDensityConfig;
1235 if (!ConfigDescription::parse(preferredDensity.value(), &preferredDensityConfig)) {
1236 context.getDiagnostics()->error(DiagMessage() << "invalid density '"
1237 << preferredDensity.value()
1238 << "' for --preferred-density option");
1239 return 1;
1240 }
1241
1242 // Clear the version that can be automatically added.
1243 preferredDensityConfig.sdkVersion = 0;
1244
1245 if (preferredDensityConfig.diff(ConfigDescription::defaultConfig())
1246 != ConfigDescription::CONFIG_DENSITY) {
1247 context.getDiagnostics()->error(DiagMessage() << "invalid preferred density '"
1248 << preferredDensity.value() << "'. "
1249 << "Preferred density must only be a density value");
1250 return 1;
1251 }
1252 options.tableSplitterOptions.preferredDensity = preferredDensityConfig.density;
Adam Lesinski6a008172016-02-02 17:02:58 -08001253 }
1254
1255 LinkCommand cmd(&context, options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001256 return cmd.run(flags.getArgs());
1257}
1258
1259} // namespace aapt