blob: 1417fd645bd6871c9c74e7668832cb65f4fe009b [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 Lesinskibf0bd0f2016-06-01 15:31:50 -070047#include <android-base/file.h>
Adam Lesinski59e04c62016-02-04 15:59:23 -080048#include <google/protobuf/io/coded_stream.h>
49
Adam Lesinski1ab598f2015-08-14 14:26:04 -070050#include <fstream>
51#include <sys/stat.h>
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070052#include <unordered_map>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070053#include <vector>
54
55namespace aapt {
56
57struct LinkOptions {
58 std::string outputPath;
59 std::string manifestPath;
60 std::vector<std::string> includePaths;
Adam Lesinskifb48d292015-11-07 15:52:13 -080061 std::vector<std::string> overlayFiles;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 Maybe<std::string> generateJavaClassPath;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070063 Maybe<std::string> customJavaPackage;
64 std::set<std::string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070065 Maybe<std::string> generateProguardRulesPath;
Rohit Agrawale49bb302016-04-22 12:27:55 -070066 Maybe<std::string> generateMainDexProguardRulesPath;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070067 bool noAutoVersion = false;
Adam Lesinski64587af2016-02-18 18:33:06 -080068 bool noVersionVectors = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070069 bool staticLib = false;
Adam Lesinski64587af2016-02-18 18:33:06 -080070 bool noStaticLibPackages = false;
Adam Lesinskief9c5012016-01-22 14:09:53 -080071 bool generateNonFinalIds = false;
Adam Lesinski3524a232016-04-01 19:19:24 -070072 std::vector<std::string> javadocAnnotations;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070073 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080074 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080075 bool doNotCompressAnything = false;
Adam Lesinski9756dec2016-08-08 12:35:04 -070076 std::unordered_set<std::string> extensionsToNotCompress;
Adam Lesinskid0f116b2016-07-08 15:00:32 -070077 Maybe<std::string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080078 ManifestFixerOptions manifestFixerOptions;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -080079 std::unordered_set<std::string> products;
Adam Lesinski355f2852016-02-13 20:26:45 -080080 TableSplitterOptions tableSplitterOptions;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -070081 std::unordered_map<ResourceName, ResourceId> stableIdMap;
82 Maybe<std::string> resourceIdMapPath;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070083};
84
Adam Lesinski64587af2016-02-18 18:33:06 -080085class LinkContext : public IAaptContext {
86public:
87 LinkContext() : mNameMangler({}) {
88 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070089
90 IDiagnostics* getDiagnostics() override {
91 return &mDiagnostics;
92 }
93
94 NameMangler* getNameMangler() override {
Adam Lesinski64587af2016-02-18 18:33:06 -080095 return &mNameMangler;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070096 }
97
Adam Lesinski64587af2016-02-18 18:33:06 -080098 void setNameManglerPolicy(const NameManglerPolicy& policy) {
99 mNameMangler = NameMangler(policy);
100 }
101
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700102 const std::string& getCompilationPackage() override {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700103 return mCompilationPackage;
104 }
105
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700106 void setCompilationPackage(const StringPiece& packageName) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800107 mCompilationPackage = packageName.toString();
108 }
109
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700110 uint8_t getPackageId() override {
111 return mPackageId;
112 }
113
Adam Lesinski64587af2016-02-18 18:33:06 -0800114 void setPackageId(uint8_t id) {
115 mPackageId = id;
116 }
117
118 SymbolTable* getExternalSymbols() override {
119 return &mSymbols;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700120 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800121
122 bool verbose() override {
123 return mVerbose;
124 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800125
126 void setVerbose(bool val) {
127 mVerbose = val;
128 }
129
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700130 int getMinSdkVersion() override {
131 return mMinSdkVersion;
132 }
133
134 void setMinSdkVersion(int minSdk) {
135 mMinSdkVersion = minSdk;
136 }
137
Adam Lesinski64587af2016-02-18 18:33:06 -0800138private:
139 StdErrDiagnostics mDiagnostics;
140 NameMangler mNameMangler;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700141 std::string mCompilationPackage;
Adam Lesinski64587af2016-02-18 18:33:06 -0800142 uint8_t mPackageId = 0x0;
143 SymbolTable mSymbols;
144 bool mVerbose = false;
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700145 int mMinSdkVersion = 0;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700146};
147
Adam Lesinski355f2852016-02-13 20:26:45 -0800148static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
149 uint32_t compressionFlags,
150 IArchiveWriter* writer, IAaptContext* context) {
151 std::unique_ptr<io::IData> data = file->openAsData();
152 if (!data) {
153 context->getDiagnostics()->error(DiagMessage(file->getSource())
154 << "failed to open file");
155 return false;
156 }
157
Adam Lesinski64587af2016-02-18 18:33:06 -0800158 const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
159 size_t bufferSize = data->size();
160
161 // If the file ends with .flat, we must strip off the CompiledFileHeader from it.
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700162 if (util::stringEndsWith(file->getSource().path, ".flat")) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800163 CompiledFileInputStream inputStream(data->data(), data->size());
164 if (!inputStream.CompiledFile()) {
165 context->getDiagnostics()->error(DiagMessage(file->getSource())
166 << "invalid compiled file header");
167 return false;
168 }
169 buffer = reinterpret_cast<const uint8_t*>(inputStream.data());
170 bufferSize = inputStream.size();
Adam Lesinski355f2852016-02-13 20:26:45 -0800171 }
172
173 if (context->verbose()) {
174 context->getDiagnostics()->note(DiagMessage() << "writing " << outPath << " to archive");
175 }
176
177 if (writer->startEntry(outPath, compressionFlags)) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800178 if (writer->writeEntry(buffer, bufferSize)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800179 if (writer->finishEntry()) {
180 return true;
181 }
182 }
183 }
184
185 context->getDiagnostics()->error(DiagMessage() << "failed to write file " << outPath);
186 return false;
187}
188
189static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
190 bool keepRawValues, IArchiveWriter* writer, IAaptContext* context) {
191 BigBuffer buffer(1024);
192 XmlFlattenerOptions options = {};
193 options.keepRawValues = keepRawValues;
194 options.maxSdkLevel = maxSdkLevel;
195 XmlFlattener flattener(&buffer, options);
196 if (!flattener.consume(context, xmlRes)) {
197 return false;
198 }
199
200 if (context->verbose()) {
201 DiagMessage msg;
202 msg << "writing " << path << " to archive";
203 if (maxSdkLevel) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800204 msg << " maxSdkLevel=" << maxSdkLevel.value() << " keepRawValues=" << keepRawValues;
Adam Lesinski355f2852016-02-13 20:26:45 -0800205 }
206 context->getDiagnostics()->note(msg);
207 }
208
209 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
210 if (writer->writeEntry(buffer)) {
211 if (writer->finishEntry()) {
212 return true;
213 }
214 }
215 }
216 context->getDiagnostics()->error(DiagMessage() << "failed to write " << path << " to archive");
217 return false;
218}
219
Adam Lesinski355f2852016-02-13 20:26:45 -0800220static std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
221 const void* data, size_t len,
222 IDiagnostics* diag) {
223 pb::ResourceTable pbTable;
224 if (!pbTable.ParseFromArray(data, len)) {
225 diag->error(DiagMessage(source) << "invalid compiled table");
226 return {};
227 }
228
229 std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source, diag);
230 if (!table) {
231 return {};
232 }
233 return table;
234}
235
236/**
237 * Inflates an XML file from the source path.
238 */
239static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
240 std::ifstream fin(path, std::ifstream::binary);
241 if (!fin) {
242 diag->error(DiagMessage(path) << strerror(errno));
243 return {};
244 }
245 return xml::inflate(&fin, diag, Source(path));
246}
247
248static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(const Source& source,
249 const void* data, size_t len,
250 IDiagnostics* diag) {
251 CompiledFileInputStream inputStream(data, len);
252 if (!inputStream.CompiledFile()) {
253 diag->error(DiagMessage(source) << "invalid compiled file header");
254 return {};
255 }
256
257 const uint8_t* xmlData = reinterpret_cast<const uint8_t*>(inputStream.data());
258 const size_t xmlDataLen = inputStream.size();
259
260 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(xmlData, xmlDataLen, diag, source);
261 if (!xmlRes) {
262 return {};
263 }
264 return xmlRes;
265}
266
267static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
268 const void* data, size_t len,
269 IDiagnostics* diag) {
270 CompiledFileInputStream inputStream(data, len);
271 const pb::CompiledFile* pbFile = inputStream.CompiledFile();
272 if (!pbFile) {
273 diag->error(DiagMessage(source) << "invalid compiled file header");
274 return {};
275 }
276
277 std::unique_ptr<ResourceFile> resFile = deserializeCompiledFileFromPb(*pbFile, source, diag);
278 if (!resFile) {
279 return {};
280 }
281 return resFile;
282}
283
284struct ResourceFileFlattenerOptions {
285 bool noAutoVersion = false;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800286 bool noVersionVectors = false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800287 bool keepRawValues = false;
288 bool doNotCompressAnything = false;
Rohit Agrawale49bb302016-04-22 12:27:55 -0700289 bool updateProguardSpec = false;
Adam Lesinski9756dec2016-08-08 12:35:04 -0700290 std::unordered_set<std::string> extensionsToNotCompress;
Adam Lesinski355f2852016-02-13 20:26:45 -0800291};
292
293class ResourceFileFlattener {
294public:
295 ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
296 IAaptContext* context, proguard::KeepSet* keepSet) :
297 mOptions(options), mContext(context), mKeepSet(keepSet) {
298 }
299
300 bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
301
302private:
303 struct FileOperation {
304 io::IFile* fileToCopy;
305 std::unique_ptr<xml::XmlResource> xmlToFlatten;
306 std::string dstPath;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800307 bool skipVersion = false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800308 };
309
310 uint32_t getCompressionFlags(const StringPiece& str);
311
Adam Lesinski626a69f2016-03-03 10:09:26 -0800312 bool linkAndVersionXmlFile(const ResourceEntry* entry, const ResourceFile& fileDesc,
313 io::IFile* file, ResourceTable* table, FileOperation* outFileOp);
Adam Lesinski355f2852016-02-13 20:26:45 -0800314
315 ResourceFileFlattenerOptions mOptions;
316 IAaptContext* mContext;
317 proguard::KeepSet* mKeepSet;
318};
319
320uint32_t ResourceFileFlattener::getCompressionFlags(const StringPiece& str) {
321 if (mOptions.doNotCompressAnything) {
322 return 0;
323 }
324
325 for (const std::string& extension : mOptions.extensionsToNotCompress) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700326 if (util::stringEndsWith(str, extension)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800327 return 0;
328 }
329 }
330 return ArchiveEntry::kCompress;
331}
332
Adam Lesinski626a69f2016-03-03 10:09:26 -0800333bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry,
334 const ResourceFile& fileDesc,
335 io::IFile* file,
336 ResourceTable* table,
337 FileOperation* outFileOp) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800338 const StringPiece srcPath = file->getSource().path;
339 if (mContext->verbose()) {
340 mContext->getDiagnostics()->note(DiagMessage() << "linking " << srcPath);
341 }
342
343 std::unique_ptr<io::IData> data = file->openAsData();
344 if (!data) {
345 mContext->getDiagnostics()->error(DiagMessage(file->getSource()) << "failed to open file");
Adam Lesinski626a69f2016-03-03 10:09:26 -0800346 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800347 }
348
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700349 if (util::stringEndsWith(srcPath, ".flat")) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800350 outFileOp->xmlToFlatten = loadBinaryXmlSkipFileExport(file->getSource(),
351 data->data(), data->size(),
352 mContext->getDiagnostics());
Adam Lesinski355f2852016-02-13 20:26:45 -0800353 } else {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800354 outFileOp->xmlToFlatten = xml::inflate(data->data(), data->size(),
355 mContext->getDiagnostics(),
356 file->getSource());
Adam Lesinski355f2852016-02-13 20:26:45 -0800357 }
358
Adam Lesinski626a69f2016-03-03 10:09:26 -0800359 if (!outFileOp->xmlToFlatten) {
360 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800361 }
362
363 // Copy the the file description header.
Adam Lesinski626a69f2016-03-03 10:09:26 -0800364 outFileOp->xmlToFlatten->file = fileDesc;
Adam Lesinski355f2852016-02-13 20:26:45 -0800365
366 XmlReferenceLinker xmlLinker;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800367 if (!xmlLinker.consume(mContext, outFileOp->xmlToFlatten.get())) {
368 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800369 }
370
Rohit Agrawale49bb302016-04-22 12:27:55 -0700371 if (mOptions.updateProguardSpec && !proguard::collectProguardRules(
372 outFileOp->xmlToFlatten->file.source, outFileOp->xmlToFlatten.get(), mKeepSet)) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800373 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800374 }
375
376 if (!mOptions.noAutoVersion) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800377 if (mOptions.noVersionVectors) {
378 // Skip this if it is a vector or animated-vector.
379 xml::Element* el = xml::findRootElement(outFileOp->xmlToFlatten.get());
380 if (el && el->namespaceUri.empty()) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700381 if (el->name == "vector" || el->name == "animated-vector") {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800382 // We are NOT going to version this file.
383 outFileOp->skipVersion = true;
384 return true;
385 }
386 }
387 }
388
Adam Lesinski355f2852016-02-13 20:26:45 -0800389 // Find the first SDK level used that is higher than this defined config and
390 // not superseded by a lower or equal SDK level resource.
391 for (int sdkLevel : xmlLinker.getSdkLevels()) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800392 if (sdkLevel > outFileOp->xmlToFlatten->file.config.sdkVersion) {
393 if (!shouldGenerateVersionedResource(entry, outFileOp->xmlToFlatten->file.config,
394 sdkLevel)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800395 // If we shouldn't generate a versioned resource, stop checking.
396 break;
397 }
398
Adam Lesinski626a69f2016-03-03 10:09:26 -0800399 ResourceFile versionedFileDesc = outFileOp->xmlToFlatten->file;
Adam Lesinski64587af2016-02-18 18:33:06 -0800400 versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel;
Adam Lesinski355f2852016-02-13 20:26:45 -0800401
402 if (mContext->verbose()) {
403 mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
404 << "auto-versioning resource from config '"
Adam Lesinski626a69f2016-03-03 10:09:26 -0800405 << outFileOp->xmlToFlatten->file.config
406 << "' -> '"
Adam Lesinski355f2852016-02-13 20:26:45 -0800407 << versionedFileDesc.config << "'");
408 }
409
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700410 std::string genPath = ResourceUtils::buildResourceFileName(
411 versionedFileDesc, mContext->getNameMangler());
Adam Lesinski355f2852016-02-13 20:26:45 -0800412
413 bool added = table->addFileReferenceAllowMangled(versionedFileDesc.name,
414 versionedFileDesc.config,
415 versionedFileDesc.source,
416 genPath,
417 file,
418 mContext->getDiagnostics());
419 if (!added) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800420 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800421 }
422 break;
423 }
424 }
425 }
Adam Lesinski626a69f2016-03-03 10:09:26 -0800426 return true;
Adam Lesinski355f2852016-02-13 20:26:45 -0800427}
428
429/**
430 * Do not insert or remove any resources while executing in this function. It will
431 * corrupt the iteration order.
432 */
433bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiveWriter) {
434 bool error = false;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700435 std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> configSortedFiles;
Adam Lesinski355f2852016-02-13 20:26:45 -0800436
437 for (auto& pkg : table->packages) {
438 for (auto& type : pkg->types) {
439 // Sort by config and name, so that we get better locality in the zip file.
440 configSortedFiles.clear();
441 for (auto& entry : type->entries) {
442 // Iterate via indices because auto generated values can be inserted ahead of
443 // the value being processed.
444 for (size_t i = 0; i < entry->values.size(); i++) {
445 ResourceConfigValue* configValue = entry->values[i].get();
446
447 FileReference* fileRef = valueCast<FileReference>(configValue->value.get());
448 if (!fileRef) {
449 continue;
450 }
451
452 io::IFile* file = fileRef->file;
453 if (!file) {
454 mContext->getDiagnostics()->error(DiagMessage(fileRef->getSource())
455 << "file not found");
456 return false;
457 }
458
459 FileOperation fileOp;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700460 fileOp.dstPath = *fileRef->path;
Adam Lesinski355f2852016-02-13 20:26:45 -0800461
462 const StringPiece srcPath = file->getSource().path;
463 if (type->type != ResourceType::kRaw &&
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700464 (util::stringEndsWith(srcPath, ".xml.flat") ||
465 util::stringEndsWith(srcPath, ".xml"))) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800466 ResourceFile fileDesc;
467 fileDesc.config = configValue->config;
468 fileDesc.name = ResourceName(pkg->name, type->type, entry->name);
469 fileDesc.source = fileRef->getSource();
Adam Lesinski626a69f2016-03-03 10:09:26 -0800470 if (!linkAndVersionXmlFile(entry.get(), fileDesc, file, table, &fileOp)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800471 error = true;
472 continue;
473 }
474
475 } else {
476 fileOp.fileToCopy = file;
477 }
478
479 // NOTE(adamlesinski): Explicitly construct a StringPiece16 here, or else
480 // we end up copying the string in the std::make_pair() method, then creating
481 // a StringPiece16 from the copy, which would cause us to end up referencing
482 // garbage in the map.
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700483 const StringPiece entryName(entry->name);
Adam Lesinski355f2852016-02-13 20:26:45 -0800484 configSortedFiles[std::make_pair(configValue->config, entryName)] =
485 std::move(fileOp);
486 }
487 }
488
489 if (error) {
490 return false;
491 }
492
493 // Now flatten the sorted values.
494 for (auto& mapEntry : configSortedFiles) {
495 const ConfigDescription& config = mapEntry.first.first;
496 const FileOperation& fileOp = mapEntry.second;
497
498 if (fileOp.xmlToFlatten) {
499 Maybe<size_t> maxSdkLevel;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800500 if (!mOptions.noAutoVersion && !fileOp.skipVersion) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800501 maxSdkLevel = std::max<size_t>(config.sdkVersion, 1u);
502 }
503
504 bool result = flattenXml(fileOp.xmlToFlatten.get(), fileOp.dstPath, maxSdkLevel,
505 mOptions.keepRawValues,
506 archiveWriter, mContext);
507 if (!result) {
508 error = true;
509 }
510 } else {
511 bool result = copyFileToArchive(fileOp.fileToCopy, fileOp.dstPath,
512 getCompressionFlags(fileOp.dstPath),
513 archiveWriter, mContext);
514 if (!result) {
515 error = true;
516 }
517 }
518 }
519 }
520 }
521 return !error;
522}
523
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700524static bool writeStableIdMapToPath(IDiagnostics* diag,
525 const std::unordered_map<ResourceName, ResourceId>& idMap,
526 const std::string idMapPath) {
527 std::ofstream fout(idMapPath, std::ofstream::binary);
528 if (!fout) {
529 diag->error(DiagMessage(idMapPath) << strerror(errno));
530 return false;
531 }
532
533 for (const auto& entry : idMap) {
534 const ResourceName& name = entry.first;
535 const ResourceId& id = entry.second;
536 fout << name << " = " << id << "\n";
537 }
538
539 if (!fout) {
540 diag->error(DiagMessage(idMapPath) << "failed writing to file: " << strerror(errno));
541 return false;
542 }
543
544 return true;
545}
546
547static bool loadStableIdMap(IDiagnostics* diag, const std::string& path,
548 std::unordered_map<ResourceName, ResourceId>* outIdMap) {
549 std::string content;
550 if (!android::base::ReadFileToString(path, &content)) {
551 diag->error(DiagMessage(path) << "failed reading stable ID file");
552 return false;
553 }
554
555 outIdMap->clear();
556 size_t lineNo = 0;
557 for (StringPiece line : util::tokenize(content, '\n')) {
558 lineNo++;
559 line = util::trimWhitespace(line);
560 if (line.empty()) {
561 continue;
562 }
563
564 auto iter = std::find(line.begin(), line.end(), '=');
565 if (iter == line.end()) {
566 diag->error(DiagMessage(Source(path, lineNo)) << "missing '='");
567 return false;
568 }
569
570 ResourceNameRef name;
571 StringPiece resNameStr = util::trimWhitespace(
572 line.substr(0, std::distance(line.begin(), iter)));
573 if (!ResourceUtils::parseResourceName(resNameStr, &name)) {
574 diag->error(DiagMessage(Source(path, lineNo))
575 << "invalid resource name '" << resNameStr << "'");
576 return false;
577 }
578
579 const size_t resIdStartIdx = std::distance(line.begin(), iter) + 1;
580 const size_t resIdStrLen = line.size() - resIdStartIdx;
581 StringPiece resIdStr = util::trimWhitespace(line.substr(resIdStartIdx, resIdStrLen));
582
583 Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(resIdStr);
584 if (!maybeId) {
585 diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource ID '"
586 << resIdStr << "'");
587 return false;
588 }
589
590 (*outIdMap)[name.toResourceName()] = maybeId.value();
591 }
592 return true;
593}
594
Adam Lesinskifb48d292015-11-07 15:52:13 -0800595class LinkCommand {
596public:
Adam Lesinski6a008172016-02-02 17:02:58 -0800597 LinkCommand(LinkContext* context, const LinkOptions& options) :
Adam Lesinski64587af2016-02-18 18:33:06 -0800598 mOptions(options), mContext(context), mFinalTable(),
599 mFileCollection(util::make_unique<io::FileCollection>()) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800600 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700601
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700602 /**
603 * Creates a SymbolTable that loads symbols from the various APKs and caches the
604 * results for faster lookup.
605 */
Adam Lesinski64587af2016-02-18 18:33:06 -0800606 bool loadSymbolsFromIncludePaths() {
607 std::unique_ptr<AssetManagerSymbolSource> assetSource =
608 util::make_unique<AssetManagerSymbolSource>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700609 for (const std::string& path : mOptions.includePaths) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800610 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800611 mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700612 }
613
Adam Lesinski64587af2016-02-18 18:33:06 -0800614 // First try to load the file as a static lib.
615 std::string errorStr;
616 std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr);
617 if (staticInclude) {
618 if (!mOptions.staticLib) {
619 // Can't include static libraries when not building a static library.
620 mContext->getDiagnostics()->error(
621 DiagMessage(path) << "can't include static library when building app");
622 return false;
623 }
624
625 // If we are using --no-static-lib-packages, we need to rename the package of this
626 // table to our compilation package.
627 if (mOptions.noStaticLibPackages) {
628 if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) {
629 pkg->name = mContext->getCompilationPackage();
630 }
631 }
632
633 mContext->getExternalSymbols()->appendSource(
634 util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
635
636 mStaticTableIncludes.push_back(std::move(staticInclude));
637
638 } else if (!errorStr.empty()) {
639 // We had an error with reading, so fail.
640 mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
641 return false;
642 }
643
644 if (!assetSource->addAssetPath(path)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800645 mContext->getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800646 DiagMessage(path) << "failed to load include path");
Adam Lesinski64587af2016-02-18 18:33:06 -0800647 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700648 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700649 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800650
651 mContext->getExternalSymbols()->appendSource(std::move(assetSource));
652 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700653 }
654
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700655 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700656 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800657 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700658 AppInfo appInfo;
659
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700660 if (!manifestEl->namespaceUri.empty() || manifestEl->name != "manifest") {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700661 diag->error(DiagMessage(xmlRes->file.source) << "root tag must be <manifest>");
662 return {};
663 }
664
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700665 xml::Attribute* packageAttr = manifestEl->findAttribute({}, "package");
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700666 if (!packageAttr) {
667 diag->error(DiagMessage(xmlRes->file.source)
668 << "<manifest> must have a 'package' attribute");
669 return {};
670 }
671
672 appInfo.package = packageAttr->value;
673
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700674 if (xml::Element* usesSdkEl = manifestEl->findChild({}, "uses-sdk")) {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700675 if (xml::Attribute* minSdk =
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700676 usesSdkEl->findAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700677 appInfo.minSdkVersion = minSdk->value;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700678 }
679 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700680
681 return appInfo;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700682 }
683 return {};
684 }
685
Adam Lesinski979ccb22016-01-11 10:42:19 -0800686 /**
687 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
688 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
689 * is an error and false is returned.
690 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800691 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800692 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800693 return mContext->getCompilationPackage() != pkg->name ||
Adam Lesinski979ccb22016-01-11 10:42:19 -0800694 !pkg->id ||
Adam Lesinski6a008172016-02-02 17:02:58 -0800695 pkg->id.value() != mContext->getPackageId();
Adam Lesinski979ccb22016-01-11 10:42:19 -0800696 };
697
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700698 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800699 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800700 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700701 // We have a package that is not related to the one we're building!
702 for (const auto& type : package->types) {
703 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800704 ResourceNameRef resName(package->name, type->type, entry->name);
705
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700706 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800707 // Special case the occurrence of an ID that is being generated for the
708 // 'android' package. This is due to legacy reasons.
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800709 if (valueCast<Id>(configValue->value.get()) &&
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700710 package->name == "android") {
Adam Lesinski6a008172016-02-02 17:02:58 -0800711 mContext->getDiagnostics()->warn(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800712 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800713 << "generated id '" << resName
714 << "' for external package '" << package->name
715 << "'");
716 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800717 mContext->getDiagnostics()->error(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800718 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800719 << "defined resource '" << resName
720 << "' for external package '" << package->name
721 << "'");
722 error = true;
723 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700724 }
725 }
726 }
727 }
728 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800729
730 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
731 isExtPackageFunc);
732 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700733 return !error;
734 }
735
Adam Lesinski64587af2016-02-18 18:33:06 -0800736 /**
737 * Returns true if no IDs have been set, false otherwise.
738 */
739 bool verifyNoIdsSet() {
740 for (const auto& package : mFinalTable.packages) {
741 for (const auto& type : package->types) {
742 if (type->id) {
743 mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type
744 << " has ID " << std::hex
745 << (int) type->id.value()
746 << std::dec << " assigned");
747 return false;
748 }
749
750 for (const auto& entry : type->entries) {
751 if (entry->id) {
752 ResourceNameRef resName(package->name, type->type, entry->name);
753 mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName
754 << " has ID " << std::hex
755 << (int) entry->id.value()
756 << std::dec << " assigned");
757 return false;
758 }
759 }
760 }
761 }
762 return true;
763 }
764
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700765 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
766 if (mOptions.outputToDirectory) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800767 return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700768 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800769 return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700770 }
771 }
772
773 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
774 BigBuffer buffer(1024);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800775 TableFlattener flattener(&buffer);
Adam Lesinski6a008172016-02-02 17:02:58 -0800776 if (!flattener.consume(mContext, table)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700777 return false;
778 }
779
Adam Lesinskia40e9722015-11-24 19:11:46 -0800780 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
781 if (writer->writeEntry(buffer)) {
782 if (writer->finishEntry()) {
783 return true;
784 }
785 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700786 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800787
Adam Lesinski6a008172016-02-02 17:02:58 -0800788 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800789 DiagMessage() << "failed to write resources.arsc to archive");
790 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700791 }
792
Adam Lesinski64587af2016-02-18 18:33:06 -0800793 bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
794 // Create the file/zip entry.
795 if (!writer->startEntry("resources.arsc.flat", 0)) {
796 mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
797 return false;
798 }
799
800 std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
801
802 // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
803 // interface.
804 {
805 google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
806
807 if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
808 mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
809 return false;
810 }
811 }
812
813 if (!writer->finishEntry()) {
814 mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
815 return false;
816 }
817 return true;
818 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700819
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700820 bool writeJavaFile(ResourceTable* table, const StringPiece& packageNameToGenerate,
821 const StringPiece& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700822 if (!mOptions.generateJavaClassPath) {
823 return true;
824 }
825
826 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700827 file::appendPath(&outPath, file::packageToPath(outPackage));
Adam Lesinski96917c22016-03-09 13:11:25 -0800828 if (!file::mkdirs(outPath)) {
829 mContext->getDiagnostics()->error(
830 DiagMessage() << "failed to create directory '" << outPath << "'");
831 return false;
832 }
833
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700834 file::appendPath(&outPath, "R.java");
835
836 std::ofstream fout(outPath, std::ofstream::binary);
837 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800838 mContext->getDiagnostics()->error(
839 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700840 return false;
841 }
842
Adam Lesinski76565542016-03-10 21:55:04 -0800843 JavaClassGenerator generator(mContext, table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700844 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800845 mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700846 return false;
847 }
Adam Lesinski96917c22016-03-09 13:11:25 -0800848
849 if (!fout) {
850 mContext->getDiagnostics()->error(
851 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
852 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700853 return true;
854 }
855
Adam Lesinski467f1712015-11-16 17:35:44 -0800856 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700857 if (!mOptions.generateJavaClassPath) {
858 return true;
859 }
860
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700861 std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
862 mContext->getDiagnostics(), manifestXml);
863
864 if (!manifestClass) {
865 // Something bad happened, but we already logged it, so exit.
866 return false;
867 }
868
869 if (manifestClass->empty()) {
870 // Empty Manifest class, no need to generate it.
871 return true;
872 }
873
Adam Lesinski3524a232016-04-01 19:19:24 -0700874 // Add any JavaDoc annotations to the generated class.
875 for (const std::string& annotation : mOptions.javadocAnnotations) {
876 std::string properAnnotation = "@";
877 properAnnotation += annotation;
878 manifestClass->getCommentBuilder()->appendComment(properAnnotation);
879 }
880
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700881 const std::string& packageUtf8 = mContext->getCompilationPackage();
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700882
Adam Lesinskica5638f2015-10-21 14:42:43 -0700883 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700884 file::appendPath(&outPath, file::packageToPath(packageUtf8));
885
Adam Lesinski96917c22016-03-09 13:11:25 -0800886 if (!file::mkdirs(outPath)) {
887 mContext->getDiagnostics()->error(
888 DiagMessage() << "failed to create directory '" << outPath << "'");
889 return false;
890 }
891
Adam Lesinskica5638f2015-10-21 14:42:43 -0700892 file::appendPath(&outPath, "Manifest.java");
893
894 std::ofstream fout(outPath, std::ofstream::binary);
895 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800896 mContext->getDiagnostics()->error(
897 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700898 return false;
899 }
900
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700901 if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800902 mContext->getDiagnostics()->error(
903 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700904 return false;
905 }
906 return true;
907 }
908
Rohit Agrawale49bb302016-04-22 12:27:55 -0700909 bool writeProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keepSet) {
910 if (!out) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700911 return true;
912 }
913
Rohit Agrawale49bb302016-04-22 12:27:55 -0700914 const std::string& outPath = out.value();
Adam Lesinski96917c22016-03-09 13:11:25 -0800915 std::ofstream fout(outPath, std::ofstream::binary);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700916 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800917 mContext->getDiagnostics()->error(
918 DiagMessage() << "failed to open '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700919 return false;
920 }
921
922 proguard::writeKeepSet(&fout, keepSet);
923 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800924 mContext->getDiagnostics()->error(
925 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700926 return false;
927 }
928 return true;
929 }
930
Adam Lesinski64587af2016-02-18 18:33:06 -0800931 std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
932 std::string* outError) {
933 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
934 input, outError);
935 if (!collection) {
936 return {};
937 }
938 return loadTablePbFromCollection(collection.get());
939 }
940
941 std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) {
942 io::IFile* file = collection->findFile("resources.arsc.flat");
943 if (!file) {
944 return {};
945 }
946
947 std::unique_ptr<io::IData> data = file->openAsData();
948 return loadTableFromPb(file->getSource(), data->data(), data->size(),
949 mContext->getDiagnostics());
950 }
951
952 bool mergeStaticLibrary(const std::string& input, bool override) {
953 if (mContext->verbose()) {
954 mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input);
955 }
956
957 std::string errorStr;
958 std::unique_ptr<io::ZipFileCollection> collection =
959 io::ZipFileCollection::create(input, &errorStr);
960 if (!collection) {
961 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
962 return false;
963 }
964
965 std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get());
966 if (!table) {
967 mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library");
968 return false;
969 }
970
971 ResourceTablePackage* pkg = table->findPackageById(0x7f);
972 if (!pkg) {
973 mContext->getDiagnostics()->error(DiagMessage(input)
974 << "static library has no package");
975 return false;
976 }
977
978 bool result;
979 if (mOptions.noStaticLibPackages) {
980 // Merge all resources as if they were in the compilation package. This is the old
981 // behaviour of aapt.
982
983 // Add the package to the set of --extra-packages so we emit an R.java for each
984 // library package.
985 if (!pkg->name.empty()) {
986 mOptions.extraJavaPackages.insert(pkg->name);
987 }
988
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700989 pkg->name = "";
Adam Lesinski64587af2016-02-18 18:33:06 -0800990 if (override) {
991 result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get());
992 } else {
993 result = mTableMerger->merge(Source(input), table.get(), collection.get());
994 }
995
996 } else {
997 // This is the proper way to merge libraries, where the package name is preserved
998 // and resource names are mangled.
999 result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(),
1000 collection.get());
1001 }
1002
1003 if (!result) {
1004 return false;
1005 }
1006
1007 // Make sure to move the collection into the set of IFileCollections.
1008 mCollections.push_back(std::move(collection));
Adam Lesinskifb48d292015-11-07 15:52:13 -08001009 return true;
1010 }
1011
Adam Lesinskia40e9722015-11-24 19:11:46 -08001012 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001013 if (mContext->verbose()) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001014 mContext->getDiagnostics()->note(DiagMessage() << "merging resource table "
1015 << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001016 }
1017
Adam Lesinskia40e9722015-11-24 19:11:46 -08001018 std::unique_ptr<io::IData> data = file->openAsData();
1019 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001020 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -08001021 << "failed to open file");
1022 return false;
1023 }
1024
Adam Lesinski355f2852016-02-13 20:26:45 -08001025 std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(),
1026 data->data(), data->size(),
1027 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001028 if (!table) {
1029 return false;
1030 }
1031
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001032 bool result = false;
1033 if (override) {
1034 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
1035 } else {
1036 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001037 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001038 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001039 }
1040
Adam Lesinski64587af2016-02-18 18:33:06 -08001041 bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001042 if (mContext->verbose()) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001043 mContext->getDiagnostics()->note(DiagMessage() << "merging compiled file "
1044 << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001045 }
1046
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001047 bool result = false;
Adam Lesinski64587af2016-02-18 18:33:06 -08001048 if (override) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001049 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -08001050 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001051 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -08001052 }
1053
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001054 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -08001055 return false;
1056 }
1057
1058 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001059 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -08001060 if (exportedSymbol.name.package.empty()) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001061 exportedSymbol.name.package = mContext->getCompilationPackage();
Adam Lesinskifb48d292015-11-07 15:52:13 -08001062 }
1063
1064 ResourceNameRef resName = exportedSymbol.name;
1065
Adam Lesinski6a008172016-02-02 17:02:58 -08001066 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
Adam Lesinskifb48d292015-11-07 15:52:13 -08001067 exportedSymbol.name);
1068 if (mangledName) {
1069 resName = mangledName.value();
1070 }
1071
1072 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -08001073 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinski64587af2016-02-18 18:33:06 -08001074 bool result = mFinalTable.addResourceAllowMangled(
1075 resName, ConfigDescription::defaultConfig(), std::string(), std::move(id),
1076 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001077 if (!result) {
1078 return false;
1079 }
1080 }
Adam Lesinskifb48d292015-11-07 15:52:13 -08001081 return true;
1082 }
1083
Adam Lesinskia40e9722015-11-24 19:11:46 -08001084 /**
Adam Lesinski64587af2016-02-18 18:33:06 -08001085 * Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
1086 * If override is true, conflicting resources are allowed to override each other, in order of
1087 * last seen.
1088 *
1089 * An io::IFileCollection is created from the ZIP file and added to the set of
1090 * io::IFileCollections that are open.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001091 */
1092 bool mergeArchive(const std::string& input, bool override) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001093 if (mContext->verbose()) {
1094 mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input);
1095 }
1096
Adam Lesinskia40e9722015-11-24 19:11:46 -08001097 std::string errorStr;
Adam Lesinski64587af2016-02-18 18:33:06 -08001098 std::unique_ptr<io::ZipFileCollection> collection =
1099 io::ZipFileCollection::create(input, &errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001100 if (!collection) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001101 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001102 return false;
1103 }
1104
1105 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001106 for (auto iter = collection->iterator(); iter->hasNext(); ) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001107 if (!mergeFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001108 error = true;
1109 }
1110 }
1111
1112 // Make sure to move the collection into the set of IFileCollections.
1113 mCollections.push_back(std::move(collection));
1114 return !error;
1115 }
1116
Adam Lesinski64587af2016-02-18 18:33:06 -08001117 /**
1118 * Takes a path to load and merge into the master ResourceTable. If override is true,
1119 * conflicting resources are allowed to override each other, in order of last seen.
1120 *
1121 * If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive
1122 * and the files within are merged individually.
1123 *
1124 * Otherwise the files is processed on its own.
1125 */
1126 bool mergePath(const std::string& path, bool override) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001127 if (util::stringEndsWith(path, ".flata") ||
1128 util::stringEndsWith(path, ".jar") ||
1129 util::stringEndsWith(path, ".jack") ||
1130 util::stringEndsWith(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001131 return mergeArchive(path, override);
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001132 } else if (util::stringEndsWith(path, ".apk")) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001133 return mergeStaticLibrary(path, override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001134 }
1135
1136 io::IFile* file = mFileCollection->insertFile(path);
Adam Lesinski64587af2016-02-18 18:33:06 -08001137 return mergeFile(file, override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001138 }
1139
Adam Lesinski64587af2016-02-18 18:33:06 -08001140 /**
1141 * Takes a file to load and merge into the master ResourceTable. If override is true,
1142 * conflicting resources are allowed to override each other, in order of last seen.
1143 *
1144 * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the
1145 * master ResourceTable. If the file ends with .flat, then it is treated like a compiled file
1146 * and the header data is read and merged into the final ResourceTable.
1147 *
1148 * All other file types are ignored. This is because these files could be coming from a zip,
1149 * where we could have other files like classes.dex.
1150 */
1151 bool mergeFile(io::IFile* file, bool override) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001152 const Source& src = file->getSource();
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001153 if (util::stringEndsWith(src.path, ".arsc.flat")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001154 return mergeResourceTable(file, override);
Adam Lesinski64587af2016-02-18 18:33:06 -08001155
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001156 } else if (util::stringEndsWith(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -08001157 // Try opening the file and looking for an Export header.
1158 std::unique_ptr<io::IData> data = file->openAsData();
1159 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001160 mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
Adam Lesinskia40e9722015-11-24 19:11:46 -08001161 return false;
1162 }
1163
1164 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
Adam Lesinski6a008172016-02-02 17:02:58 -08001165 src, data->data(), data->size(), mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -08001166 if (resourceFile) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001167 return mergeCompiledFile(file, resourceFile.get(), override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001168 }
Adam Lesinskic446a732016-01-21 11:04:46 -08001169 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001170 }
Adam Lesinski52364f72016-01-11 13:10:24 -08001171
Adam Lesinskic446a732016-01-21 11:04:46 -08001172 // Ignore non .flat files. This could be classes.dex or something else that happens
1173 // to be in an archive.
1174 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001175 }
1176
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001177 int run(const std::vector<std::string>& inputFiles) {
1178 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -08001179 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
Adam Lesinski6a008172016-02-02 17:02:58 -08001180 mContext->getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001181 if (!manifestXml) {
1182 return 1;
1183 }
1184
Adam Lesinskifb6312f2016-06-28 14:40:32 -07001185 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
1186 mContext->getDiagnostics())) {
1187 AppInfo& appInfo = maybeAppInfo.value();
1188 mContext->setCompilationPackage(appInfo.package);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001189 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001190 return 1;
1191 }
1192
Adam Lesinski64587af2016-02-18 18:33:06 -08001193 if (!util::isJavaPackageName(mContext->getCompilationPackage())) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001194 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001195 << "invalid package name '"
Adam Lesinski64587af2016-02-18 18:33:06 -08001196 << mContext->getCompilationPackage()
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001197 << "'");
1198 return 1;
1199 }
1200
Adam Lesinski64587af2016-02-18 18:33:06 -08001201 mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() });
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001202
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001203 if (mContext->getCompilationPackage() == "android") {
Adam Lesinski64587af2016-02-18 18:33:06 -08001204 mContext->setPackageId(0x01);
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001205 } else {
Adam Lesinski64587af2016-02-18 18:33:06 -08001206 mContext->setPackageId(0x7f);
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001207 }
1208
Adam Lesinski64587af2016-02-18 18:33:06 -08001209 if (!loadSymbolsFromIncludePaths()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001210 return 1;
1211 }
1212
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001213 TableMergerOptions tableMergerOptions;
1214 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
Adam Lesinski6a008172016-02-02 17:02:58 -08001215 mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -08001216
Adam Lesinski355f2852016-02-13 20:26:45 -08001217 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001218 mContext->getDiagnostics()->note(
Adam Lesinski64587af2016-02-18 18:33:06 -08001219 DiagMessage() << "linking package '" << mContext->getCompilationPackage()
1220 << "' with package ID " << std::hex
1221 << (int) mContext->getPackageId());
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001222 }
1223
Adam Lesinskifb48d292015-11-07 15:52:13 -08001224
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001225 for (const std::string& input : inputFiles) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001226 if (!mergePath(input, false)) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001227 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
Adam Lesinski467f1712015-11-16 17:35:44 -08001228 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001229 }
1230 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001231
Adam Lesinskifb48d292015-11-07 15:52:13 -08001232 for (const std::string& input : mOptions.overlayFiles) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001233 if (!mergePath(input, true)) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001234 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
Adam Lesinski467f1712015-11-16 17:35:44 -08001235 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001236 }
1237 }
1238
Adam Lesinskifb48d292015-11-07 15:52:13 -08001239 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001240 return 1;
1241 }
1242
1243 if (!mOptions.staticLib) {
1244 PrivateAttributeMover mover;
Adam Lesinski6a008172016-02-02 17:02:58 -08001245 if (!mover.consume(mContext, &mFinalTable)) {
1246 mContext->getDiagnostics()->error(
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001247 DiagMessage() << "failed moving private attributes");
1248 return 1;
1249 }
1250 }
1251
Adam Lesinski64587af2016-02-18 18:33:06 -08001252 if (!mOptions.staticLib) {
1253 // Assign IDs if we are building a regular app.
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001254 IdAssigner idAssigner(&mOptions.stableIdMap);
Adam Lesinski6a008172016-02-02 17:02:58 -08001255 if (!idAssigner.consume(mContext, &mFinalTable)) {
1256 mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001257 return 1;
1258 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001259
1260 // Now grab each ID and emit it as a file.
1261 if (mOptions.resourceIdMapPath) {
1262 for (auto& package : mFinalTable.packages) {
1263 for (auto& type : package->types) {
1264 for (auto& entry : type->entries) {
1265 ResourceName name(package->name, type->type, entry->name);
1266 // The IDs are guaranteed to exist.
1267 mOptions.stableIdMap[std::move(name)] = ResourceId(package->id.value(),
1268 type->id.value(),
1269 entry->id.value());
1270 }
1271 }
1272 }
1273
1274 if (!writeStableIdMapToPath(mContext->getDiagnostics(),
1275 mOptions.stableIdMap,
1276 mOptions.resourceIdMapPath.value())) {
1277 return 1;
1278 }
1279 }
Adam Lesinski64587af2016-02-18 18:33:06 -08001280 } else {
1281 // Static libs are merged with other apps, and ID collisions are bad, so verify that
1282 // no IDs have been set.
1283 if (!verifyNoIdsSet()) {
1284 return 1;
1285 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001286 }
1287
Adam Lesinski64587af2016-02-18 18:33:06 -08001288 // Add the names to mangle based on our source merge earlier.
1289 mContext->setNameManglerPolicy(NameManglerPolicy{
1290 mContext->getCompilationPackage(), mTableMerger->getMergedPackages() });
1291
1292 // Add our table to the symbol table.
1293 mContext->getExternalSymbols()->prependSource(
1294 util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001295
1296 {
1297 ReferenceLinker linker;
Adam Lesinski6a008172016-02-02 17:02:58 -08001298 if (!linker.consume(mContext, &mFinalTable)) {
1299 mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001300 return 1;
1301 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001302
Adam Lesinski64587af2016-02-18 18:33:06 -08001303 if (mOptions.staticLib) {
1304 if (!mOptions.products.empty()) {
1305 mContext->getDiagnostics()->warn(
1306 DiagMessage() << "can't select products when building static library");
1307 }
Adam Lesinski355f2852016-02-13 20:26:45 -08001308
Adam Lesinski64587af2016-02-18 18:33:06 -08001309 if (mOptions.tableSplitterOptions.configFilter != nullptr ||
1310 mOptions.tableSplitterOptions.preferredDensity) {
1311 mContext->getDiagnostics()->warn(
1312 DiagMessage() << "can't strip resources when building static library");
1313 }
1314 } else {
1315 ProductFilter productFilter(mOptions.products);
1316 if (!productFilter.consume(mContext, &mFinalTable)) {
1317 mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
1318 return 1;
1319 }
Adam Lesinski355f2852016-02-13 20:26:45 -08001320
Adam Lesinski64587af2016-02-18 18:33:06 -08001321 // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
1322 // level.
1323 TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
1324 if (!tableSplitter.verifySplitConstraints(mContext)) {
1325 return 1;
1326 }
1327 tableSplitter.splitTable(&mFinalTable);
1328 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001329 }
1330
1331 proguard::KeepSet proguardKeepSet;
Rohit Agrawale49bb302016-04-22 12:27:55 -07001332 proguard::KeepSet proguardMainDexKeepSet;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001333
1334 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
1335 if (!archiveWriter) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001336 mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001337 return 1;
1338 }
1339
Adam Lesinski467f1712015-11-16 17:35:44 -08001340 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001341 {
Adam Lesinski52364f72016-01-11 13:10:24 -08001342 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski6a008172016-02-02 17:02:58 -08001343 if (!manifestFixer.consume(mContext, manifestXml.get())) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001344 error = true;
1345 }
1346
Adam Lesinski467f1712015-11-16 17:35:44 -08001347 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
1348 // (aka, which package the AndroidManifest.xml is coming from).
1349 // So we give it a package name so it can see local resources.
Adam Lesinski64587af2016-02-18 18:33:06 -08001350 manifestXml->file.name.package = mContext->getCompilationPackage();
Adam Lesinski467f1712015-11-16 17:35:44 -08001351
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001352 XmlReferenceLinker manifestLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -08001353 if (manifestLinker.consume(mContext, manifestXml.get())) {
Rohit Agrawale49bb302016-04-22 12:27:55 -07001354 if (mOptions.generateProguardRulesPath &&
1355 !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
1356 manifestXml.get(),
1357 &proguardKeepSet)) {
1358 error = true;
1359 }
1360
1361 if (mOptions.generateMainDexProguardRulesPath &&
1362 !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
1363 manifestXml.get(),
1364 &proguardMainDexKeepSet,
1365 true)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001366 error = true;
1367 }
1368
Adam Lesinskica5638f2015-10-21 14:42:43 -07001369 if (mOptions.generateJavaClassPath) {
1370 if (!writeManifestJavaFile(manifestXml.get())) {
1371 error = true;
1372 }
1373 }
1374
Adam Lesinski355f2852016-02-13 20:26:45 -08001375 const bool keepRawValues = mOptions.staticLib;
1376 bool result = flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
1377 keepRawValues, archiveWriter.get(), mContext);
1378 if (!result) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001379 error = true;
1380 }
1381 } else {
1382 error = true;
1383 }
1384 }
1385
Adam Lesinski467f1712015-11-16 17:35:44 -08001386 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001387 mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
Adam Lesinski467f1712015-11-16 17:35:44 -08001388 return 1;
1389 }
1390
Adam Lesinski355f2852016-02-13 20:26:45 -08001391 ResourceFileFlattenerOptions fileFlattenerOptions;
1392 fileFlattenerOptions.keepRawValues = mOptions.staticLib;
1393 fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
1394 fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
1395 fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
Adam Lesinski626a69f2016-03-03 10:09:26 -08001396 fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
Rohit Agrawale49bb302016-04-22 12:27:55 -07001397 fileFlattenerOptions.updateProguardSpec =
1398 static_cast<bool>(mOptions.generateProguardRulesPath);
Adam Lesinski355f2852016-02-13 20:26:45 -08001399 ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001400
Adam Lesinski355f2852016-02-13 20:26:45 -08001401 if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001402 mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001403 return 1;
1404 }
1405
Adam Lesinski626a69f2016-03-03 10:09:26 -08001406 if (!mOptions.noAutoVersion) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001407 AutoVersioner versioner;
Adam Lesinski6a008172016-02-02 17:02:58 -08001408 if (!versioner.consume(mContext, &mFinalTable)) {
1409 mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001410 return 1;
1411 }
1412 }
1413
Adam Lesinskifb6312f2016-06-28 14:40:32 -07001414 Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
1415 mContext->getDiagnostics());
1416 if (maybeAppInfo && maybeAppInfo.value().minSdkVersion) {
1417 if (Maybe<int> maybeMinSdkVersion =
1418 ResourceUtils::tryParseSdkVersion(maybeAppInfo.value().minSdkVersion.value())) {
1419 mContext->setMinSdkVersion(maybeMinSdkVersion.value());
1420 }
1421 }
1422
1423 if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) {
1424 if (mContext->verbose()) {
1425 mContext->getDiagnostics()->note(
1426 DiagMessage() << "collapsing resource versions for minimum SDK "
1427 << mContext->getMinSdkVersion());
1428 }
1429
1430 VersionCollapser collapser;
1431 if (!collapser.consume(mContext, &mFinalTable)) {
1432 return 1;
1433 }
1434 }
1435
Adam Lesinski64587af2016-02-18 18:33:06 -08001436 if (mOptions.staticLib) {
1437 if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) {
1438 mContext->getDiagnostics()->error(DiagMessage()
1439 << "failed to write resources.arsc.flat");
1440 return 1;
1441 }
1442 } else {
1443 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
1444 mContext->getDiagnostics()->error(DiagMessage()
1445 << "failed to write resources.arsc");
1446 return 1;
1447 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001448 }
1449
1450 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001451 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -08001452 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
Adam Lesinski3524a232016-04-01 19:19:24 -07001453 options.javadocAnnotations = mOptions.javadocAnnotations;
Adam Lesinski52364f72016-01-11 13:10:24 -08001454
Adam Lesinskief9c5012016-01-22 14:09:53 -08001455 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001456 options.useFinal = false;
1457 }
1458
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001459 const StringPiece actualPackage = mContext->getCompilationPackage();
1460 StringPiece outputPackage = mContext->getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -08001461 if (mOptions.customJavaPackage) {
1462 // Override the output java package to the custom one.
1463 outputPackage = mOptions.customJavaPackage.value();
1464 }
Adam Lesinski83f22552015-11-07 11:51:23 -08001465
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001466 if (mOptions.privateSymbols) {
1467 // If we defined a private symbols package, we only emit Public symbols
1468 // to the original package, and private and public symbols to the private package.
1469
1470 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinski6a008172016-02-02 17:02:58 -08001471 if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -08001472 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001473 return 1;
1474 }
1475
1476 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -08001477 outputPackage = mOptions.privateSymbols.value();
1478 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001479
Adam Lesinskifb48d292015-11-07 15:52:13 -08001480 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -08001481 return 1;
1482 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001483
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001484 for (const std::string& extraPackage : mOptions.extraJavaPackages) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001485 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001486 return 1;
1487 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001488 }
1489 }
1490
Rohit Agrawale49bb302016-04-22 12:27:55 -07001491 if (!writeProguardFile(mOptions.generateProguardRulesPath, proguardKeepSet)) {
1492 return 1;
1493 }
1494
1495 if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath, proguardMainDexKeepSet)) {
1496 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001497 }
1498
Adam Lesinski355f2852016-02-13 20:26:45 -08001499 if (mContext->verbose()) {
1500 DebugPrintTableOptions debugPrintTableOptions;
1501 debugPrintTableOptions.showSources = true;
1502 Debug::printTable(&mFinalTable, debugPrintTableOptions);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001503 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001504 return 0;
1505 }
Adam Lesinskifb48d292015-11-07 15:52:13 -08001506
1507private:
1508 LinkOptions mOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -08001509 LinkContext* mContext;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001510 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001511
Adam Lesinskifb48d292015-11-07 15:52:13 -08001512 std::unique_ptr<TableMerger> mTableMerger;
1513
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001514 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinski64587af2016-02-18 18:33:06 -08001515 std::unique_ptr<io::FileCollection> mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001516
1517 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001518 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski64587af2016-02-18 18:33:06 -08001519
1520 // A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable
1521 // can use these.
1522 std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001523};
1524
1525int link(const std::vector<StringPiece>& args) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001526 LinkContext context;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001527 LinkOptions options;
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001528 std::vector<std::string> overlayArgList;
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001529 std::vector<std::string> extraJavaPackages;
Adam Lesinski6a008172016-02-02 17:02:58 -08001530 Maybe<std::string> configs;
Adam Lesinski355f2852016-02-13 20:26:45 -08001531 Maybe<std::string> preferredDensity;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001532 Maybe<std::string> productList;
Adam Lesinski8900aa82016-01-25 22:48:15 -08001533 bool legacyXFlag = false;
1534 bool requireLocalization = false;
Adam Lesinski64587af2016-02-18 18:33:06 -08001535 bool verbose = false;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001536 Maybe<std::string> stableIdFilePath;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001537 Flags flags = Flags()
1538 .requiredFlag("-o", "Output path", &options.outputPath)
1539 .requiredFlag("--manifest", "Path to the Android manifest to build",
1540 &options.manifestPath)
1541 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -08001542 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -08001543 "The last conflicting resource given takes precedence.",
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001544 &overlayArgList)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001545 .optionalFlag("--java", "Directory in which to generate R.java",
1546 &options.generateJavaClassPath)
1547 .optionalFlag("--proguard", "Output file for generated Proguard rules",
1548 &options.generateProguardRulesPath)
Rohit Agrawale49bb302016-04-22 12:27:55 -07001549 .optionalFlag("--proguard-main-dex",
1550 "Output file for generated Proguard rules for the main dex",
1551 &options.generateMainDexProguardRulesPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001552 .optionalSwitch("--no-auto-version",
1553 "Disables automatic style and layout SDK versioning",
1554 &options.noAutoVersion)
Adam Lesinski64587af2016-02-18 18:33:06 -08001555 .optionalSwitch("--no-version-vectors",
1556 "Disables automatic versioning of vector drawables. Use this only\n"
1557 "when building with vector drawable support library",
1558 &options.noVersionVectors)
Adam Lesinski8900aa82016-01-25 22:48:15 -08001559 .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
1560 &legacyXFlag)
1561 .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
1562 &requireLocalization)
Adam Lesinski6a008172016-02-02 17:02:58 -08001563 .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
1564 "is all configurations", &configs)
Adam Lesinski355f2852016-02-13 20:26:45 -08001565 .optionalFlag("--preferred-density",
1566 "Selects the closest matching density and strips out all others.",
1567 &preferredDensity)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001568 .optionalFlag("--product", "Comma separated list of product names to keep",
1569 &productList)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001570 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
1571 "by -o",
1572 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001573 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001574 "AndroidManifest.xml",
1575 &options.manifestFixerOptions.minSdkVersionDefault)
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001576 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001577 "AndroidManifest.xml",
1578 &options.manifestFixerOptions.targetSdkVersionDefault)
Adam Lesinski52364f72016-01-11 13:10:24 -08001579 .optionalFlag("--version-code", "Version code (integer) to inject into the "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001580 "AndroidManifest.xml if none is present",
1581 &options.manifestFixerOptions.versionCodeDefault)
Adam Lesinski52364f72016-01-11 13:10:24 -08001582 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001583 "if none is present",
1584 &options.manifestFixerOptions.versionNameDefault)
1585 .optionalSwitch("--static-lib", "Generate a static Android library",
1586 &options.staticLib)
Adam Lesinski64587af2016-02-18 18:33:06 -08001587 .optionalSwitch("--no-static-lib-packages",
1588 "Merge all library resources under the app's package",
1589 &options.noStaticLibPackages)
Adam Lesinskief9c5012016-01-22 14:09:53 -08001590 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
1591 "This is implied when --static-lib is specified.",
1592 &options.generateNonFinalIds)
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001593 .optionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
1594 &stableIdFilePath)
1595 .optionalFlag("--emit-ids", "Emit a file at the given path with a list of name to ID\n"
1596 "mappings, suitable for use with --stable-ids.",
1597 &options.resourceIdMapPath)
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001598 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001599 "private symbols.\n"
1600 "If not specified, public and private symbols will use the application's "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001601 "package name",
1602 &options.privateSymbols)
Adam Lesinski52364f72016-01-11 13:10:24 -08001603 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001604 &options.customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -08001605 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001606 "package names",
1607 &extraJavaPackages)
Adam Lesinski3524a232016-04-01 19:19:24 -07001608 .optionalFlagList("--add-javadoc-annotation", "Adds a JavaDoc annotation to all "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001609 "generated Java classes",
1610 &options.javadocAnnotations)
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001611 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001612 "overlays without <add-resource> tags",
1613 &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -08001614 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001615 &options.manifestFixerOptions.renameManifestPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -08001616 .optionalFlag("--rename-instrumentation-target-package",
1617 "Changes the name of the target package for instrumentation. Most useful "
1618 "when used\nin conjunction with --rename-manifest-package",
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001619 &options.manifestFixerOptions.renameInstrumentationTargetPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -08001620 .optionalFlagList("-0", "File extensions not to compress",
1621 &options.extensionsToNotCompress)
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001622 .optionalSwitch("-v", "Enables verbose logging",
1623 &verbose);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001624
1625 if (!flags.parse("aapt2 link", args, &std::cerr)) {
1626 return 1;
1627 }
1628
Adam Lesinskic51562c2016-04-28 11:12:38 -07001629 // Expand all argument-files passed into the command line. These start with '@'.
1630 std::vector<std::string> argList;
1631 for (const std::string& arg : flags.getArgs()) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001632 if (util::stringStartsWith(arg, "@")) {
Adam Lesinskic51562c2016-04-28 11:12:38 -07001633 const std::string path = arg.substr(1, arg.size() - 1);
1634 std::string error;
1635 if (!file::appendArgsFromFile(path, &argList, &error)) {
1636 context.getDiagnostics()->error(DiagMessage(path) << error);
1637 return 1;
1638 }
1639 } else {
1640 argList.push_back(arg);
1641 }
1642 }
1643
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001644 // Expand all argument-files passed to -R.
1645 for (const std::string& arg : overlayArgList) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001646 if (util::stringStartsWith(arg, "@")) {
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001647 const std::string path = arg.substr(1, arg.size() - 1);
1648 std::string error;
1649 if (!file::appendArgsFromFile(path, &options.overlayFiles, &error)) {
1650 context.getDiagnostics()->error(DiagMessage(path) << error);
1651 return 1;
1652 }
1653 } else {
1654 options.overlayFiles.push_back(arg);
1655 }
1656 }
1657
Adam Lesinski64587af2016-02-18 18:33:06 -08001658 if (verbose) {
1659 context.setVerbose(verbose);
1660 }
1661
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001662 // Populate the set of extra packages for which to generate R.java.
1663 for (std::string& extraPackage : extraJavaPackages) {
1664 // A given package can actually be a colon separated list of packages.
1665 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001666 options.extraJavaPackages.insert(package.toString());
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001667 }
1668 }
1669
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001670 if (productList) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001671 for (StringPiece product : util::tokenize(productList.value(), ',')) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001672 if (product != "" && product != "default") {
1673 options.products.insert(product.toString());
1674 }
1675 }
1676 }
1677
Adam Lesinski6a008172016-02-02 17:02:58 -08001678 AxisConfigFilter filter;
1679 if (configs) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001680 for (const StringPiece& configStr : util::tokenize(configs.value(), ',')) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001681 ConfigDescription config;
1682 LocaleValue lv;
1683 if (lv.initFromFilterString(configStr)) {
1684 lv.writeTo(&config);
1685 } else if (!ConfigDescription::parse(configStr, &config)) {
1686 context.getDiagnostics()->error(
1687 DiagMessage() << "invalid config '" << configStr << "' for -c option");
1688 return 1;
1689 }
1690
1691 if (config.density != 0) {
1692 context.getDiagnostics()->warn(
1693 DiagMessage() << "ignoring density '" << config << "' for -c option");
1694 } else {
1695 filter.addConfig(config);
1696 }
1697 }
1698
Adam Lesinski355f2852016-02-13 20:26:45 -08001699 options.tableSplitterOptions.configFilter = &filter;
1700 }
1701
1702 if (preferredDensity) {
1703 ConfigDescription preferredDensityConfig;
1704 if (!ConfigDescription::parse(preferredDensity.value(), &preferredDensityConfig)) {
1705 context.getDiagnostics()->error(DiagMessage() << "invalid density '"
1706 << preferredDensity.value()
1707 << "' for --preferred-density option");
1708 return 1;
1709 }
1710
1711 // Clear the version that can be automatically added.
1712 preferredDensityConfig.sdkVersion = 0;
1713
1714 if (preferredDensityConfig.diff(ConfigDescription::defaultConfig())
1715 != ConfigDescription::CONFIG_DENSITY) {
1716 context.getDiagnostics()->error(DiagMessage() << "invalid preferred density '"
1717 << preferredDensity.value() << "'. "
1718 << "Preferred density must only be a density value");
1719 return 1;
1720 }
1721 options.tableSplitterOptions.preferredDensity = preferredDensityConfig.density;
Adam Lesinski6a008172016-02-02 17:02:58 -08001722 }
1723
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001724 if (!options.staticLib && stableIdFilePath) {
1725 if (!loadStableIdMap(context.getDiagnostics(), stableIdFilePath.value(),
1726 &options.stableIdMap)) {
1727 return 1;
1728 }
1729 }
1730
Adam Lesinski9756dec2016-08-08 12:35:04 -07001731 // Populate some default no-compress extensions that are already compressed.
1732 options.extensionsToNotCompress.insert({
1733 ".jpg", ".jpeg", ".png", ".gif",
1734 ".wav", ".mp2", ".mp3", ".ogg", ".aac",
1735 ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
1736 ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
1737 ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
1738 ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
1739
Adam Lesinski626a69f2016-03-03 10:09:26 -08001740 // Turn off auto versioning for static-libs.
1741 if (options.staticLib) {
1742 options.noAutoVersion = true;
1743 options.noVersionVectors = true;
1744 }
1745
Adam Lesinski6a008172016-02-02 17:02:58 -08001746 LinkCommand cmd(&context, options);
Adam Lesinskic51562c2016-04-28 11:12:38 -07001747 return cmd.run(argList);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001748}
1749
1750} // namespace aapt