blob: acb0f38995cc3e7286f2bbcf998a1a0bf2fffa14 [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.
Alexandria Cornwallf6762fc2016-08-09 12:36:46 -0700391 const int minSdkVersion = mContext->getMinSdkVersion();
Adam Lesinski355f2852016-02-13 20:26:45 -0800392 for (int sdkLevel : xmlLinker.getSdkLevels()) {
Alexandria Cornwallf6762fc2016-08-09 12:36:46 -0700393 if (sdkLevel > minSdkVersion
394 && sdkLevel > outFileOp->xmlToFlatten->file.config.sdkVersion) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800395 if (!shouldGenerateVersionedResource(entry, outFileOp->xmlToFlatten->file.config,
396 sdkLevel)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800397 // If we shouldn't generate a versioned resource, stop checking.
398 break;
399 }
400
Adam Lesinski626a69f2016-03-03 10:09:26 -0800401 ResourceFile versionedFileDesc = outFileOp->xmlToFlatten->file;
Adam Lesinski64587af2016-02-18 18:33:06 -0800402 versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel;
Adam Lesinski355f2852016-02-13 20:26:45 -0800403
404 if (mContext->verbose()) {
405 mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
406 << "auto-versioning resource from config '"
Adam Lesinski626a69f2016-03-03 10:09:26 -0800407 << outFileOp->xmlToFlatten->file.config
408 << "' -> '"
Adam Lesinski355f2852016-02-13 20:26:45 -0800409 << versionedFileDesc.config << "'");
410 }
411
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700412 std::string genPath = ResourceUtils::buildResourceFileName(
413 versionedFileDesc, mContext->getNameMangler());
Adam Lesinski355f2852016-02-13 20:26:45 -0800414
415 bool added = table->addFileReferenceAllowMangled(versionedFileDesc.name,
416 versionedFileDesc.config,
417 versionedFileDesc.source,
418 genPath,
419 file,
420 mContext->getDiagnostics());
421 if (!added) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800422 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800423 }
424 break;
425 }
426 }
427 }
Adam Lesinski626a69f2016-03-03 10:09:26 -0800428 return true;
Adam Lesinski355f2852016-02-13 20:26:45 -0800429}
430
431/**
432 * Do not insert or remove any resources while executing in this function. It will
433 * corrupt the iteration order.
434 */
435bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiveWriter) {
436 bool error = false;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700437 std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> configSortedFiles;
Adam Lesinski355f2852016-02-13 20:26:45 -0800438
439 for (auto& pkg : table->packages) {
440 for (auto& type : pkg->types) {
441 // Sort by config and name, so that we get better locality in the zip file.
442 configSortedFiles.clear();
443 for (auto& entry : type->entries) {
444 // Iterate via indices because auto generated values can be inserted ahead of
445 // the value being processed.
446 for (size_t i = 0; i < entry->values.size(); i++) {
447 ResourceConfigValue* configValue = entry->values[i].get();
448
449 FileReference* fileRef = valueCast<FileReference>(configValue->value.get());
450 if (!fileRef) {
451 continue;
452 }
453
454 io::IFile* file = fileRef->file;
455 if (!file) {
456 mContext->getDiagnostics()->error(DiagMessage(fileRef->getSource())
457 << "file not found");
458 return false;
459 }
460
461 FileOperation fileOp;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700462 fileOp.dstPath = *fileRef->path;
Adam Lesinski355f2852016-02-13 20:26:45 -0800463
464 const StringPiece srcPath = file->getSource().path;
465 if (type->type != ResourceType::kRaw &&
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700466 (util::stringEndsWith(srcPath, ".xml.flat") ||
467 util::stringEndsWith(srcPath, ".xml"))) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800468 ResourceFile fileDesc;
469 fileDesc.config = configValue->config;
470 fileDesc.name = ResourceName(pkg->name, type->type, entry->name);
471 fileDesc.source = fileRef->getSource();
Adam Lesinski626a69f2016-03-03 10:09:26 -0800472 if (!linkAndVersionXmlFile(entry.get(), fileDesc, file, table, &fileOp)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800473 error = true;
474 continue;
475 }
476
477 } else {
478 fileOp.fileToCopy = file;
479 }
480
481 // NOTE(adamlesinski): Explicitly construct a StringPiece16 here, or else
482 // we end up copying the string in the std::make_pair() method, then creating
483 // a StringPiece16 from the copy, which would cause us to end up referencing
484 // garbage in the map.
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700485 const StringPiece entryName(entry->name);
Adam Lesinski355f2852016-02-13 20:26:45 -0800486 configSortedFiles[std::make_pair(configValue->config, entryName)] =
487 std::move(fileOp);
488 }
489 }
490
491 if (error) {
492 return false;
493 }
494
495 // Now flatten the sorted values.
496 for (auto& mapEntry : configSortedFiles) {
497 const ConfigDescription& config = mapEntry.first.first;
498 const FileOperation& fileOp = mapEntry.second;
499
500 if (fileOp.xmlToFlatten) {
501 Maybe<size_t> maxSdkLevel;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800502 if (!mOptions.noAutoVersion && !fileOp.skipVersion) {
Alexandria Cornwallf6762fc2016-08-09 12:36:46 -0700503 maxSdkLevel =
504 std::max<size_t>(
505 std::max<size_t>(config.sdkVersion, 1u),
506 mContext->getMinSdkVersion());
Adam Lesinski355f2852016-02-13 20:26:45 -0800507 }
508
509 bool result = flattenXml(fileOp.xmlToFlatten.get(), fileOp.dstPath, maxSdkLevel,
510 mOptions.keepRawValues,
511 archiveWriter, mContext);
512 if (!result) {
513 error = true;
514 }
515 } else {
516 bool result = copyFileToArchive(fileOp.fileToCopy, fileOp.dstPath,
517 getCompressionFlags(fileOp.dstPath),
518 archiveWriter, mContext);
519 if (!result) {
520 error = true;
521 }
522 }
523 }
524 }
525 }
526 return !error;
527}
528
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -0700529static bool writeStableIdMapToPath(IDiagnostics* diag,
530 const std::unordered_map<ResourceName, ResourceId>& idMap,
531 const std::string idMapPath) {
532 std::ofstream fout(idMapPath, std::ofstream::binary);
533 if (!fout) {
534 diag->error(DiagMessage(idMapPath) << strerror(errno));
535 return false;
536 }
537
538 for (const auto& entry : idMap) {
539 const ResourceName& name = entry.first;
540 const ResourceId& id = entry.second;
541 fout << name << " = " << id << "\n";
542 }
543
544 if (!fout) {
545 diag->error(DiagMessage(idMapPath) << "failed writing to file: " << strerror(errno));
546 return false;
547 }
548
549 return true;
550}
551
552static bool loadStableIdMap(IDiagnostics* diag, const std::string& path,
553 std::unordered_map<ResourceName, ResourceId>* outIdMap) {
554 std::string content;
555 if (!android::base::ReadFileToString(path, &content)) {
556 diag->error(DiagMessage(path) << "failed reading stable ID file");
557 return false;
558 }
559
560 outIdMap->clear();
561 size_t lineNo = 0;
562 for (StringPiece line : util::tokenize(content, '\n')) {
563 lineNo++;
564 line = util::trimWhitespace(line);
565 if (line.empty()) {
566 continue;
567 }
568
569 auto iter = std::find(line.begin(), line.end(), '=');
570 if (iter == line.end()) {
571 diag->error(DiagMessage(Source(path, lineNo)) << "missing '='");
572 return false;
573 }
574
575 ResourceNameRef name;
576 StringPiece resNameStr = util::trimWhitespace(
577 line.substr(0, std::distance(line.begin(), iter)));
578 if (!ResourceUtils::parseResourceName(resNameStr, &name)) {
579 diag->error(DiagMessage(Source(path, lineNo))
580 << "invalid resource name '" << resNameStr << "'");
581 return false;
582 }
583
584 const size_t resIdStartIdx = std::distance(line.begin(), iter) + 1;
585 const size_t resIdStrLen = line.size() - resIdStartIdx;
586 StringPiece resIdStr = util::trimWhitespace(line.substr(resIdStartIdx, resIdStrLen));
587
588 Maybe<ResourceId> maybeId = ResourceUtils::tryParseResourceId(resIdStr);
589 if (!maybeId) {
590 diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource ID '"
591 << resIdStr << "'");
592 return false;
593 }
594
595 (*outIdMap)[name.toResourceName()] = maybeId.value();
596 }
597 return true;
598}
599
Adam Lesinskifb48d292015-11-07 15:52:13 -0800600class LinkCommand {
601public:
Adam Lesinski6a008172016-02-02 17:02:58 -0800602 LinkCommand(LinkContext* context, const LinkOptions& options) :
Adam Lesinski64587af2016-02-18 18:33:06 -0800603 mOptions(options), mContext(context), mFinalTable(),
604 mFileCollection(util::make_unique<io::FileCollection>()) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800605 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700606
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700607 /**
608 * Creates a SymbolTable that loads symbols from the various APKs and caches the
609 * results for faster lookup.
610 */
Adam Lesinski64587af2016-02-18 18:33:06 -0800611 bool loadSymbolsFromIncludePaths() {
612 std::unique_ptr<AssetManagerSymbolSource> assetSource =
613 util::make_unique<AssetManagerSymbolSource>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700614 for (const std::string& path : mOptions.includePaths) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800615 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800616 mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700617 }
618
Adam Lesinski64587af2016-02-18 18:33:06 -0800619 // First try to load the file as a static lib.
620 std::string errorStr;
621 std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr);
622 if (staticInclude) {
623 if (!mOptions.staticLib) {
624 // Can't include static libraries when not building a static library.
625 mContext->getDiagnostics()->error(
626 DiagMessage(path) << "can't include static library when building app");
627 return false;
628 }
629
630 // If we are using --no-static-lib-packages, we need to rename the package of this
631 // table to our compilation package.
632 if (mOptions.noStaticLibPackages) {
633 if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) {
634 pkg->name = mContext->getCompilationPackage();
635 }
636 }
637
638 mContext->getExternalSymbols()->appendSource(
639 util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
640
641 mStaticTableIncludes.push_back(std::move(staticInclude));
642
643 } else if (!errorStr.empty()) {
644 // We had an error with reading, so fail.
645 mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
646 return false;
647 }
648
649 if (!assetSource->addAssetPath(path)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800650 mContext->getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800651 DiagMessage(path) << "failed to load include path");
Adam Lesinski64587af2016-02-18 18:33:06 -0800652 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700653 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700654 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800655
656 mContext->getExternalSymbols()->appendSource(std::move(assetSource));
657 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700658 }
659
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700660 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes, IDiagnostics* diag) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700661 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800662 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700663 AppInfo appInfo;
664
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700665 if (!manifestEl->namespaceUri.empty() || manifestEl->name != "manifest") {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700666 diag->error(DiagMessage(xmlRes->file.source) << "root tag must be <manifest>");
667 return {};
668 }
669
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700670 xml::Attribute* packageAttr = manifestEl->findAttribute({}, "package");
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700671 if (!packageAttr) {
672 diag->error(DiagMessage(xmlRes->file.source)
673 << "<manifest> must have a 'package' attribute");
674 return {};
675 }
676
677 appInfo.package = packageAttr->value;
678
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700679 if (xml::Element* usesSdkEl = manifestEl->findChild({}, "uses-sdk")) {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700680 if (xml::Attribute* minSdk =
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700681 usesSdkEl->findAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700682 appInfo.minSdkVersion = minSdk->value;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700683 }
684 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700685
686 return appInfo;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700687 }
688 return {};
689 }
690
Adam Lesinski979ccb22016-01-11 10:42:19 -0800691 /**
692 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
693 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
694 * is an error and false is returned.
695 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800696 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800697 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800698 return mContext->getCompilationPackage() != pkg->name ||
Adam Lesinski979ccb22016-01-11 10:42:19 -0800699 !pkg->id ||
Adam Lesinski6a008172016-02-02 17:02:58 -0800700 pkg->id.value() != mContext->getPackageId();
Adam Lesinski979ccb22016-01-11 10:42:19 -0800701 };
702
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700703 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800704 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800705 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700706 // We have a package that is not related to the one we're building!
707 for (const auto& type : package->types) {
708 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800709 ResourceNameRef resName(package->name, type->type, entry->name);
710
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700711 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800712 // Special case the occurrence of an ID that is being generated for the
713 // 'android' package. This is due to legacy reasons.
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800714 if (valueCast<Id>(configValue->value.get()) &&
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700715 package->name == "android") {
Adam Lesinski6a008172016-02-02 17:02:58 -0800716 mContext->getDiagnostics()->warn(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800717 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800718 << "generated id '" << resName
719 << "' for external package '" << package->name
720 << "'");
721 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800722 mContext->getDiagnostics()->error(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800723 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800724 << "defined resource '" << resName
725 << "' for external package '" << package->name
726 << "'");
727 error = true;
728 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700729 }
730 }
731 }
732 }
733 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800734
735 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
736 isExtPackageFunc);
737 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700738 return !error;
739 }
740
Adam Lesinski64587af2016-02-18 18:33:06 -0800741 /**
742 * Returns true if no IDs have been set, false otherwise.
743 */
744 bool verifyNoIdsSet() {
745 for (const auto& package : mFinalTable.packages) {
746 for (const auto& type : package->types) {
747 if (type->id) {
748 mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type
749 << " has ID " << std::hex
750 << (int) type->id.value()
751 << std::dec << " assigned");
752 return false;
753 }
754
755 for (const auto& entry : type->entries) {
756 if (entry->id) {
757 ResourceNameRef resName(package->name, type->type, entry->name);
758 mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName
759 << " has ID " << std::hex
760 << (int) entry->id.value()
761 << std::dec << " assigned");
762 return false;
763 }
764 }
765 }
766 }
767 return true;
768 }
769
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700770 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
771 if (mOptions.outputToDirectory) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800772 return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700773 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800774 return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700775 }
776 }
777
778 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
779 BigBuffer buffer(1024);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800780 TableFlattener flattener(&buffer);
Adam Lesinski6a008172016-02-02 17:02:58 -0800781 if (!flattener.consume(mContext, table)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700782 return false;
783 }
784
Adam Lesinskia40e9722015-11-24 19:11:46 -0800785 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
786 if (writer->writeEntry(buffer)) {
787 if (writer->finishEntry()) {
788 return true;
789 }
790 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700791 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800792
Adam Lesinski6a008172016-02-02 17:02:58 -0800793 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800794 DiagMessage() << "failed to write resources.arsc to archive");
795 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700796 }
797
Adam Lesinski64587af2016-02-18 18:33:06 -0800798 bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
799 // Create the file/zip entry.
800 if (!writer->startEntry("resources.arsc.flat", 0)) {
801 mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
802 return false;
803 }
804
805 std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
806
807 // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
808 // interface.
809 {
810 google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
811
812 if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
813 mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
814 return false;
815 }
816 }
817
818 if (!writer->finishEntry()) {
819 mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
820 return false;
821 }
822 return true;
823 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700824
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700825 bool writeJavaFile(ResourceTable* table, const StringPiece& packageNameToGenerate,
826 const StringPiece& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700827 if (!mOptions.generateJavaClassPath) {
828 return true;
829 }
830
831 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700832 file::appendPath(&outPath, file::packageToPath(outPackage));
Adam Lesinski96917c22016-03-09 13:11:25 -0800833 if (!file::mkdirs(outPath)) {
834 mContext->getDiagnostics()->error(
835 DiagMessage() << "failed to create directory '" << outPath << "'");
836 return false;
837 }
838
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700839 file::appendPath(&outPath, "R.java");
840
841 std::ofstream fout(outPath, std::ofstream::binary);
842 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800843 mContext->getDiagnostics()->error(
844 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700845 return false;
846 }
847
Adam Lesinski76565542016-03-10 21:55:04 -0800848 JavaClassGenerator generator(mContext, table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700849 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800850 mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700851 return false;
852 }
Adam Lesinski96917c22016-03-09 13:11:25 -0800853
854 if (!fout) {
855 mContext->getDiagnostics()->error(
856 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
857 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700858 return true;
859 }
860
Adam Lesinski467f1712015-11-16 17:35:44 -0800861 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700862 if (!mOptions.generateJavaClassPath) {
863 return true;
864 }
865
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700866 std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
867 mContext->getDiagnostics(), manifestXml);
868
869 if (!manifestClass) {
870 // Something bad happened, but we already logged it, so exit.
871 return false;
872 }
873
874 if (manifestClass->empty()) {
875 // Empty Manifest class, no need to generate it.
876 return true;
877 }
878
Adam Lesinski3524a232016-04-01 19:19:24 -0700879 // Add any JavaDoc annotations to the generated class.
880 for (const std::string& annotation : mOptions.javadocAnnotations) {
881 std::string properAnnotation = "@";
882 properAnnotation += annotation;
883 manifestClass->getCommentBuilder()->appendComment(properAnnotation);
884 }
885
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700886 const std::string& packageUtf8 = mContext->getCompilationPackage();
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700887
Adam Lesinskica5638f2015-10-21 14:42:43 -0700888 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700889 file::appendPath(&outPath, file::packageToPath(packageUtf8));
890
Adam Lesinski96917c22016-03-09 13:11:25 -0800891 if (!file::mkdirs(outPath)) {
892 mContext->getDiagnostics()->error(
893 DiagMessage() << "failed to create directory '" << outPath << "'");
894 return false;
895 }
896
Adam Lesinskica5638f2015-10-21 14:42:43 -0700897 file::appendPath(&outPath, "Manifest.java");
898
899 std::ofstream fout(outPath, std::ofstream::binary);
900 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800901 mContext->getDiagnostics()->error(
902 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700903 return false;
904 }
905
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700906 if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800907 mContext->getDiagnostics()->error(
908 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700909 return false;
910 }
911 return true;
912 }
913
Rohit Agrawale49bb302016-04-22 12:27:55 -0700914 bool writeProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keepSet) {
915 if (!out) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700916 return true;
917 }
918
Rohit Agrawale49bb302016-04-22 12:27:55 -0700919 const std::string& outPath = out.value();
Adam Lesinski96917c22016-03-09 13:11:25 -0800920 std::ofstream fout(outPath, std::ofstream::binary);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700921 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800922 mContext->getDiagnostics()->error(
923 DiagMessage() << "failed to open '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700924 return false;
925 }
926
927 proguard::writeKeepSet(&fout, keepSet);
928 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800929 mContext->getDiagnostics()->error(
930 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700931 return false;
932 }
933 return true;
934 }
935
Adam Lesinski64587af2016-02-18 18:33:06 -0800936 std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
937 std::string* outError) {
938 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
939 input, outError);
940 if (!collection) {
941 return {};
942 }
943 return loadTablePbFromCollection(collection.get());
944 }
945
946 std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) {
947 io::IFile* file = collection->findFile("resources.arsc.flat");
948 if (!file) {
949 return {};
950 }
951
952 std::unique_ptr<io::IData> data = file->openAsData();
953 return loadTableFromPb(file->getSource(), data->data(), data->size(),
954 mContext->getDiagnostics());
955 }
956
957 bool mergeStaticLibrary(const std::string& input, bool override) {
958 if (mContext->verbose()) {
959 mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input);
960 }
961
962 std::string errorStr;
963 std::unique_ptr<io::ZipFileCollection> collection =
964 io::ZipFileCollection::create(input, &errorStr);
965 if (!collection) {
966 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
967 return false;
968 }
969
970 std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get());
971 if (!table) {
972 mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library");
973 return false;
974 }
975
976 ResourceTablePackage* pkg = table->findPackageById(0x7f);
977 if (!pkg) {
978 mContext->getDiagnostics()->error(DiagMessage(input)
979 << "static library has no package");
980 return false;
981 }
982
983 bool result;
984 if (mOptions.noStaticLibPackages) {
985 // Merge all resources as if they were in the compilation package. This is the old
986 // behaviour of aapt.
987
988 // Add the package to the set of --extra-packages so we emit an R.java for each
989 // library package.
990 if (!pkg->name.empty()) {
991 mOptions.extraJavaPackages.insert(pkg->name);
992 }
993
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700994 pkg->name = "";
Adam Lesinski64587af2016-02-18 18:33:06 -0800995 if (override) {
996 result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get());
997 } else {
998 result = mTableMerger->merge(Source(input), table.get(), collection.get());
999 }
1000
1001 } else {
1002 // This is the proper way to merge libraries, where the package name is preserved
1003 // and resource names are mangled.
1004 result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(),
1005 collection.get());
1006 }
1007
1008 if (!result) {
1009 return false;
1010 }
1011
1012 // Make sure to move the collection into the set of IFileCollections.
1013 mCollections.push_back(std::move(collection));
Adam Lesinskifb48d292015-11-07 15:52:13 -08001014 return true;
1015 }
1016
Adam Lesinskia40e9722015-11-24 19:11:46 -08001017 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001018 if (mContext->verbose()) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001019 mContext->getDiagnostics()->note(DiagMessage() << "merging resource table "
1020 << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001021 }
1022
Adam Lesinskia40e9722015-11-24 19:11:46 -08001023 std::unique_ptr<io::IData> data = file->openAsData();
1024 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001025 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -08001026 << "failed to open file");
1027 return false;
1028 }
1029
Adam Lesinski355f2852016-02-13 20:26:45 -08001030 std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(),
1031 data->data(), data->size(),
1032 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001033 if (!table) {
1034 return false;
1035 }
1036
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001037 bool result = false;
1038 if (override) {
1039 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
1040 } else {
1041 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001042 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001043 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001044 }
1045
Adam Lesinski64587af2016-02-18 18:33:06 -08001046 bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001047 if (mContext->verbose()) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001048 mContext->getDiagnostics()->note(DiagMessage() << "merging compiled file "
1049 << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001050 }
1051
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001052 bool result = false;
Adam Lesinski64587af2016-02-18 18:33:06 -08001053 if (override) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001054 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -08001055 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001056 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -08001057 }
1058
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001059 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -08001060 return false;
1061 }
1062
1063 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001064 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -08001065 if (exportedSymbol.name.package.empty()) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001066 exportedSymbol.name.package = mContext->getCompilationPackage();
Adam Lesinskifb48d292015-11-07 15:52:13 -08001067 }
1068
1069 ResourceNameRef resName = exportedSymbol.name;
1070
Adam Lesinski6a008172016-02-02 17:02:58 -08001071 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
Adam Lesinskifb48d292015-11-07 15:52:13 -08001072 exportedSymbol.name);
1073 if (mangledName) {
1074 resName = mangledName.value();
1075 }
1076
1077 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -08001078 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinski64587af2016-02-18 18:33:06 -08001079 bool result = mFinalTable.addResourceAllowMangled(
1080 resName, ConfigDescription::defaultConfig(), std::string(), std::move(id),
1081 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -08001082 if (!result) {
1083 return false;
1084 }
1085 }
Adam Lesinskifb48d292015-11-07 15:52:13 -08001086 return true;
1087 }
1088
Adam Lesinskia40e9722015-11-24 19:11:46 -08001089 /**
Adam Lesinski64587af2016-02-18 18:33:06 -08001090 * Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
1091 * If override is true, conflicting resources are allowed to override each other, in order of
1092 * last seen.
1093 *
1094 * An io::IFileCollection is created from the ZIP file and added to the set of
1095 * io::IFileCollections that are open.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001096 */
1097 bool mergeArchive(const std::string& input, bool override) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001098 if (mContext->verbose()) {
1099 mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input);
1100 }
1101
Adam Lesinskia40e9722015-11-24 19:11:46 -08001102 std::string errorStr;
Adam Lesinski64587af2016-02-18 18:33:06 -08001103 std::unique_ptr<io::ZipFileCollection> collection =
1104 io::ZipFileCollection::create(input, &errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001105 if (!collection) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001106 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001107 return false;
1108 }
1109
1110 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001111 for (auto iter = collection->iterator(); iter->hasNext(); ) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001112 if (!mergeFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001113 error = true;
1114 }
1115 }
1116
1117 // Make sure to move the collection into the set of IFileCollections.
1118 mCollections.push_back(std::move(collection));
1119 return !error;
1120 }
1121
Adam Lesinski64587af2016-02-18 18:33:06 -08001122 /**
1123 * Takes a path to load and merge into the master ResourceTable. If override is true,
1124 * conflicting resources are allowed to override each other, in order of last seen.
1125 *
1126 * If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive
1127 * and the files within are merged individually.
1128 *
1129 * Otherwise the files is processed on its own.
1130 */
1131 bool mergePath(const std::string& path, bool override) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001132 if (util::stringEndsWith(path, ".flata") ||
1133 util::stringEndsWith(path, ".jar") ||
1134 util::stringEndsWith(path, ".jack") ||
1135 util::stringEndsWith(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001136 return mergeArchive(path, override);
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001137 } else if (util::stringEndsWith(path, ".apk")) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001138 return mergeStaticLibrary(path, override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001139 }
1140
1141 io::IFile* file = mFileCollection->insertFile(path);
Adam Lesinski64587af2016-02-18 18:33:06 -08001142 return mergeFile(file, override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001143 }
1144
Adam Lesinski64587af2016-02-18 18:33:06 -08001145 /**
1146 * Takes a file to load and merge into the master ResourceTable. If override is true,
1147 * conflicting resources are allowed to override each other, in order of last seen.
1148 *
1149 * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the
1150 * master ResourceTable. If the file ends with .flat, then it is treated like a compiled file
1151 * and the header data is read and merged into the final ResourceTable.
1152 *
1153 * All other file types are ignored. This is because these files could be coming from a zip,
1154 * where we could have other files like classes.dex.
1155 */
1156 bool mergeFile(io::IFile* file, bool override) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001157 const Source& src = file->getSource();
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001158 if (util::stringEndsWith(src.path, ".arsc.flat")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001159 return mergeResourceTable(file, override);
Adam Lesinski64587af2016-02-18 18:33:06 -08001160
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001161 } else if (util::stringEndsWith(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -08001162 // Try opening the file and looking for an Export header.
1163 std::unique_ptr<io::IData> data = file->openAsData();
1164 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001165 mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
Adam Lesinskia40e9722015-11-24 19:11:46 -08001166 return false;
1167 }
1168
1169 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
Adam Lesinski6a008172016-02-02 17:02:58 -08001170 src, data->data(), data->size(), mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -08001171 if (resourceFile) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001172 return mergeCompiledFile(file, resourceFile.get(), override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001173 }
Adam Lesinskic446a732016-01-21 11:04:46 -08001174 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001175 }
Adam Lesinski52364f72016-01-11 13:10:24 -08001176
Adam Lesinskic446a732016-01-21 11:04:46 -08001177 // Ignore non .flat files. This could be classes.dex or something else that happens
1178 // to be in an archive.
1179 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001180 }
1181
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001182 int run(const std::vector<std::string>& inputFiles) {
1183 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -08001184 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
Adam Lesinski6a008172016-02-02 17:02:58 -08001185 mContext->getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001186 if (!manifestXml) {
1187 return 1;
1188 }
1189
Adam Lesinskifb6312f2016-06-28 14:40:32 -07001190 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
1191 mContext->getDiagnostics())) {
1192 AppInfo& appInfo = maybeAppInfo.value();
1193 mContext->setCompilationPackage(appInfo.package);
Alexandria Cornwall637b4822016-08-11 09:53:16 -07001194 if (appInfo.minSdkVersion) {
1195 if (Maybe<int> maybeMinSdkVersion =
1196 ResourceUtils::tryParseSdkVersion(appInfo.minSdkVersion.value())) {
1197 mContext->setMinSdkVersion(maybeMinSdkVersion.value());
1198 }
1199 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001200 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001201 return 1;
1202 }
1203
Adam Lesinski64587af2016-02-18 18:33:06 -08001204 if (!util::isJavaPackageName(mContext->getCompilationPackage())) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001205 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001206 << "invalid package name '"
Adam Lesinski64587af2016-02-18 18:33:06 -08001207 << mContext->getCompilationPackage()
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001208 << "'");
1209 return 1;
1210 }
1211
Adam Lesinski64587af2016-02-18 18:33:06 -08001212 mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() });
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001213
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001214 if (mContext->getCompilationPackage() == "android") {
Adam Lesinski64587af2016-02-18 18:33:06 -08001215 mContext->setPackageId(0x01);
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001216 } else {
Adam Lesinski64587af2016-02-18 18:33:06 -08001217 mContext->setPackageId(0x7f);
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001218 }
1219
Adam Lesinski64587af2016-02-18 18:33:06 -08001220 if (!loadSymbolsFromIncludePaths()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001221 return 1;
1222 }
1223
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001224 TableMergerOptions tableMergerOptions;
1225 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
Adam Lesinski6a008172016-02-02 17:02:58 -08001226 mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -08001227
Adam Lesinski355f2852016-02-13 20:26:45 -08001228 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001229 mContext->getDiagnostics()->note(
Adam Lesinski64587af2016-02-18 18:33:06 -08001230 DiagMessage() << "linking package '" << mContext->getCompilationPackage()
1231 << "' with package ID " << std::hex
1232 << (int) mContext->getPackageId());
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001233 }
1234
Adam Lesinskifb48d292015-11-07 15:52:13 -08001235
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001236 for (const std::string& input : inputFiles) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001237 if (!mergePath(input, false)) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001238 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
Adam Lesinski467f1712015-11-16 17:35:44 -08001239 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001240 }
1241 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001242
Adam Lesinskifb48d292015-11-07 15:52:13 -08001243 for (const std::string& input : mOptions.overlayFiles) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001244 if (!mergePath(input, true)) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001245 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
Adam Lesinski467f1712015-11-16 17:35:44 -08001246 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001247 }
1248 }
1249
Adam Lesinskifb48d292015-11-07 15:52:13 -08001250 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001251 return 1;
1252 }
1253
1254 if (!mOptions.staticLib) {
1255 PrivateAttributeMover mover;
Adam Lesinski6a008172016-02-02 17:02:58 -08001256 if (!mover.consume(mContext, &mFinalTable)) {
1257 mContext->getDiagnostics()->error(
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001258 DiagMessage() << "failed moving private attributes");
1259 return 1;
1260 }
1261 }
1262
Adam Lesinski64587af2016-02-18 18:33:06 -08001263 if (!mOptions.staticLib) {
1264 // Assign IDs if we are building a regular app.
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001265 IdAssigner idAssigner(&mOptions.stableIdMap);
Adam Lesinski6a008172016-02-02 17:02:58 -08001266 if (!idAssigner.consume(mContext, &mFinalTable)) {
1267 mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001268 return 1;
1269 }
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001270
1271 // Now grab each ID and emit it as a file.
1272 if (mOptions.resourceIdMapPath) {
1273 for (auto& package : mFinalTable.packages) {
1274 for (auto& type : package->types) {
1275 for (auto& entry : type->entries) {
1276 ResourceName name(package->name, type->type, entry->name);
1277 // The IDs are guaranteed to exist.
1278 mOptions.stableIdMap[std::move(name)] = ResourceId(package->id.value(),
1279 type->id.value(),
1280 entry->id.value());
1281 }
1282 }
1283 }
1284
1285 if (!writeStableIdMapToPath(mContext->getDiagnostics(),
1286 mOptions.stableIdMap,
1287 mOptions.resourceIdMapPath.value())) {
1288 return 1;
1289 }
1290 }
Adam Lesinski64587af2016-02-18 18:33:06 -08001291 } else {
1292 // Static libs are merged with other apps, and ID collisions are bad, so verify that
1293 // no IDs have been set.
1294 if (!verifyNoIdsSet()) {
1295 return 1;
1296 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001297 }
1298
Adam Lesinski64587af2016-02-18 18:33:06 -08001299 // Add the names to mangle based on our source merge earlier.
1300 mContext->setNameManglerPolicy(NameManglerPolicy{
1301 mContext->getCompilationPackage(), mTableMerger->getMergedPackages() });
1302
1303 // Add our table to the symbol table.
1304 mContext->getExternalSymbols()->prependSource(
1305 util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001306
1307 {
1308 ReferenceLinker linker;
Adam Lesinski6a008172016-02-02 17:02:58 -08001309 if (!linker.consume(mContext, &mFinalTable)) {
1310 mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001311 return 1;
1312 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001313
Adam Lesinski64587af2016-02-18 18:33:06 -08001314 if (mOptions.staticLib) {
1315 if (!mOptions.products.empty()) {
1316 mContext->getDiagnostics()->warn(
1317 DiagMessage() << "can't select products when building static library");
1318 }
Adam Lesinski355f2852016-02-13 20:26:45 -08001319
Adam Lesinski64587af2016-02-18 18:33:06 -08001320 if (mOptions.tableSplitterOptions.configFilter != nullptr ||
1321 mOptions.tableSplitterOptions.preferredDensity) {
1322 mContext->getDiagnostics()->warn(
1323 DiagMessage() << "can't strip resources when building static library");
1324 }
1325 } else {
1326 ProductFilter productFilter(mOptions.products);
1327 if (!productFilter.consume(mContext, &mFinalTable)) {
1328 mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
1329 return 1;
1330 }
Adam Lesinski355f2852016-02-13 20:26:45 -08001331
Adam Lesinski64587af2016-02-18 18:33:06 -08001332 // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
1333 // level.
1334 TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
1335 if (!tableSplitter.verifySplitConstraints(mContext)) {
1336 return 1;
1337 }
1338 tableSplitter.splitTable(&mFinalTable);
1339 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001340 }
1341
1342 proguard::KeepSet proguardKeepSet;
Rohit Agrawale49bb302016-04-22 12:27:55 -07001343 proguard::KeepSet proguardMainDexKeepSet;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001344
1345 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
1346 if (!archiveWriter) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001347 mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001348 return 1;
1349 }
1350
Adam Lesinski467f1712015-11-16 17:35:44 -08001351 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001352 {
Adam Lesinski52364f72016-01-11 13:10:24 -08001353 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski6a008172016-02-02 17:02:58 -08001354 if (!manifestFixer.consume(mContext, manifestXml.get())) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001355 error = true;
1356 }
1357
Adam Lesinski467f1712015-11-16 17:35:44 -08001358 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
1359 // (aka, which package the AndroidManifest.xml is coming from).
1360 // So we give it a package name so it can see local resources.
Adam Lesinski64587af2016-02-18 18:33:06 -08001361 manifestXml->file.name.package = mContext->getCompilationPackage();
Adam Lesinski467f1712015-11-16 17:35:44 -08001362
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001363 XmlReferenceLinker manifestLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -08001364 if (manifestLinker.consume(mContext, manifestXml.get())) {
Rohit Agrawale49bb302016-04-22 12:27:55 -07001365 if (mOptions.generateProguardRulesPath &&
1366 !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
1367 manifestXml.get(),
1368 &proguardKeepSet)) {
1369 error = true;
1370 }
1371
1372 if (mOptions.generateMainDexProguardRulesPath &&
1373 !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
1374 manifestXml.get(),
1375 &proguardMainDexKeepSet,
1376 true)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001377 error = true;
1378 }
1379
Adam Lesinskica5638f2015-10-21 14:42:43 -07001380 if (mOptions.generateJavaClassPath) {
1381 if (!writeManifestJavaFile(manifestXml.get())) {
1382 error = true;
1383 }
1384 }
1385
Adam Lesinski355f2852016-02-13 20:26:45 -08001386 const bool keepRawValues = mOptions.staticLib;
1387 bool result = flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
1388 keepRawValues, archiveWriter.get(), mContext);
1389 if (!result) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001390 error = true;
1391 }
1392 } else {
1393 error = true;
1394 }
1395 }
1396
Adam Lesinski467f1712015-11-16 17:35:44 -08001397 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001398 mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
Adam Lesinski467f1712015-11-16 17:35:44 -08001399 return 1;
1400 }
1401
Adam Lesinski626a69f2016-03-03 10:09:26 -08001402 if (!mOptions.noAutoVersion) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001403 AutoVersioner versioner;
Adam Lesinski6a008172016-02-02 17:02:58 -08001404 if (!versioner.consume(mContext, &mFinalTable)) {
1405 mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001406 return 1;
1407 }
1408 }
1409
Adam Lesinskifb6312f2016-06-28 14:40:32 -07001410 if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) {
1411 if (mContext->verbose()) {
1412 mContext->getDiagnostics()->note(
1413 DiagMessage() << "collapsing resource versions for minimum SDK "
1414 << mContext->getMinSdkVersion());
1415 }
1416
1417 VersionCollapser collapser;
1418 if (!collapser.consume(mContext, &mFinalTable)) {
1419 return 1;
1420 }
1421 }
1422
Alexandria Cornwall637b4822016-08-11 09:53:16 -07001423 // Write out the table to an archive. Optimizations to the table should come before this
1424 // step.
1425 ResourceFileFlattenerOptions fileFlattenerOptions;
1426 fileFlattenerOptions.keepRawValues = mOptions.staticLib;
1427 fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
1428 fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
1429 fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
1430 fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
1431 fileFlattenerOptions.updateProguardSpec =
1432 static_cast<bool>(mOptions.generateProguardRulesPath);
1433 ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet);
1434
1435 if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) {
1436 mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
1437 return 1;
1438 }
1439
Adam Lesinski64587af2016-02-18 18:33:06 -08001440 if (mOptions.staticLib) {
1441 if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) {
1442 mContext->getDiagnostics()->error(DiagMessage()
1443 << "failed to write resources.arsc.flat");
1444 return 1;
1445 }
1446 } else {
1447 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
1448 mContext->getDiagnostics()->error(DiagMessage()
1449 << "failed to write resources.arsc");
1450 return 1;
1451 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001452 }
1453
1454 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001455 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -08001456 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
Adam Lesinski3524a232016-04-01 19:19:24 -07001457 options.javadocAnnotations = mOptions.javadocAnnotations;
Adam Lesinski52364f72016-01-11 13:10:24 -08001458
Adam Lesinskief9c5012016-01-22 14:09:53 -08001459 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001460 options.useFinal = false;
1461 }
1462
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001463 const StringPiece actualPackage = mContext->getCompilationPackage();
1464 StringPiece outputPackage = mContext->getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -08001465 if (mOptions.customJavaPackage) {
1466 // Override the output java package to the custom one.
1467 outputPackage = mOptions.customJavaPackage.value();
1468 }
Adam Lesinski83f22552015-11-07 11:51:23 -08001469
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001470 if (mOptions.privateSymbols) {
1471 // If we defined a private symbols package, we only emit Public symbols
1472 // to the original package, and private and public symbols to the private package.
1473
1474 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinski6a008172016-02-02 17:02:58 -08001475 if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -08001476 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001477 return 1;
1478 }
1479
1480 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -08001481 outputPackage = mOptions.privateSymbols.value();
1482 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001483
Adam Lesinskifb48d292015-11-07 15:52:13 -08001484 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -08001485 return 1;
1486 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001487
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001488 for (const std::string& extraPackage : mOptions.extraJavaPackages) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001489 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001490 return 1;
1491 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001492 }
1493 }
1494
Rohit Agrawale49bb302016-04-22 12:27:55 -07001495 if (!writeProguardFile(mOptions.generateProguardRulesPath, proguardKeepSet)) {
1496 return 1;
1497 }
1498
1499 if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath, proguardMainDexKeepSet)) {
1500 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001501 }
1502
Adam Lesinski355f2852016-02-13 20:26:45 -08001503 if (mContext->verbose()) {
1504 DebugPrintTableOptions debugPrintTableOptions;
1505 debugPrintTableOptions.showSources = true;
1506 Debug::printTable(&mFinalTable, debugPrintTableOptions);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001507 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001508 return 0;
1509 }
Adam Lesinskifb48d292015-11-07 15:52:13 -08001510
1511private:
1512 LinkOptions mOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -08001513 LinkContext* mContext;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001514 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001515
Adam Lesinskifb48d292015-11-07 15:52:13 -08001516 std::unique_ptr<TableMerger> mTableMerger;
1517
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001518 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinski64587af2016-02-18 18:33:06 -08001519 std::unique_ptr<io::FileCollection> mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001520
1521 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001522 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski64587af2016-02-18 18:33:06 -08001523
1524 // A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable
1525 // can use these.
1526 std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001527};
1528
1529int link(const std::vector<StringPiece>& args) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001530 LinkContext context;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001531 LinkOptions options;
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001532 std::vector<std::string> overlayArgList;
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001533 std::vector<std::string> extraJavaPackages;
Adam Lesinski6a008172016-02-02 17:02:58 -08001534 Maybe<std::string> configs;
Adam Lesinski355f2852016-02-13 20:26:45 -08001535 Maybe<std::string> preferredDensity;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001536 Maybe<std::string> productList;
Adam Lesinski8900aa82016-01-25 22:48:15 -08001537 bool legacyXFlag = false;
1538 bool requireLocalization = false;
Adam Lesinski64587af2016-02-18 18:33:06 -08001539 bool verbose = false;
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001540 Maybe<std::string> stableIdFilePath;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001541 Flags flags = Flags()
1542 .requiredFlag("-o", "Output path", &options.outputPath)
1543 .requiredFlag("--manifest", "Path to the Android manifest to build",
1544 &options.manifestPath)
1545 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -08001546 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -08001547 "The last conflicting resource given takes precedence.",
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001548 &overlayArgList)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001549 .optionalFlag("--java", "Directory in which to generate R.java",
1550 &options.generateJavaClassPath)
1551 .optionalFlag("--proguard", "Output file for generated Proguard rules",
1552 &options.generateProguardRulesPath)
Rohit Agrawale49bb302016-04-22 12:27:55 -07001553 .optionalFlag("--proguard-main-dex",
1554 "Output file for generated Proguard rules for the main dex",
1555 &options.generateMainDexProguardRulesPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001556 .optionalSwitch("--no-auto-version",
1557 "Disables automatic style and layout SDK versioning",
1558 &options.noAutoVersion)
Adam Lesinski64587af2016-02-18 18:33:06 -08001559 .optionalSwitch("--no-version-vectors",
1560 "Disables automatic versioning of vector drawables. Use this only\n"
1561 "when building with vector drawable support library",
1562 &options.noVersionVectors)
Adam Lesinski8900aa82016-01-25 22:48:15 -08001563 .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
1564 &legacyXFlag)
1565 .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
1566 &requireLocalization)
Adam Lesinski6a008172016-02-02 17:02:58 -08001567 .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
1568 "is all configurations", &configs)
Adam Lesinski355f2852016-02-13 20:26:45 -08001569 .optionalFlag("--preferred-density",
1570 "Selects the closest matching density and strips out all others.",
1571 &preferredDensity)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001572 .optionalFlag("--product", "Comma separated list of product names to keep",
1573 &productList)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001574 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
1575 "by -o",
1576 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001577 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001578 "AndroidManifest.xml",
1579 &options.manifestFixerOptions.minSdkVersionDefault)
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001580 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001581 "AndroidManifest.xml",
1582 &options.manifestFixerOptions.targetSdkVersionDefault)
Adam Lesinski52364f72016-01-11 13:10:24 -08001583 .optionalFlag("--version-code", "Version code (integer) to inject into the "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001584 "AndroidManifest.xml if none is present",
1585 &options.manifestFixerOptions.versionCodeDefault)
Adam Lesinski52364f72016-01-11 13:10:24 -08001586 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001587 "if none is present",
1588 &options.manifestFixerOptions.versionNameDefault)
1589 .optionalSwitch("--static-lib", "Generate a static Android library",
1590 &options.staticLib)
Adam Lesinski64587af2016-02-18 18:33:06 -08001591 .optionalSwitch("--no-static-lib-packages",
1592 "Merge all library resources under the app's package",
1593 &options.noStaticLibPackages)
Adam Lesinskief9c5012016-01-22 14:09:53 -08001594 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
1595 "This is implied when --static-lib is specified.",
1596 &options.generateNonFinalIds)
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001597 .optionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
1598 &stableIdFilePath)
1599 .optionalFlag("--emit-ids", "Emit a file at the given path with a list of name to ID\n"
1600 "mappings, suitable for use with --stable-ids.",
1601 &options.resourceIdMapPath)
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001602 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001603 "private symbols.\n"
1604 "If not specified, public and private symbols will use the application's "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001605 "package name",
1606 &options.privateSymbols)
Adam Lesinski52364f72016-01-11 13:10:24 -08001607 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001608 &options.customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -08001609 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001610 "package names",
1611 &extraJavaPackages)
Adam Lesinski3524a232016-04-01 19:19:24 -07001612 .optionalFlagList("--add-javadoc-annotation", "Adds a JavaDoc annotation to all "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001613 "generated Java classes",
1614 &options.javadocAnnotations)
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001615 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001616 "overlays without <add-resource> tags",
1617 &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -08001618 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001619 &options.manifestFixerOptions.renameManifestPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -08001620 .optionalFlag("--rename-instrumentation-target-package",
1621 "Changes the name of the target package for instrumentation. Most useful "
1622 "when used\nin conjunction with --rename-manifest-package",
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001623 &options.manifestFixerOptions.renameInstrumentationTargetPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -08001624 .optionalFlagList("-0", "File extensions not to compress",
1625 &options.extensionsToNotCompress)
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001626 .optionalSwitch("-v", "Enables verbose logging",
1627 &verbose);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001628
1629 if (!flags.parse("aapt2 link", args, &std::cerr)) {
1630 return 1;
1631 }
1632
Adam Lesinskic51562c2016-04-28 11:12:38 -07001633 // Expand all argument-files passed into the command line. These start with '@'.
1634 std::vector<std::string> argList;
1635 for (const std::string& arg : flags.getArgs()) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001636 if (util::stringStartsWith(arg, "@")) {
Adam Lesinskic51562c2016-04-28 11:12:38 -07001637 const std::string path = arg.substr(1, arg.size() - 1);
1638 std::string error;
1639 if (!file::appendArgsFromFile(path, &argList, &error)) {
1640 context.getDiagnostics()->error(DiagMessage(path) << error);
1641 return 1;
1642 }
1643 } else {
1644 argList.push_back(arg);
1645 }
1646 }
1647
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001648 // Expand all argument-files passed to -R.
1649 for (const std::string& arg : overlayArgList) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001650 if (util::stringStartsWith(arg, "@")) {
Adam Lesinski1e21ff02016-06-24 14:57:58 -07001651 const std::string path = arg.substr(1, arg.size() - 1);
1652 std::string error;
1653 if (!file::appendArgsFromFile(path, &options.overlayFiles, &error)) {
1654 context.getDiagnostics()->error(DiagMessage(path) << error);
1655 return 1;
1656 }
1657 } else {
1658 options.overlayFiles.push_back(arg);
1659 }
1660 }
1661
Adam Lesinski64587af2016-02-18 18:33:06 -08001662 if (verbose) {
1663 context.setVerbose(verbose);
1664 }
1665
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001666 // Populate the set of extra packages for which to generate R.java.
1667 for (std::string& extraPackage : extraJavaPackages) {
1668 // A given package can actually be a colon separated list of packages.
1669 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001670 options.extraJavaPackages.insert(package.toString());
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001671 }
1672 }
1673
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001674 if (productList) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001675 for (StringPiece product : util::tokenize(productList.value(), ',')) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001676 if (product != "" && product != "default") {
1677 options.products.insert(product.toString());
1678 }
1679 }
1680 }
1681
Adam Lesinski6a008172016-02-02 17:02:58 -08001682 AxisConfigFilter filter;
1683 if (configs) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -07001684 for (const StringPiece& configStr : util::tokenize(configs.value(), ',')) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001685 ConfigDescription config;
1686 LocaleValue lv;
1687 if (lv.initFromFilterString(configStr)) {
1688 lv.writeTo(&config);
1689 } else if (!ConfigDescription::parse(configStr, &config)) {
1690 context.getDiagnostics()->error(
1691 DiagMessage() << "invalid config '" << configStr << "' for -c option");
1692 return 1;
1693 }
1694
1695 if (config.density != 0) {
1696 context.getDiagnostics()->warn(
1697 DiagMessage() << "ignoring density '" << config << "' for -c option");
1698 } else {
1699 filter.addConfig(config);
1700 }
1701 }
1702
Adam Lesinski355f2852016-02-13 20:26:45 -08001703 options.tableSplitterOptions.configFilter = &filter;
1704 }
1705
1706 if (preferredDensity) {
1707 ConfigDescription preferredDensityConfig;
1708 if (!ConfigDescription::parse(preferredDensity.value(), &preferredDensityConfig)) {
1709 context.getDiagnostics()->error(DiagMessage() << "invalid density '"
1710 << preferredDensity.value()
1711 << "' for --preferred-density option");
1712 return 1;
1713 }
1714
1715 // Clear the version that can be automatically added.
1716 preferredDensityConfig.sdkVersion = 0;
1717
1718 if (preferredDensityConfig.diff(ConfigDescription::defaultConfig())
1719 != ConfigDescription::CONFIG_DENSITY) {
1720 context.getDiagnostics()->error(DiagMessage() << "invalid preferred density '"
1721 << preferredDensity.value() << "'. "
1722 << "Preferred density must only be a density value");
1723 return 1;
1724 }
1725 options.tableSplitterOptions.preferredDensity = preferredDensityConfig.density;
Adam Lesinski6a008172016-02-02 17:02:58 -08001726 }
1727
Adam Lesinskibf0bd0f2016-06-01 15:31:50 -07001728 if (!options.staticLib && stableIdFilePath) {
1729 if (!loadStableIdMap(context.getDiagnostics(), stableIdFilePath.value(),
1730 &options.stableIdMap)) {
1731 return 1;
1732 }
1733 }
1734
Adam Lesinski9756dec2016-08-08 12:35:04 -07001735 // Populate some default no-compress extensions that are already compressed.
1736 options.extensionsToNotCompress.insert({
1737 ".jpg", ".jpeg", ".png", ".gif",
1738 ".wav", ".mp2", ".mp3", ".ogg", ".aac",
1739 ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
1740 ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
1741 ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
1742 ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
1743
Adam Lesinski626a69f2016-03-03 10:09:26 -08001744 // Turn off auto versioning for static-libs.
1745 if (options.staticLib) {
1746 options.noAutoVersion = true;
1747 options.noVersionVectors = true;
1748 }
1749
Adam Lesinski6a008172016-02-02 17:02:58 -08001750 LinkCommand cmd(&context, options);
Adam Lesinskic51562c2016-04-28 11:12:38 -07001751 return cmd.run(argList);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001752}
1753
1754} // namespace aapt