blob: 4569d8ed7eb0841e5fb9330c5f37e6e3668fab9c [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "AppInfo.h"
18#include "Debug.h"
19#include "Flags.h"
Adam Lesinski6a008172016-02-02 17:02:58 -080020#include "Locale.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "NameMangler.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080022#include "ResourceUtils.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023#include "compile/IdAssigner.h"
Adam Lesinski6a008172016-02-02 17:02:58 -080024#include "filter/ConfigFilter.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070025#include "flatten/Archive.h"
26#include "flatten/TableFlattener.h"
27#include "flatten/XmlFlattener.h"
Adam Lesinskia40e9722015-11-24 19:11:46 -080028#include "io/FileSystem.h"
29#include "io/ZipArchive.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070030#include "java/JavaClassGenerator.h"
31#include "java/ManifestClassGenerator.h"
32#include "java/ProguardRules.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033#include "link/Linkers.h"
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -080034#include "link/ProductFilter.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080035#include "link/ReferenceLinker.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080036#include "link/ManifestFixer.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070037#include "link/TableMerger.h"
38#include "process/IResourceTableConsumer.h"
39#include "process/SymbolTable.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080040#include "proto/ProtoSerialize.h"
Adam Lesinski355f2852016-02-13 20:26:45 -080041#include "split/TableSplitter.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070042#include "unflatten/BinaryResourceParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043#include "util/Files.h"
44#include "util/StringPiece.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080045#include "xml/XmlDom.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046
Adam Lesinski59e04c62016-02-04 15:59:23 -080047#include <google/protobuf/io/coded_stream.h>
48
Adam Lesinski1ab598f2015-08-14 14:26:04 -070049#include <fstream>
50#include <sys/stat.h>
Adam Lesinski1ab598f2015-08-14 14:26:04 -070051#include <vector>
52
53namespace aapt {
54
55struct LinkOptions {
56 std::string outputPath;
57 std::string manifestPath;
58 std::vector<std::string> includePaths;
Adam Lesinskifb48d292015-11-07 15:52:13 -080059 std::vector<std::string> overlayFiles;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070060 Maybe<std::string> generateJavaClassPath;
Adam Lesinski52364f72016-01-11 13:10:24 -080061 Maybe<std::u16string> customJavaPackage;
62 std::set<std::u16string> extraJavaPackages;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070063 Maybe<std::string> generateProguardRulesPath;
Rohit Agrawale49bb302016-04-22 12:27:55 -070064 Maybe<std::string> generateMainDexProguardRulesPath;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070065 bool noAutoVersion = false;
Adam Lesinski64587af2016-02-18 18:33:06 -080066 bool noVersionVectors = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070067 bool staticLib = false;
Adam Lesinski64587af2016-02-18 18:33:06 -080068 bool noStaticLibPackages = false;
Adam Lesinskief9c5012016-01-22 14:09:53 -080069 bool generateNonFinalIds = false;
Adam Lesinski3524a232016-04-01 19:19:24 -070070 std::vector<std::string> javadocAnnotations;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070071 bool outputToDirectory = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -080072 bool autoAddOverlay = false;
Adam Lesinski52364f72016-01-11 13:10:24 -080073 bool doNotCompressAnything = false;
74 std::vector<std::string> extensionsToNotCompress;
Adam Lesinski9e10ac72015-10-16 14:37:48 -070075 Maybe<std::u16string> privateSymbols;
Adam Lesinski52364f72016-01-11 13:10:24 -080076 ManifestFixerOptions manifestFixerOptions;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -080077 std::unordered_set<std::string> products;
Adam Lesinski355f2852016-02-13 20:26:45 -080078 TableSplitterOptions tableSplitterOptions;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070079};
80
Adam Lesinski64587af2016-02-18 18:33:06 -080081class LinkContext : public IAaptContext {
82public:
83 LinkContext() : mNameMangler({}) {
84 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070085
86 IDiagnostics* getDiagnostics() override {
87 return &mDiagnostics;
88 }
89
90 NameMangler* getNameMangler() override {
Adam Lesinski64587af2016-02-18 18:33:06 -080091 return &mNameMangler;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070092 }
93
Adam Lesinski64587af2016-02-18 18:33:06 -080094 void setNameManglerPolicy(const NameManglerPolicy& policy) {
95 mNameMangler = NameMangler(policy);
96 }
97
98 const std::u16string& getCompilationPackage() override {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070099 return mCompilationPackage;
100 }
101
Adam Lesinski64587af2016-02-18 18:33:06 -0800102 void setCompilationPackage(const StringPiece16& packageName) {
103 mCompilationPackage = packageName.toString();
104 }
105
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700106 uint8_t getPackageId() override {
107 return mPackageId;
108 }
109
Adam Lesinski64587af2016-02-18 18:33:06 -0800110 void setPackageId(uint8_t id) {
111 mPackageId = id;
112 }
113
114 SymbolTable* getExternalSymbols() override {
115 return &mSymbols;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700116 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800117
118 bool verbose() override {
119 return mVerbose;
120 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800121
122 void setVerbose(bool val) {
123 mVerbose = val;
124 }
125
126private:
127 StdErrDiagnostics mDiagnostics;
128 NameMangler mNameMangler;
129 std::u16string mCompilationPackage;
130 uint8_t mPackageId = 0x0;
131 SymbolTable mSymbols;
132 bool mVerbose = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700133};
134
Adam Lesinski355f2852016-02-13 20:26:45 -0800135static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
136 uint32_t compressionFlags,
137 IArchiveWriter* writer, IAaptContext* context) {
138 std::unique_ptr<io::IData> data = file->openAsData();
139 if (!data) {
140 context->getDiagnostics()->error(DiagMessage(file->getSource())
141 << "failed to open file");
142 return false;
143 }
144
Adam Lesinski64587af2016-02-18 18:33:06 -0800145 const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
146 size_t bufferSize = data->size();
147
148 // If the file ends with .flat, we must strip off the CompiledFileHeader from it.
149 if (util::stringEndsWith<char>(file->getSource().path, ".flat")) {
150 CompiledFileInputStream inputStream(data->data(), data->size());
151 if (!inputStream.CompiledFile()) {
152 context->getDiagnostics()->error(DiagMessage(file->getSource())
153 << "invalid compiled file header");
154 return false;
155 }
156 buffer = reinterpret_cast<const uint8_t*>(inputStream.data());
157 bufferSize = inputStream.size();
Adam Lesinski355f2852016-02-13 20:26:45 -0800158 }
159
160 if (context->verbose()) {
161 context->getDiagnostics()->note(DiagMessage() << "writing " << outPath << " to archive");
162 }
163
164 if (writer->startEntry(outPath, compressionFlags)) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800165 if (writer->writeEntry(buffer, bufferSize)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800166 if (writer->finishEntry()) {
167 return true;
168 }
169 }
170 }
171
172 context->getDiagnostics()->error(DiagMessage() << "failed to write file " << outPath);
173 return false;
174}
175
176static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
177 bool keepRawValues, IArchiveWriter* writer, IAaptContext* context) {
178 BigBuffer buffer(1024);
179 XmlFlattenerOptions options = {};
180 options.keepRawValues = keepRawValues;
181 options.maxSdkLevel = maxSdkLevel;
182 XmlFlattener flattener(&buffer, options);
183 if (!flattener.consume(context, xmlRes)) {
184 return false;
185 }
186
187 if (context->verbose()) {
188 DiagMessage msg;
189 msg << "writing " << path << " to archive";
190 if (maxSdkLevel) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800191 msg << " maxSdkLevel=" << maxSdkLevel.value() << " keepRawValues=" << keepRawValues;
Adam Lesinski355f2852016-02-13 20:26:45 -0800192 }
193 context->getDiagnostics()->note(msg);
194 }
195
196 if (writer->startEntry(path, ArchiveEntry::kCompress)) {
197 if (writer->writeEntry(buffer)) {
198 if (writer->finishEntry()) {
199 return true;
200 }
201 }
202 }
203 context->getDiagnostics()->error(DiagMessage() << "failed to write " << path << " to archive");
204 return false;
205}
206
207/*static std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len,
208 IDiagnostics* diag) {
209 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
210 BinaryResourceParser parser(diag, table.get(), source, data, len);
211 if (!parser.parse()) {
212 return {};
213 }
214 return table;
215}*/
216
217static std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
218 const void* data, size_t len,
219 IDiagnostics* diag) {
220 pb::ResourceTable pbTable;
221 if (!pbTable.ParseFromArray(data, len)) {
222 diag->error(DiagMessage(source) << "invalid compiled table");
223 return {};
224 }
225
226 std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source, diag);
227 if (!table) {
228 return {};
229 }
230 return table;
231}
232
233/**
234 * Inflates an XML file from the source path.
235 */
236static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
237 std::ifstream fin(path, std::ifstream::binary);
238 if (!fin) {
239 diag->error(DiagMessage(path) << strerror(errno));
240 return {};
241 }
242 return xml::inflate(&fin, diag, Source(path));
243}
244
245static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(const Source& source,
246 const void* data, size_t len,
247 IDiagnostics* diag) {
248 CompiledFileInputStream inputStream(data, len);
249 if (!inputStream.CompiledFile()) {
250 diag->error(DiagMessage(source) << "invalid compiled file header");
251 return {};
252 }
253
254 const uint8_t* xmlData = reinterpret_cast<const uint8_t*>(inputStream.data());
255 const size_t xmlDataLen = inputStream.size();
256
257 std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(xmlData, xmlDataLen, diag, source);
258 if (!xmlRes) {
259 return {};
260 }
261 return xmlRes;
262}
263
264static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
265 const void* data, size_t len,
266 IDiagnostics* diag) {
267 CompiledFileInputStream inputStream(data, len);
268 const pb::CompiledFile* pbFile = inputStream.CompiledFile();
269 if (!pbFile) {
270 diag->error(DiagMessage(source) << "invalid compiled file header");
271 return {};
272 }
273
274 std::unique_ptr<ResourceFile> resFile = deserializeCompiledFileFromPb(*pbFile, source, diag);
275 if (!resFile) {
276 return {};
277 }
278 return resFile;
279}
280
281struct ResourceFileFlattenerOptions {
282 bool noAutoVersion = false;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800283 bool noVersionVectors = false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800284 bool keepRawValues = false;
285 bool doNotCompressAnything = false;
Rohit Agrawale49bb302016-04-22 12:27:55 -0700286 bool updateProguardSpec = false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800287 std::vector<std::string> extensionsToNotCompress;
288};
289
290class ResourceFileFlattener {
291public:
292 ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
293 IAaptContext* context, proguard::KeepSet* keepSet) :
294 mOptions(options), mContext(context), mKeepSet(keepSet) {
295 }
296
297 bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
298
299private:
300 struct FileOperation {
301 io::IFile* fileToCopy;
302 std::unique_ptr<xml::XmlResource> xmlToFlatten;
303 std::string dstPath;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800304 bool skipVersion = false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800305 };
306
307 uint32_t getCompressionFlags(const StringPiece& str);
308
Adam Lesinski626a69f2016-03-03 10:09:26 -0800309 bool linkAndVersionXmlFile(const ResourceEntry* entry, const ResourceFile& fileDesc,
310 io::IFile* file, ResourceTable* table, FileOperation* outFileOp);
Adam Lesinski355f2852016-02-13 20:26:45 -0800311
312 ResourceFileFlattenerOptions mOptions;
313 IAaptContext* mContext;
314 proguard::KeepSet* mKeepSet;
315};
316
317uint32_t ResourceFileFlattener::getCompressionFlags(const StringPiece& str) {
318 if (mOptions.doNotCompressAnything) {
319 return 0;
320 }
321
322 for (const std::string& extension : mOptions.extensionsToNotCompress) {
323 if (util::stringEndsWith<char>(str, extension)) {
324 return 0;
325 }
326 }
327 return ArchiveEntry::kCompress;
328}
329
Adam Lesinski626a69f2016-03-03 10:09:26 -0800330bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry,
331 const ResourceFile& fileDesc,
332 io::IFile* file,
333 ResourceTable* table,
334 FileOperation* outFileOp) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800335 const StringPiece srcPath = file->getSource().path;
336 if (mContext->verbose()) {
337 mContext->getDiagnostics()->note(DiagMessage() << "linking " << srcPath);
338 }
339
340 std::unique_ptr<io::IData> data = file->openAsData();
341 if (!data) {
342 mContext->getDiagnostics()->error(DiagMessage(file->getSource()) << "failed to open file");
Adam Lesinski626a69f2016-03-03 10:09:26 -0800343 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800344 }
345
Adam Lesinski355f2852016-02-13 20:26:45 -0800346 if (util::stringEndsWith<char>(srcPath, ".flat")) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800347 outFileOp->xmlToFlatten = loadBinaryXmlSkipFileExport(file->getSource(),
348 data->data(), data->size(),
349 mContext->getDiagnostics());
Adam Lesinski355f2852016-02-13 20:26:45 -0800350 } else {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800351 outFileOp->xmlToFlatten = xml::inflate(data->data(), data->size(),
352 mContext->getDiagnostics(),
353 file->getSource());
Adam Lesinski355f2852016-02-13 20:26:45 -0800354 }
355
Adam Lesinski626a69f2016-03-03 10:09:26 -0800356 if (!outFileOp->xmlToFlatten) {
357 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800358 }
359
360 // Copy the the file description header.
Adam Lesinski626a69f2016-03-03 10:09:26 -0800361 outFileOp->xmlToFlatten->file = fileDesc;
Adam Lesinski355f2852016-02-13 20:26:45 -0800362
363 XmlReferenceLinker xmlLinker;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800364 if (!xmlLinker.consume(mContext, outFileOp->xmlToFlatten.get())) {
365 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800366 }
367
Rohit Agrawale49bb302016-04-22 12:27:55 -0700368 if (mOptions.updateProguardSpec && !proguard::collectProguardRules(
369 outFileOp->xmlToFlatten->file.source, outFileOp->xmlToFlatten.get(), mKeepSet)) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800370 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800371 }
372
373 if (!mOptions.noAutoVersion) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800374 if (mOptions.noVersionVectors) {
375 // Skip this if it is a vector or animated-vector.
376 xml::Element* el = xml::findRootElement(outFileOp->xmlToFlatten.get());
377 if (el && el->namespaceUri.empty()) {
378 if (el->name == u"vector" || el->name == u"animated-vector") {
379 // We are NOT going to version this file.
380 outFileOp->skipVersion = true;
381 return true;
382 }
383 }
384 }
385
Adam Lesinski355f2852016-02-13 20:26:45 -0800386 // Find the first SDK level used that is higher than this defined config and
387 // not superseded by a lower or equal SDK level resource.
388 for (int sdkLevel : xmlLinker.getSdkLevels()) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800389 if (sdkLevel > outFileOp->xmlToFlatten->file.config.sdkVersion) {
390 if (!shouldGenerateVersionedResource(entry, outFileOp->xmlToFlatten->file.config,
391 sdkLevel)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800392 // If we shouldn't generate a versioned resource, stop checking.
393 break;
394 }
395
Adam Lesinski626a69f2016-03-03 10:09:26 -0800396 ResourceFile versionedFileDesc = outFileOp->xmlToFlatten->file;
Adam Lesinski64587af2016-02-18 18:33:06 -0800397 versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel;
Adam Lesinski355f2852016-02-13 20:26:45 -0800398
399 if (mContext->verbose()) {
400 mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
401 << "auto-versioning resource from config '"
Adam Lesinski626a69f2016-03-03 10:09:26 -0800402 << outFileOp->xmlToFlatten->file.config
403 << "' -> '"
Adam Lesinski355f2852016-02-13 20:26:45 -0800404 << versionedFileDesc.config << "'");
405 }
406
407 std::u16string genPath = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(
408 versionedFileDesc, mContext->getNameMangler()));
409
410 bool added = table->addFileReferenceAllowMangled(versionedFileDesc.name,
411 versionedFileDesc.config,
412 versionedFileDesc.source,
413 genPath,
414 file,
415 mContext->getDiagnostics());
416 if (!added) {
Adam Lesinski626a69f2016-03-03 10:09:26 -0800417 return false;
Adam Lesinski355f2852016-02-13 20:26:45 -0800418 }
419 break;
420 }
421 }
422 }
Adam Lesinski626a69f2016-03-03 10:09:26 -0800423 return true;
Adam Lesinski355f2852016-02-13 20:26:45 -0800424}
425
426/**
427 * Do not insert or remove any resources while executing in this function. It will
428 * corrupt the iteration order.
429 */
430bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiveWriter) {
431 bool error = false;
432 std::map<std::pair<ConfigDescription, StringPiece16>, FileOperation> configSortedFiles;
433
434 for (auto& pkg : table->packages) {
435 for (auto& type : pkg->types) {
436 // Sort by config and name, so that we get better locality in the zip file.
437 configSortedFiles.clear();
438 for (auto& entry : type->entries) {
439 // Iterate via indices because auto generated values can be inserted ahead of
440 // the value being processed.
441 for (size_t i = 0; i < entry->values.size(); i++) {
442 ResourceConfigValue* configValue = entry->values[i].get();
443
444 FileReference* fileRef = valueCast<FileReference>(configValue->value.get());
445 if (!fileRef) {
446 continue;
447 }
448
449 io::IFile* file = fileRef->file;
450 if (!file) {
451 mContext->getDiagnostics()->error(DiagMessage(fileRef->getSource())
452 << "file not found");
453 return false;
454 }
455
456 FileOperation fileOp;
457 fileOp.dstPath = util::utf16ToUtf8(*fileRef->path);
458
459 const StringPiece srcPath = file->getSource().path;
460 if (type->type != ResourceType::kRaw &&
461 (util::stringEndsWith<char>(srcPath, ".xml.flat") ||
462 util::stringEndsWith<char>(srcPath, ".xml"))) {
463 ResourceFile fileDesc;
464 fileDesc.config = configValue->config;
465 fileDesc.name = ResourceName(pkg->name, type->type, entry->name);
466 fileDesc.source = fileRef->getSource();
Adam Lesinski626a69f2016-03-03 10:09:26 -0800467 if (!linkAndVersionXmlFile(entry.get(), fileDesc, file, table, &fileOp)) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800468 error = true;
469 continue;
470 }
471
472 } else {
473 fileOp.fileToCopy = file;
474 }
475
476 // NOTE(adamlesinski): Explicitly construct a StringPiece16 here, or else
477 // we end up copying the string in the std::make_pair() method, then creating
478 // a StringPiece16 from the copy, which would cause us to end up referencing
479 // garbage in the map.
480 const StringPiece16 entryName(entry->name);
481 configSortedFiles[std::make_pair(configValue->config, entryName)] =
482 std::move(fileOp);
483 }
484 }
485
486 if (error) {
487 return false;
488 }
489
490 // Now flatten the sorted values.
491 for (auto& mapEntry : configSortedFiles) {
492 const ConfigDescription& config = mapEntry.first.first;
493 const FileOperation& fileOp = mapEntry.second;
494
495 if (fileOp.xmlToFlatten) {
496 Maybe<size_t> maxSdkLevel;
Adam Lesinski626a69f2016-03-03 10:09:26 -0800497 if (!mOptions.noAutoVersion && !fileOp.skipVersion) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800498 maxSdkLevel = std::max<size_t>(config.sdkVersion, 1u);
499 }
500
501 bool result = flattenXml(fileOp.xmlToFlatten.get(), fileOp.dstPath, maxSdkLevel,
502 mOptions.keepRawValues,
503 archiveWriter, mContext);
504 if (!result) {
505 error = true;
506 }
507 } else {
508 bool result = copyFileToArchive(fileOp.fileToCopy, fileOp.dstPath,
509 getCompressionFlags(fileOp.dstPath),
510 archiveWriter, mContext);
511 if (!result) {
512 error = true;
513 }
514 }
515 }
516 }
517 }
518 return !error;
519}
520
Adam Lesinskifb48d292015-11-07 15:52:13 -0800521class LinkCommand {
522public:
Adam Lesinski6a008172016-02-02 17:02:58 -0800523 LinkCommand(LinkContext* context, const LinkOptions& options) :
Adam Lesinski64587af2016-02-18 18:33:06 -0800524 mOptions(options), mContext(context), mFinalTable(),
525 mFileCollection(util::make_unique<io::FileCollection>()) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800526 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700527
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700528 /**
529 * Creates a SymbolTable that loads symbols from the various APKs and caches the
530 * results for faster lookup.
531 */
Adam Lesinski64587af2016-02-18 18:33:06 -0800532 bool loadSymbolsFromIncludePaths() {
533 std::unique_ptr<AssetManagerSymbolSource> assetSource =
534 util::make_unique<AssetManagerSymbolSource>();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700535 for (const std::string& path : mOptions.includePaths) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800536 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800537 mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700538 }
539
Adam Lesinski64587af2016-02-18 18:33:06 -0800540 // First try to load the file as a static lib.
541 std::string errorStr;
542 std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr);
543 if (staticInclude) {
544 if (!mOptions.staticLib) {
545 // Can't include static libraries when not building a static library.
546 mContext->getDiagnostics()->error(
547 DiagMessage(path) << "can't include static library when building app");
548 return false;
549 }
550
551 // If we are using --no-static-lib-packages, we need to rename the package of this
552 // table to our compilation package.
553 if (mOptions.noStaticLibPackages) {
554 if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) {
555 pkg->name = mContext->getCompilationPackage();
556 }
557 }
558
559 mContext->getExternalSymbols()->appendSource(
560 util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
561
562 mStaticTableIncludes.push_back(std::move(staticInclude));
563
564 } else if (!errorStr.empty()) {
565 // We had an error with reading, so fail.
566 mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
567 return false;
568 }
569
570 if (!assetSource->addAssetPath(path)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800571 mContext->getDiagnostics()->error(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800572 DiagMessage(path) << "failed to load include path");
Adam Lesinski64587af2016-02-18 18:33:06 -0800573 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700574 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700575 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800576
577 mContext->getExternalSymbols()->appendSource(std::move(assetSource));
578 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700579 }
580
Adam Lesinski467f1712015-11-16 17:35:44 -0800581 Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700582 // Make sure the first element is <manifest> with package attribute.
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800583 if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700584 if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
585 if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
586 return AppInfo{ packageAttr->value };
587 }
588 }
589 }
590 return {};
591 }
592
Adam Lesinski979ccb22016-01-11 10:42:19 -0800593 /**
594 * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
595 * Postcondition: ResourceTable has only one package left. All others are stripped, or there
596 * is an error and false is returned.
597 */
Adam Lesinskifb48d292015-11-07 15:52:13 -0800598 bool verifyNoExternalPackages() {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800599 auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
Adam Lesinski6a008172016-02-02 17:02:58 -0800600 return mContext->getCompilationPackage() != pkg->name ||
Adam Lesinski979ccb22016-01-11 10:42:19 -0800601 !pkg->id ||
Adam Lesinski6a008172016-02-02 17:02:58 -0800602 pkg->id.value() != mContext->getPackageId();
Adam Lesinski979ccb22016-01-11 10:42:19 -0800603 };
604
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700605 bool error = false;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800606 for (const auto& package : mFinalTable.packages) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800607 if (isExtPackageFunc(package)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700608 // We have a package that is not related to the one we're building!
609 for (const auto& type : package->types) {
610 for (const auto& entry : type->entries) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800611 ResourceNameRef resName(package->name, type->type, entry->name);
612
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700613 for (const auto& configValue : entry->values) {
Adam Lesinski979ccb22016-01-11 10:42:19 -0800614 // Special case the occurrence of an ID that is being generated for the
615 // 'android' package. This is due to legacy reasons.
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800616 if (valueCast<Id>(configValue->value.get()) &&
Adam Lesinski979ccb22016-01-11 10:42:19 -0800617 package->name == u"android") {
Adam Lesinski6a008172016-02-02 17:02:58 -0800618 mContext->getDiagnostics()->warn(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800619 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800620 << "generated id '" << resName
621 << "' for external package '" << package->name
622 << "'");
623 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800624 mContext->getDiagnostics()->error(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800625 DiagMessage(configValue->value->getSource())
Adam Lesinski979ccb22016-01-11 10:42:19 -0800626 << "defined resource '" << resName
627 << "' for external package '" << package->name
628 << "'");
629 error = true;
630 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700631 }
632 }
633 }
634 }
635 }
Adam Lesinski979ccb22016-01-11 10:42:19 -0800636
637 auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
638 isExtPackageFunc);
639 mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700640 return !error;
641 }
642
Adam Lesinski64587af2016-02-18 18:33:06 -0800643 /**
644 * Returns true if no IDs have been set, false otherwise.
645 */
646 bool verifyNoIdsSet() {
647 for (const auto& package : mFinalTable.packages) {
648 for (const auto& type : package->types) {
649 if (type->id) {
650 mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type
651 << " has ID " << std::hex
652 << (int) type->id.value()
653 << std::dec << " assigned");
654 return false;
655 }
656
657 for (const auto& entry : type->entries) {
658 if (entry->id) {
659 ResourceNameRef resName(package->name, type->type, entry->name);
660 mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName
661 << " has ID " << std::hex
662 << (int) entry->id.value()
663 << std::dec << " assigned");
664 return false;
665 }
666 }
667 }
668 }
669 return true;
670 }
671
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700672 std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
673 if (mOptions.outputToDirectory) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800674 return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700675 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -0800676 return createZipFileArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700677 }
678 }
679
680 bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
681 BigBuffer buffer(1024);
Adam Lesinski59e04c62016-02-04 15:59:23 -0800682 TableFlattener flattener(&buffer);
Adam Lesinski6a008172016-02-02 17:02:58 -0800683 if (!flattener.consume(mContext, table)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700684 return false;
685 }
686
Adam Lesinskia40e9722015-11-24 19:11:46 -0800687 if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
688 if (writer->writeEntry(buffer)) {
689 if (writer->finishEntry()) {
690 return true;
691 }
692 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700693 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800694
Adam Lesinski6a008172016-02-02 17:02:58 -0800695 mContext->getDiagnostics()->error(
Adam Lesinskia40e9722015-11-24 19:11:46 -0800696 DiagMessage() << "failed to write resources.arsc to archive");
697 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700698 }
699
Adam Lesinski64587af2016-02-18 18:33:06 -0800700 bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
701 // Create the file/zip entry.
702 if (!writer->startEntry("resources.arsc.flat", 0)) {
703 mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
704 return false;
705 }
706
707 std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
708
709 // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
710 // interface.
711 {
712 google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
713
714 if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
715 mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
716 return false;
717 }
718 }
719
720 if (!writer->finishEntry()) {
721 mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
722 return false;
723 }
724 return true;
725 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700726
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700727 bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
728 const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700729 if (!mOptions.generateJavaClassPath) {
730 return true;
731 }
732
733 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700734 file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
Adam Lesinski96917c22016-03-09 13:11:25 -0800735 if (!file::mkdirs(outPath)) {
736 mContext->getDiagnostics()->error(
737 DiagMessage() << "failed to create directory '" << outPath << "'");
738 return false;
739 }
740
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700741 file::appendPath(&outPath, "R.java");
742
743 std::ofstream fout(outPath, std::ofstream::binary);
744 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800745 mContext->getDiagnostics()->error(
746 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700747 return false;
748 }
749
Adam Lesinski76565542016-03-10 21:55:04 -0800750 JavaClassGenerator generator(mContext, table, javaOptions);
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700751 if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800752 mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700753 return false;
754 }
Adam Lesinski96917c22016-03-09 13:11:25 -0800755
756 if (!fout) {
757 mContext->getDiagnostics()->error(
758 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
759 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700760 return true;
761 }
762
Adam Lesinski467f1712015-11-16 17:35:44 -0800763 bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
Adam Lesinskica5638f2015-10-21 14:42:43 -0700764 if (!mOptions.generateJavaClassPath) {
765 return true;
766 }
767
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700768 std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
769 mContext->getDiagnostics(), manifestXml);
770
771 if (!manifestClass) {
772 // Something bad happened, but we already logged it, so exit.
773 return false;
774 }
775
776 if (manifestClass->empty()) {
777 // Empty Manifest class, no need to generate it.
778 return true;
779 }
780
Adam Lesinski3524a232016-04-01 19:19:24 -0700781 // Add any JavaDoc annotations to the generated class.
782 for (const std::string& annotation : mOptions.javadocAnnotations) {
783 std::string properAnnotation = "@";
784 properAnnotation += annotation;
785 manifestClass->getCommentBuilder()->appendComment(properAnnotation);
786 }
787
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700788 const std::string packageUtf8 = util::utf16ToUtf8(mContext->getCompilationPackage());
789
Adam Lesinskica5638f2015-10-21 14:42:43 -0700790 std::string outPath = mOptions.generateJavaClassPath.value();
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700791 file::appendPath(&outPath, file::packageToPath(packageUtf8));
792
Adam Lesinski96917c22016-03-09 13:11:25 -0800793 if (!file::mkdirs(outPath)) {
794 mContext->getDiagnostics()->error(
795 DiagMessage() << "failed to create directory '" << outPath << "'");
796 return false;
797 }
798
Adam Lesinskica5638f2015-10-21 14:42:43 -0700799 file::appendPath(&outPath, "Manifest.java");
800
801 std::ofstream fout(outPath, std::ofstream::binary);
802 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800803 mContext->getDiagnostics()->error(
804 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700805 return false;
806 }
807
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700808 if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800809 mContext->getDiagnostics()->error(
810 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinskica5638f2015-10-21 14:42:43 -0700811 return false;
812 }
813 return true;
814 }
815
Rohit Agrawale49bb302016-04-22 12:27:55 -0700816 bool writeProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keepSet) {
817 if (!out) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700818 return true;
819 }
820
Rohit Agrawale49bb302016-04-22 12:27:55 -0700821 const std::string& outPath = out.value();
Adam Lesinski96917c22016-03-09 13:11:25 -0800822 std::ofstream fout(outPath, std::ofstream::binary);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700823 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800824 mContext->getDiagnostics()->error(
825 DiagMessage() << "failed to open '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700826 return false;
827 }
828
829 proguard::writeKeepSet(&fout, keepSet);
830 if (!fout) {
Adam Lesinski96917c22016-03-09 13:11:25 -0800831 mContext->getDiagnostics()->error(
832 DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700833 return false;
834 }
835 return true;
836 }
837
Adam Lesinski64587af2016-02-18 18:33:06 -0800838 std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
839 std::string* outError) {
840 std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
841 input, outError);
842 if (!collection) {
843 return {};
844 }
845 return loadTablePbFromCollection(collection.get());
846 }
847
848 std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) {
849 io::IFile* file = collection->findFile("resources.arsc.flat");
850 if (!file) {
851 return {};
852 }
853
854 std::unique_ptr<io::IData> data = file->openAsData();
855 return loadTableFromPb(file->getSource(), data->data(), data->size(),
856 mContext->getDiagnostics());
857 }
858
859 bool mergeStaticLibrary(const std::string& input, bool override) {
860 if (mContext->verbose()) {
861 mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input);
862 }
863
864 std::string errorStr;
865 std::unique_ptr<io::ZipFileCollection> collection =
866 io::ZipFileCollection::create(input, &errorStr);
867 if (!collection) {
868 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
869 return false;
870 }
871
872 std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get());
873 if (!table) {
874 mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library");
875 return false;
876 }
877
878 ResourceTablePackage* pkg = table->findPackageById(0x7f);
879 if (!pkg) {
880 mContext->getDiagnostics()->error(DiagMessage(input)
881 << "static library has no package");
882 return false;
883 }
884
885 bool result;
886 if (mOptions.noStaticLibPackages) {
887 // Merge all resources as if they were in the compilation package. This is the old
888 // behaviour of aapt.
889
890 // Add the package to the set of --extra-packages so we emit an R.java for each
891 // library package.
892 if (!pkg->name.empty()) {
893 mOptions.extraJavaPackages.insert(pkg->name);
894 }
895
896 pkg->name = u"";
897 if (override) {
898 result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get());
899 } else {
900 result = mTableMerger->merge(Source(input), table.get(), collection.get());
901 }
902
903 } else {
904 // This is the proper way to merge libraries, where the package name is preserved
905 // and resource names are mangled.
906 result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(),
907 collection.get());
908 }
909
910 if (!result) {
911 return false;
912 }
913
914 // Make sure to move the collection into the set of IFileCollections.
915 mCollections.push_back(std::move(collection));
Adam Lesinskifb48d292015-11-07 15:52:13 -0800916 return true;
917 }
918
Adam Lesinskia40e9722015-11-24 19:11:46 -0800919 bool mergeResourceTable(io::IFile* file, bool override) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800920 if (mContext->verbose()) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800921 mContext->getDiagnostics()->note(DiagMessage() << "merging resource table "
922 << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800923 }
924
Adam Lesinskia40e9722015-11-24 19:11:46 -0800925 std::unique_ptr<io::IData> data = file->openAsData();
926 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -0800927 mContext->getDiagnostics()->error(DiagMessage(file->getSource())
Adam Lesinskia40e9722015-11-24 19:11:46 -0800928 << "failed to open file");
929 return false;
930 }
931
Adam Lesinski355f2852016-02-13 20:26:45 -0800932 std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(),
933 data->data(), data->size(),
934 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800935 if (!table) {
936 return false;
937 }
938
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800939 bool result = false;
940 if (override) {
941 result = mTableMerger->mergeOverlay(file->getSource(), table.get());
942 } else {
943 result = mTableMerger->merge(file->getSource(), table.get());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800944 }
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800945 return result;
Adam Lesinskifb48d292015-11-07 15:52:13 -0800946 }
947
Adam Lesinski64587af2016-02-18 18:33:06 -0800948 bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) {
Adam Lesinski355f2852016-02-13 20:26:45 -0800949 if (mContext->verbose()) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800950 mContext->getDiagnostics()->note(DiagMessage() << "merging compiled file "
951 << file->getSource());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800952 }
953
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800954 bool result = false;
Adam Lesinski64587af2016-02-18 18:33:06 -0800955 if (override) {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800956 result = mTableMerger->mergeFileOverlay(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800957 } else {
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800958 result = mTableMerger->mergeFile(*fileDesc, file);
Adam Lesinskifb48d292015-11-07 15:52:13 -0800959 }
960
Adam Lesinskia6fe3452015-12-09 15:20:52 -0800961 if (!result) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800962 return false;
963 }
964
965 // Add the exports of this file to the table.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800966 for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
Adam Lesinskifb48d292015-11-07 15:52:13 -0800967 if (exportedSymbol.name.package.empty()) {
Adam Lesinski64587af2016-02-18 18:33:06 -0800968 exportedSymbol.name.package = mContext->getCompilationPackage();
Adam Lesinskifb48d292015-11-07 15:52:13 -0800969 }
970
971 ResourceNameRef resName = exportedSymbol.name;
972
Adam Lesinski6a008172016-02-02 17:02:58 -0800973 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
Adam Lesinskifb48d292015-11-07 15:52:13 -0800974 exportedSymbol.name);
975 if (mangledName) {
976 resName = mangledName.value();
977 }
978
979 std::unique_ptr<Id> id = util::make_unique<Id>();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800980 id->setSource(fileDesc->source.withLine(exportedSymbol.line));
Adam Lesinski64587af2016-02-18 18:33:06 -0800981 bool result = mFinalTable.addResourceAllowMangled(
982 resName, ConfigDescription::defaultConfig(), std::string(), std::move(id),
983 mContext->getDiagnostics());
Adam Lesinskifb48d292015-11-07 15:52:13 -0800984 if (!result) {
985 return false;
986 }
987 }
Adam Lesinskifb48d292015-11-07 15:52:13 -0800988 return true;
989 }
990
Adam Lesinskia40e9722015-11-24 19:11:46 -0800991 /**
Adam Lesinski64587af2016-02-18 18:33:06 -0800992 * Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
993 * If override is true, conflicting resources are allowed to override each other, in order of
994 * last seen.
995 *
996 * An io::IFileCollection is created from the ZIP file and added to the set of
997 * io::IFileCollections that are open.
Adam Lesinskia40e9722015-11-24 19:11:46 -0800998 */
999 bool mergeArchive(const std::string& input, bool override) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001000 if (mContext->verbose()) {
1001 mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input);
1002 }
1003
Adam Lesinskia40e9722015-11-24 19:11:46 -08001004 std::string errorStr;
Adam Lesinski64587af2016-02-18 18:33:06 -08001005 std::unique_ptr<io::ZipFileCollection> collection =
1006 io::ZipFileCollection::create(input, &errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001007 if (!collection) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001008 mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001009 return false;
1010 }
1011
1012 bool error = false;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001013 for (auto iter = collection->iterator(); iter->hasNext(); ) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001014 if (!mergeFile(iter->next(), override)) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001015 error = true;
1016 }
1017 }
1018
1019 // Make sure to move the collection into the set of IFileCollections.
1020 mCollections.push_back(std::move(collection));
1021 return !error;
1022 }
1023
Adam Lesinski64587af2016-02-18 18:33:06 -08001024 /**
1025 * Takes a path to load and merge into the master ResourceTable. If override is true,
1026 * conflicting resources are allowed to override each other, in order of last seen.
1027 *
1028 * If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive
1029 * and the files within are merged individually.
1030 *
1031 * Otherwise the files is processed on its own.
1032 */
1033 bool mergePath(const std::string& path, bool override) {
Adam Lesinski656a5772016-01-14 15:17:41 -08001034 if (util::stringEndsWith<char>(path, ".flata") ||
1035 util::stringEndsWith<char>(path, ".jar") ||
1036 util::stringEndsWith<char>(path, ".jack") ||
1037 util::stringEndsWith<char>(path, ".zip")) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001038 return mergeArchive(path, override);
Adam Lesinski64587af2016-02-18 18:33:06 -08001039 } else if (util::stringEndsWith<char>(path, ".apk")) {
1040 return mergeStaticLibrary(path, override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001041 }
1042
1043 io::IFile* file = mFileCollection->insertFile(path);
Adam Lesinski64587af2016-02-18 18:33:06 -08001044 return mergeFile(file, override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001045 }
1046
Adam Lesinski64587af2016-02-18 18:33:06 -08001047 /**
1048 * Takes a file to load and merge into the master ResourceTable. If override is true,
1049 * conflicting resources are allowed to override each other, in order of last seen.
1050 *
1051 * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the
1052 * master ResourceTable. If the file ends with .flat, then it is treated like a compiled file
1053 * and the header data is read and merged into the final ResourceTable.
1054 *
1055 * All other file types are ignored. This is because these files could be coming from a zip,
1056 * where we could have other files like classes.dex.
1057 */
1058 bool mergeFile(io::IFile* file, bool override) {
Adam Lesinskia40e9722015-11-24 19:11:46 -08001059 const Source& src = file->getSource();
1060 if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
1061 return mergeResourceTable(file, override);
Adam Lesinski64587af2016-02-18 18:33:06 -08001062
Adam Lesinski52364f72016-01-11 13:10:24 -08001063 } else if (util::stringEndsWith<char>(src.path, ".flat")){
Adam Lesinskia40e9722015-11-24 19:11:46 -08001064 // Try opening the file and looking for an Export header.
1065 std::unique_ptr<io::IData> data = file->openAsData();
1066 if (!data) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001067 mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
Adam Lesinskia40e9722015-11-24 19:11:46 -08001068 return false;
1069 }
1070
1071 std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
Adam Lesinski6a008172016-02-02 17:02:58 -08001072 src, data->data(), data->size(), mContext->getDiagnostics());
Adam Lesinskia40e9722015-11-24 19:11:46 -08001073 if (resourceFile) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001074 return mergeCompiledFile(file, resourceFile.get(), override);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001075 }
Adam Lesinskic446a732016-01-21 11:04:46 -08001076 return false;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001077 }
Adam Lesinski52364f72016-01-11 13:10:24 -08001078
Adam Lesinskic446a732016-01-21 11:04:46 -08001079 // Ignore non .flat files. This could be classes.dex or something else that happens
1080 // to be in an archive.
1081 return true;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001082 }
1083
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001084 int run(const std::vector<std::string>& inputFiles) {
1085 // Load the AndroidManifest.xml
Adam Lesinskia40e9722015-11-24 19:11:46 -08001086 std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
Adam Lesinski6a008172016-02-02 17:02:58 -08001087 mContext->getDiagnostics());
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001088 if (!manifestXml) {
1089 return 1;
1090 }
1091
1092 if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001093 mContext->setCompilationPackage(maybeAppInfo.value().package);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001094 } else {
Adam Lesinski6a008172016-02-02 17:02:58 -08001095 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001096 << "no package specified in <manifest> tag");
1097 return 1;
1098 }
1099
Adam Lesinski64587af2016-02-18 18:33:06 -08001100 if (!util::isJavaPackageName(mContext->getCompilationPackage())) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001101 mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001102 << "invalid package name '"
Adam Lesinski64587af2016-02-18 18:33:06 -08001103 << mContext->getCompilationPackage()
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001104 << "'");
1105 return 1;
1106 }
1107
Adam Lesinski64587af2016-02-18 18:33:06 -08001108 mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() });
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001109
Adam Lesinski64587af2016-02-18 18:33:06 -08001110 if (mContext->getCompilationPackage() == u"android") {
1111 mContext->setPackageId(0x01);
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001112 } else {
Adam Lesinski64587af2016-02-18 18:33:06 -08001113 mContext->setPackageId(0x7f);
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001114 }
1115
Adam Lesinski64587af2016-02-18 18:33:06 -08001116 if (!loadSymbolsFromIncludePaths()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001117 return 1;
1118 }
1119
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001120 TableMergerOptions tableMergerOptions;
1121 tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
Adam Lesinski6a008172016-02-02 17:02:58 -08001122 mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
Adam Lesinskifb48d292015-11-07 15:52:13 -08001123
Adam Lesinski355f2852016-02-13 20:26:45 -08001124 if (mContext->verbose()) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001125 mContext->getDiagnostics()->note(
Adam Lesinski64587af2016-02-18 18:33:06 -08001126 DiagMessage() << "linking package '" << mContext->getCompilationPackage()
1127 << "' with package ID " << std::hex
1128 << (int) mContext->getPackageId());
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001129 }
1130
Adam Lesinskifb48d292015-11-07 15:52:13 -08001131
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001132 for (const std::string& input : inputFiles) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001133 if (!mergePath(input, false)) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001134 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
Adam Lesinski467f1712015-11-16 17:35:44 -08001135 return 1;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001136 }
1137 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001138
Adam Lesinskifb48d292015-11-07 15:52:13 -08001139 for (const std::string& input : mOptions.overlayFiles) {
Adam Lesinski64587af2016-02-18 18:33:06 -08001140 if (!mergePath(input, true)) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001141 mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
Adam Lesinski467f1712015-11-16 17:35:44 -08001142 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001143 }
1144 }
1145
Adam Lesinskifb48d292015-11-07 15:52:13 -08001146 if (!verifyNoExternalPackages()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001147 return 1;
1148 }
1149
1150 if (!mOptions.staticLib) {
1151 PrivateAttributeMover mover;
Adam Lesinski6a008172016-02-02 17:02:58 -08001152 if (!mover.consume(mContext, &mFinalTable)) {
1153 mContext->getDiagnostics()->error(
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001154 DiagMessage() << "failed moving private attributes");
1155 return 1;
1156 }
1157 }
1158
Adam Lesinski64587af2016-02-18 18:33:06 -08001159 if (!mOptions.staticLib) {
1160 // Assign IDs if we are building a regular app.
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001161 IdAssigner idAssigner;
Adam Lesinski6a008172016-02-02 17:02:58 -08001162 if (!idAssigner.consume(mContext, &mFinalTable)) {
1163 mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001164 return 1;
1165 }
Adam Lesinski64587af2016-02-18 18:33:06 -08001166 } else {
1167 // Static libs are merged with other apps, and ID collisions are bad, so verify that
1168 // no IDs have been set.
1169 if (!verifyNoIdsSet()) {
1170 return 1;
1171 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001172 }
1173
Adam Lesinski64587af2016-02-18 18:33:06 -08001174 // Add the names to mangle based on our source merge earlier.
1175 mContext->setNameManglerPolicy(NameManglerPolicy{
1176 mContext->getCompilationPackage(), mTableMerger->getMergedPackages() });
1177
1178 // Add our table to the symbol table.
1179 mContext->getExternalSymbols()->prependSource(
1180 util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001181
1182 {
1183 ReferenceLinker linker;
Adam Lesinski6a008172016-02-02 17:02:58 -08001184 if (!linker.consume(mContext, &mFinalTable)) {
1185 mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001186 return 1;
1187 }
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001188
Adam Lesinski64587af2016-02-18 18:33:06 -08001189 if (mOptions.staticLib) {
1190 if (!mOptions.products.empty()) {
1191 mContext->getDiagnostics()->warn(
1192 DiagMessage() << "can't select products when building static library");
1193 }
Adam Lesinski355f2852016-02-13 20:26:45 -08001194
Adam Lesinski64587af2016-02-18 18:33:06 -08001195 if (mOptions.tableSplitterOptions.configFilter != nullptr ||
1196 mOptions.tableSplitterOptions.preferredDensity) {
1197 mContext->getDiagnostics()->warn(
1198 DiagMessage() << "can't strip resources when building static library");
1199 }
1200 } else {
1201 ProductFilter productFilter(mOptions.products);
1202 if (!productFilter.consume(mContext, &mFinalTable)) {
1203 mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
1204 return 1;
1205 }
Adam Lesinski355f2852016-02-13 20:26:45 -08001206
Adam Lesinski64587af2016-02-18 18:33:06 -08001207 // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
1208 // level.
1209 TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
1210 if (!tableSplitter.verifySplitConstraints(mContext)) {
1211 return 1;
1212 }
1213 tableSplitter.splitTable(&mFinalTable);
1214 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001215 }
1216
1217 proguard::KeepSet proguardKeepSet;
Rohit Agrawale49bb302016-04-22 12:27:55 -07001218 proguard::KeepSet proguardMainDexKeepSet;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001219
1220 std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
1221 if (!archiveWriter) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001222 mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001223 return 1;
1224 }
1225
Adam Lesinski467f1712015-11-16 17:35:44 -08001226 bool error = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001227 {
Adam Lesinski52364f72016-01-11 13:10:24 -08001228 ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
Adam Lesinski6a008172016-02-02 17:02:58 -08001229 if (!manifestFixer.consume(mContext, manifestXml.get())) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001230 error = true;
1231 }
1232
Adam Lesinski467f1712015-11-16 17:35:44 -08001233 // AndroidManifest.xml has no resource name, but the CallSite is built from the name
1234 // (aka, which package the AndroidManifest.xml is coming from).
1235 // So we give it a package name so it can see local resources.
Adam Lesinski64587af2016-02-18 18:33:06 -08001236 manifestXml->file.name.package = mContext->getCompilationPackage();
Adam Lesinski467f1712015-11-16 17:35:44 -08001237
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001238 XmlReferenceLinker manifestLinker;
Adam Lesinski6a008172016-02-02 17:02:58 -08001239 if (manifestLinker.consume(mContext, manifestXml.get())) {
Rohit Agrawale49bb302016-04-22 12:27:55 -07001240 if (mOptions.generateProguardRulesPath &&
1241 !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
1242 manifestXml.get(),
1243 &proguardKeepSet)) {
1244 error = true;
1245 }
1246
1247 if (mOptions.generateMainDexProguardRulesPath &&
1248 !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
1249 manifestXml.get(),
1250 &proguardMainDexKeepSet,
1251 true)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001252 error = true;
1253 }
1254
Adam Lesinskica5638f2015-10-21 14:42:43 -07001255 if (mOptions.generateJavaClassPath) {
1256 if (!writeManifestJavaFile(manifestXml.get())) {
1257 error = true;
1258 }
1259 }
1260
Adam Lesinski355f2852016-02-13 20:26:45 -08001261 const bool keepRawValues = mOptions.staticLib;
1262 bool result = flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
1263 keepRawValues, archiveWriter.get(), mContext);
1264 if (!result) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001265 error = true;
1266 }
1267 } else {
1268 error = true;
1269 }
1270 }
1271
Adam Lesinski467f1712015-11-16 17:35:44 -08001272 if (error) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001273 mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
Adam Lesinski467f1712015-11-16 17:35:44 -08001274 return 1;
1275 }
1276
Adam Lesinski355f2852016-02-13 20:26:45 -08001277 ResourceFileFlattenerOptions fileFlattenerOptions;
1278 fileFlattenerOptions.keepRawValues = mOptions.staticLib;
1279 fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
1280 fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
1281 fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
Adam Lesinski626a69f2016-03-03 10:09:26 -08001282 fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
Rohit Agrawale49bb302016-04-22 12:27:55 -07001283 fileFlattenerOptions.updateProguardSpec =
1284 static_cast<bool>(mOptions.generateProguardRulesPath);
Adam Lesinski355f2852016-02-13 20:26:45 -08001285 ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet);
Adam Lesinskia40e9722015-11-24 19:11:46 -08001286
Adam Lesinski355f2852016-02-13 20:26:45 -08001287 if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) {
Adam Lesinski6a008172016-02-02 17:02:58 -08001288 mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001289 return 1;
1290 }
1291
Adam Lesinski626a69f2016-03-03 10:09:26 -08001292 if (!mOptions.noAutoVersion) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001293 AutoVersioner versioner;
Adam Lesinski6a008172016-02-02 17:02:58 -08001294 if (!versioner.consume(mContext, &mFinalTable)) {
1295 mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001296 return 1;
1297 }
1298 }
1299
Adam Lesinski64587af2016-02-18 18:33:06 -08001300 if (mOptions.staticLib) {
1301 if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) {
1302 mContext->getDiagnostics()->error(DiagMessage()
1303 << "failed to write resources.arsc.flat");
1304 return 1;
1305 }
1306 } else {
1307 if (!flattenTable(&mFinalTable, archiveWriter.get())) {
1308 mContext->getDiagnostics()->error(DiagMessage()
1309 << "failed to write resources.arsc");
1310 return 1;
1311 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001312 }
1313
1314 if (mOptions.generateJavaClassPath) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001315 JavaClassGeneratorOptions options;
Adam Lesinski52364f72016-01-11 13:10:24 -08001316 options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
Adam Lesinski3524a232016-04-01 19:19:24 -07001317 options.javadocAnnotations = mOptions.javadocAnnotations;
Adam Lesinski52364f72016-01-11 13:10:24 -08001318
Adam Lesinskief9c5012016-01-22 14:09:53 -08001319 if (mOptions.staticLib || mOptions.generateNonFinalIds) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001320 options.useFinal = false;
1321 }
1322
Adam Lesinski6a008172016-02-02 17:02:58 -08001323 const StringPiece16 actualPackage = mContext->getCompilationPackage();
1324 StringPiece16 outputPackage = mContext->getCompilationPackage();
Adam Lesinski52364f72016-01-11 13:10:24 -08001325 if (mOptions.customJavaPackage) {
1326 // Override the output java package to the custom one.
1327 outputPackage = mOptions.customJavaPackage.value();
1328 }
Adam Lesinski83f22552015-11-07 11:51:23 -08001329
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001330 if (mOptions.privateSymbols) {
1331 // If we defined a private symbols package, we only emit Public symbols
1332 // to the original package, and private and public symbols to the private package.
1333
1334 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
Adam Lesinski6a008172016-02-02 17:02:58 -08001335 if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
Adam Lesinski52364f72016-01-11 13:10:24 -08001336 outputPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001337 return 1;
1338 }
1339
1340 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
Adam Lesinski83f22552015-11-07 11:51:23 -08001341 outputPackage = mOptions.privateSymbols.value();
1342 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001343
Adam Lesinskifb48d292015-11-07 15:52:13 -08001344 if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
Adam Lesinski83f22552015-11-07 11:51:23 -08001345 return 1;
1346 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001347
Adam Lesinski52364f72016-01-11 13:10:24 -08001348 for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
1349 if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001350 return 1;
1351 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001352 }
1353 }
1354
Rohit Agrawale49bb302016-04-22 12:27:55 -07001355 if (!writeProguardFile(mOptions.generateProguardRulesPath, proguardKeepSet)) {
1356 return 1;
1357 }
1358
1359 if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath, proguardMainDexKeepSet)) {
1360 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001361 }
1362
Adam Lesinski355f2852016-02-13 20:26:45 -08001363 if (mContext->verbose()) {
1364 DebugPrintTableOptions debugPrintTableOptions;
1365 debugPrintTableOptions.showSources = true;
1366 Debug::printTable(&mFinalTable, debugPrintTableOptions);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001367 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001368 return 0;
1369 }
Adam Lesinskifb48d292015-11-07 15:52:13 -08001370
1371private:
1372 LinkOptions mOptions;
Adam Lesinski6a008172016-02-02 17:02:58 -08001373 LinkContext* mContext;
Adam Lesinskifb48d292015-11-07 15:52:13 -08001374 ResourceTable mFinalTable;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001375
Adam Lesinskifb48d292015-11-07 15:52:13 -08001376 std::unique_ptr<TableMerger> mTableMerger;
1377
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001378 // A pointer to the FileCollection representing the filesystem (not archives).
Adam Lesinski64587af2016-02-18 18:33:06 -08001379 std::unique_ptr<io::FileCollection> mFileCollection;
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001380
1381 // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
Adam Lesinskia40e9722015-11-24 19:11:46 -08001382 std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
Adam Lesinski64587af2016-02-18 18:33:06 -08001383
1384 // A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable
1385 // can use these.
1386 std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001387};
1388
1389int link(const std::vector<StringPiece>& args) {
Adam Lesinski355f2852016-02-13 20:26:45 -08001390 LinkContext context;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001391 LinkOptions options;
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001392 Maybe<std::string> privateSymbolsPackage;
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001393 Maybe<std::string> minSdkVersion, targetSdkVersion;
Adam Lesinski52364f72016-01-11 13:10:24 -08001394 Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
1395 Maybe<std::string> versionCode, versionName;
1396 Maybe<std::string> customJavaPackage;
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001397 std::vector<std::string> extraJavaPackages;
Adam Lesinski6a008172016-02-02 17:02:58 -08001398 Maybe<std::string> configs;
Adam Lesinski355f2852016-02-13 20:26:45 -08001399 Maybe<std::string> preferredDensity;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001400 Maybe<std::string> productList;
Adam Lesinski8900aa82016-01-25 22:48:15 -08001401 bool legacyXFlag = false;
1402 bool requireLocalization = false;
Adam Lesinski64587af2016-02-18 18:33:06 -08001403 bool verbose = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001404 Flags flags = Flags()
1405 .requiredFlag("-o", "Output path", &options.outputPath)
1406 .requiredFlag("--manifest", "Path to the Android manifest to build",
1407 &options.manifestPath)
1408 .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
Adam Lesinski52364f72016-01-11 13:10:24 -08001409 .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
Adam Lesinskifb48d292015-11-07 15:52:13 -08001410 "The last conflicting resource given takes precedence.",
1411 &options.overlayFiles)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001412 .optionalFlag("--java", "Directory in which to generate R.java",
1413 &options.generateJavaClassPath)
1414 .optionalFlag("--proguard", "Output file for generated Proguard rules",
1415 &options.generateProguardRulesPath)
Rohit Agrawale49bb302016-04-22 12:27:55 -07001416 .optionalFlag("--proguard-main-dex",
1417 "Output file for generated Proguard rules for the main dex",
1418 &options.generateMainDexProguardRulesPath)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001419 .optionalSwitch("--no-auto-version",
1420 "Disables automatic style and layout SDK versioning",
1421 &options.noAutoVersion)
Adam Lesinski64587af2016-02-18 18:33:06 -08001422 .optionalSwitch("--no-version-vectors",
1423 "Disables automatic versioning of vector drawables. Use this only\n"
1424 "when building with vector drawable support library",
1425 &options.noVersionVectors)
Adam Lesinski8900aa82016-01-25 22:48:15 -08001426 .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
1427 &legacyXFlag)
1428 .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
1429 &requireLocalization)
Adam Lesinski6a008172016-02-02 17:02:58 -08001430 .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
1431 "is all configurations", &configs)
Adam Lesinski355f2852016-02-13 20:26:45 -08001432 .optionalFlag("--preferred-density",
1433 "Selects the closest matching density and strips out all others.",
1434 &preferredDensity)
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001435 .optionalFlag("--product", "Comma separated list of product names to keep",
1436 &productList)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001437 .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
1438 "by -o",
1439 &options.outputToDirectory)
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001440 .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
1441 "AndroidManifest.xml", &minSdkVersion)
1442 .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
1443 "AndroidManifest.xml", &targetSdkVersion)
Adam Lesinski52364f72016-01-11 13:10:24 -08001444 .optionalFlag("--version-code", "Version code (integer) to inject into the "
1445 "AndroidManifest.xml if none is present", &versionCode)
1446 .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
1447 "if none is present", &versionName)
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001448 .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
Adam Lesinski64587af2016-02-18 18:33:06 -08001449 .optionalSwitch("--no-static-lib-packages",
1450 "Merge all library resources under the app's package",
1451 &options.noStaticLibPackages)
Adam Lesinskief9c5012016-01-22 14:09:53 -08001452 .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
1453 "This is implied when --static-lib is specified.",
1454 &options.generateNonFinalIds)
Adam Lesinski9ba47d82015-10-13 11:37:10 -07001455 .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001456 "private symbols.\n"
1457 "If not specified, public and private symbols will use the application's "
1458 "package name", &privateSymbolsPackage)
Adam Lesinski52364f72016-01-11 13:10:24 -08001459 .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
1460 &customJavaPackage)
Adam Lesinski83f22552015-11-07 11:51:23 -08001461 .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001462 "package names", &extraJavaPackages)
Adam Lesinski3524a232016-04-01 19:19:24 -07001463 .optionalFlagList("--add-javadoc-annotation", "Adds a JavaDoc annotation to all "
1464 "generated Java classes", &options.javadocAnnotations)
Adam Lesinskia6fe3452015-12-09 15:20:52 -08001465 .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
1466 "overlays without <add-resource> tags", &options.autoAddOverlay)
Adam Lesinski52364f72016-01-11 13:10:24 -08001467 .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
1468 &renameManifestPackage)
1469 .optionalFlag("--rename-instrumentation-target-package",
1470 "Changes the name of the target package for instrumentation. Most useful "
1471 "when used\nin conjunction with --rename-manifest-package",
1472 &renameInstrumentationTargetPackage)
1473 .optionalFlagList("-0", "File extensions not to compress",
1474 &options.extensionsToNotCompress)
Adam Lesinski64587af2016-02-18 18:33:06 -08001475 .optionalSwitch("-v", "Enables verbose logging", &verbose);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001476
1477 if (!flags.parse("aapt2 link", args, &std::cerr)) {
1478 return 1;
1479 }
1480
Adam Lesinski64587af2016-02-18 18:33:06 -08001481 if (verbose) {
1482 context.setVerbose(verbose);
1483 }
1484
Adam Lesinski9e10ac72015-10-16 14:37:48 -07001485 if (privateSymbolsPackage) {
1486 options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
1487 }
1488
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001489 if (minSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001490 options.manifestFixerOptions.minSdkVersionDefault =
1491 util::utf8ToUtf16(minSdkVersion.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001492 }
1493
1494 if (targetSdkVersion) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001495 options.manifestFixerOptions.targetSdkVersionDefault =
1496 util::utf8ToUtf16(targetSdkVersion.value());
1497 }
1498
1499 if (renameManifestPackage) {
1500 options.manifestFixerOptions.renameManifestPackage =
1501 util::utf8ToUtf16(renameManifestPackage.value());
1502 }
1503
1504 if (renameInstrumentationTargetPackage) {
1505 options.manifestFixerOptions.renameInstrumentationTargetPackage =
1506 util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
1507 }
1508
1509 if (versionCode) {
1510 options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
1511 }
1512
1513 if (versionName) {
1514 options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
1515 }
1516
1517 if (customJavaPackage) {
1518 options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001519 }
1520
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001521 // Populate the set of extra packages for which to generate R.java.
1522 for (std::string& extraPackage : extraJavaPackages) {
1523 // A given package can actually be a colon separated list of packages.
1524 for (StringPiece package : util::split(extraPackage, ':')) {
Adam Lesinski52364f72016-01-11 13:10:24 -08001525 options.extraJavaPackages.insert(util::utf8ToUtf16(package));
Adam Lesinskifc9570e62015-11-16 15:07:54 -08001526 }
1527 }
1528
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -08001529 if (productList) {
1530 for (StringPiece product : util::tokenize<char>(productList.value(), ',')) {
1531 if (product != "" && product != "default") {
1532 options.products.insert(product.toString());
1533 }
1534 }
1535 }
1536
Adam Lesinski6a008172016-02-02 17:02:58 -08001537 AxisConfigFilter filter;
1538 if (configs) {
1539 for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) {
1540 ConfigDescription config;
1541 LocaleValue lv;
1542 if (lv.initFromFilterString(configStr)) {
1543 lv.writeTo(&config);
1544 } else if (!ConfigDescription::parse(configStr, &config)) {
1545 context.getDiagnostics()->error(
1546 DiagMessage() << "invalid config '" << configStr << "' for -c option");
1547 return 1;
1548 }
1549
1550 if (config.density != 0) {
1551 context.getDiagnostics()->warn(
1552 DiagMessage() << "ignoring density '" << config << "' for -c option");
1553 } else {
1554 filter.addConfig(config);
1555 }
1556 }
1557
Adam Lesinski355f2852016-02-13 20:26:45 -08001558 options.tableSplitterOptions.configFilter = &filter;
1559 }
1560
1561 if (preferredDensity) {
1562 ConfigDescription preferredDensityConfig;
1563 if (!ConfigDescription::parse(preferredDensity.value(), &preferredDensityConfig)) {
1564 context.getDiagnostics()->error(DiagMessage() << "invalid density '"
1565 << preferredDensity.value()
1566 << "' for --preferred-density option");
1567 return 1;
1568 }
1569
1570 // Clear the version that can be automatically added.
1571 preferredDensityConfig.sdkVersion = 0;
1572
1573 if (preferredDensityConfig.diff(ConfigDescription::defaultConfig())
1574 != ConfigDescription::CONFIG_DENSITY) {
1575 context.getDiagnostics()->error(DiagMessage() << "invalid preferred density '"
1576 << preferredDensity.value() << "'. "
1577 << "Preferred density must only be a density value");
1578 return 1;
1579 }
1580 options.tableSplitterOptions.preferredDensity = preferredDensityConfig.density;
Adam Lesinski6a008172016-02-02 17:02:58 -08001581 }
1582
Adam Lesinski626a69f2016-03-03 10:09:26 -08001583 // Turn off auto versioning for static-libs.
1584 if (options.staticLib) {
1585 options.noAutoVersion = true;
1586 options.noVersionVectors = true;
1587 }
1588
Adam Lesinski6a008172016-02-02 17:02:58 -08001589 LinkCommand cmd(&context, options);
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001590 return cmd.run(flags.getArgs());
1591}
1592
1593} // namespace aapt