2015-08-14 14:26:04 -07:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "AppInfo.h"
|
|
|
|
#include "Debug.h"
|
|
|
|
#include "Flags.h"
|
|
|
|
#include "NameMangler.h"
|
|
|
|
#include "compile/IdAssigner.h"
|
|
|
|
#include "flatten/Archive.h"
|
|
|
|
#include "flatten/TableFlattener.h"
|
|
|
|
#include "flatten/XmlFlattener.h"
|
2015-11-24 19:11:46 -08:00
|
|
|
#include "io/FileSystem.h"
|
|
|
|
#include "io/ZipArchive.h"
|
2015-10-21 14:42:43 -07:00
|
|
|
#include "java/JavaClassGenerator.h"
|
|
|
|
#include "java/ManifestClassGenerator.h"
|
|
|
|
#include "java/ProguardRules.h"
|
2015-08-14 14:26:04 -07:00
|
|
|
#include "link/Linkers.h"
|
2015-11-16 17:35:44 -08:00
|
|
|
#include "link/ReferenceLinker.h"
|
2015-11-02 16:10:55 -08:00
|
|
|
#include "link/ManifestFixer.h"
|
2015-08-14 14:26:04 -07:00
|
|
|
#include "link/TableMerger.h"
|
|
|
|
#include "process/IResourceTableConsumer.h"
|
|
|
|
#include "process/SymbolTable.h"
|
|
|
|
#include "unflatten/BinaryResourceParser.h"
|
|
|
|
#include "unflatten/FileExportHeaderReader.h"
|
|
|
|
#include "util/Files.h"
|
|
|
|
#include "util/StringPiece.h"
|
2015-11-16 17:35:44 -08:00
|
|
|
#include "xml/XmlDom.h"
|
2015-08-14 14:26:04 -07:00
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
namespace aapt {
|
|
|
|
|
|
|
|
struct LinkOptions {
|
|
|
|
std::string outputPath;
|
|
|
|
std::string manifestPath;
|
|
|
|
std::vector<std::string> includePaths;
|
2015-11-07 15:52:13 -08:00
|
|
|
std::vector<std::string> overlayFiles;
|
2015-08-14 14:26:04 -07:00
|
|
|
Maybe<std::string> generateJavaClassPath;
|
2015-11-16 15:07:54 -08:00
|
|
|
std::set<std::string> extraJavaPackages;
|
2015-08-14 14:26:04 -07:00
|
|
|
Maybe<std::string> generateProguardRulesPath;
|
|
|
|
bool noAutoVersion = false;
|
|
|
|
bool staticLib = false;
|
|
|
|
bool verbose = false;
|
|
|
|
bool outputToDirectory = false;
|
2015-10-16 14:37:48 -07:00
|
|
|
Maybe<std::u16string> privateSymbols;
|
2015-11-02 16:10:55 -08:00
|
|
|
Maybe<std::u16string> minSdkVersionDefault;
|
|
|
|
Maybe<std::u16string> targetSdkVersionDefault;
|
2015-08-14 14:26:04 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct LinkContext : public IAaptContext {
|
|
|
|
StdErrDiagnostics mDiagnostics;
|
|
|
|
std::unique_ptr<NameMangler> mNameMangler;
|
|
|
|
std::u16string mCompilationPackage;
|
|
|
|
uint8_t mPackageId;
|
|
|
|
std::unique_ptr<ISymbolTable> mSymbols;
|
|
|
|
|
|
|
|
IDiagnostics* getDiagnostics() override {
|
|
|
|
return &mDiagnostics;
|
|
|
|
}
|
|
|
|
|
|
|
|
NameMangler* getNameMangler() override {
|
|
|
|
return mNameMangler.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
StringPiece16 getCompilationPackage() override {
|
|
|
|
return mCompilationPackage;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t getPackageId() override {
|
|
|
|
return mPackageId;
|
|
|
|
}
|
|
|
|
|
|
|
|
ISymbolTable* getExternalSymbols() override {
|
|
|
|
return mSymbols.get();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
class LinkCommand {
|
|
|
|
public:
|
|
|
|
LinkCommand(const LinkOptions& options) :
|
2015-11-24 19:11:46 -08:00
|
|
|
mOptions(options), mContext(), mFinalTable(), mFileCollection(nullptr) {
|
|
|
|
std::unique_ptr<io::FileCollection> fileCollection =
|
|
|
|
util::make_unique<io::FileCollection>();
|
|
|
|
|
|
|
|
// Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
|
|
|
|
mFileCollection = fileCollection.get();
|
|
|
|
|
|
|
|
// Move it to the collection.
|
|
|
|
mCollections.push_back(std::move(fileCollection));
|
2015-11-07 15:52:13 -08:00
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
|
|
|
std::string buildResourceFileName(const ResourceFile& resFile) {
|
|
|
|
std::stringstream out;
|
|
|
|
out << "res/" << resFile.name.type;
|
|
|
|
if (resFile.config != ConfigDescription{}) {
|
|
|
|
out << "-" << resFile.config;
|
|
|
|
}
|
|
|
|
out << "/";
|
|
|
|
|
|
|
|
if (mContext.getNameMangler()->shouldMangle(resFile.name.package)) {
|
|
|
|
out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
|
|
|
|
} else {
|
|
|
|
out << resFile.name.entry;
|
|
|
|
}
|
|
|
|
out << file::getExtension(resFile.source.path);
|
|
|
|
return out.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a SymbolTable that loads symbols from the various APKs and caches the
|
|
|
|
* results for faster lookup.
|
|
|
|
*/
|
|
|
|
std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
|
|
|
|
AssetManagerSymbolTableBuilder builder;
|
|
|
|
for (const std::string& path : mOptions.includePaths) {
|
|
|
|
if (mOptions.verbose) {
|
2015-11-07 15:52:13 -08:00
|
|
|
mContext.getDiagnostics()->note(DiagMessage(path) << "loading include path");
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<android::AssetManager> assetManager =
|
|
|
|
util::make_unique<android::AssetManager>();
|
|
|
|
int32_t cookie = 0;
|
|
|
|
if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
|
|
|
|
mContext.getDiagnostics()->error(
|
2015-11-07 15:52:13 -08:00
|
|
|
DiagMessage(path) << "failed to load include path");
|
2015-08-14 14:26:04 -07:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
builder.add(std::move(assetManager));
|
|
|
|
}
|
|
|
|
return builder.build();
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len) {
|
2015-08-14 14:26:04 -07:00
|
|
|
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
|
2015-11-24 19:11:46 -08:00
|
|
|
BinaryResourceParser parser(&mContext, table.get(), source, data, len);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!parser.parse()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return table;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inflates an XML file from the source path.
|
|
|
|
*/
|
2015-11-24 19:11:46 -08:00
|
|
|
static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
|
2015-08-14 14:26:04 -07:00
|
|
|
std::ifstream fin(path, std::ifstream::binary);
|
|
|
|
if (!fin) {
|
2015-11-24 19:11:46 -08:00
|
|
|
diag->error(DiagMessage(path) << strerror(errno));
|
2015-08-14 14:26:04 -07:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
return xml::inflate(&fin, diag, Source(path));
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
static std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(
|
|
|
|
const Source& source,
|
|
|
|
const void* data, size_t len,
|
|
|
|
IDiagnostics* diag) {
|
2015-08-14 14:26:04 -07:00
|
|
|
std::string errorStr;
|
2015-11-24 19:11:46 -08:00
|
|
|
ssize_t offset = getWrappedDataOffset(data, len, &errorStr);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (offset < 0) {
|
2015-11-24 19:11:46 -08:00
|
|
|
diag->error(DiagMessage(source) << errorStr);
|
2015-08-14 14:26:04 -07:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
|
2015-11-24 19:11:46 -08:00
|
|
|
reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset),
|
|
|
|
len - static_cast<size_t>(offset),
|
|
|
|
diag,
|
|
|
|
source);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!xmlRes) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return xmlRes;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
|
|
|
|
const void* data, size_t len,
|
|
|
|
IDiagnostics* diag) {
|
|
|
|
std::unique_ptr<ResourceFile> resFile = util::make_unique<ResourceFile>();
|
2015-08-14 14:26:04 -07:00
|
|
|
std::string errorStr;
|
2015-11-24 19:11:46 -08:00
|
|
|
ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (offset < 0) {
|
2015-11-24 19:11:46 -08:00
|
|
|
diag->error(DiagMessage(source) << errorStr);
|
2015-08-14 14:26:04 -07:00
|
|
|
return {};
|
|
|
|
}
|
2015-11-24 19:11:46 -08:00
|
|
|
return resFile;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
bool copyFileToArchive(io::IFile* file, const std::string& outPath, uint32_t flags,
|
2015-08-14 14:26:04 -07:00
|
|
|
IArchiveWriter* writer) {
|
2015-11-24 19:11:46 -08:00
|
|
|
std::unique_ptr<io::IData> data = file->openAsData();
|
|
|
|
if (!data) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage(file->getSource())
|
|
|
|
<< "failed to open file");
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
std::string errorStr;
|
|
|
|
ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (offset < 0) {
|
2015-11-24 19:11:46 -08:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr);
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
if (writer->startEntry(outPath, flags)) {
|
|
|
|
if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
|
|
|
|
data->size() - static_cast<size_t>(offset))) {
|
|
|
|
if (writer->finishEntry()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
2015-11-24 19:11:46 -08:00
|
|
|
|
|
|
|
mContext.getDiagnostics()->error(
|
|
|
|
DiagMessage(mOptions.outputPath) << "failed to write file " << outPath);
|
|
|
|
return false;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
|
2015-08-14 14:26:04 -07:00
|
|
|
// Make sure the first element is <manifest> with package attribute.
|
2015-11-02 16:10:55 -08:00
|
|
|
if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) {
|
2015-08-14 14:26:04 -07:00
|
|
|
if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") {
|
|
|
|
if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) {
|
|
|
|
return AppInfo{ packageAttr->value };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
bool verifyNoExternalPackages() {
|
2015-08-14 14:26:04 -07:00
|
|
|
bool error = false;
|
2015-11-07 15:52:13 -08:00
|
|
|
for (const auto& package : mFinalTable.packages) {
|
2015-08-14 14:26:04 -07:00
|
|
|
if (mContext.getCompilationPackage() != package->name ||
|
|
|
|
!package->id || package->id.value() != mContext.getPackageId()) {
|
|
|
|
// We have a package that is not related to the one we're building!
|
|
|
|
for (const auto& type : package->types) {
|
|
|
|
for (const auto& entry : type->entries) {
|
|
|
|
for (const auto& configValue : entry->values) {
|
2015-10-22 12:48:43 -07:00
|
|
|
mContext.getDiagnostics()->error(
|
|
|
|
DiagMessage(configValue.value->getSource())
|
|
|
|
<< "defined resource '"
|
|
|
|
<< ResourceNameRef(package->name,
|
|
|
|
type->type,
|
|
|
|
entry->name)
|
|
|
|
<< "' for external package '"
|
|
|
|
<< package->name << "'");
|
2015-08-14 14:26:04 -07:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return !error;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
|
|
|
|
if (mOptions.outputToDirectory) {
|
2015-11-24 19:11:46 -08:00
|
|
|
return createDirectoryArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
|
2015-08-14 14:26:04 -07:00
|
|
|
} else {
|
2015-11-24 19:11:46 -08:00
|
|
|
return createZipFileArchiveWriter(mContext.getDiagnostics(), mOptions.outputPath);
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
|
|
|
|
BigBuffer buffer(1024);
|
|
|
|
TableFlattenerOptions options = {};
|
|
|
|
options.useExtendedChunks = mOptions.staticLib;
|
|
|
|
TableFlattener flattener(&buffer, options);
|
|
|
|
if (!flattener.consume(&mContext, table)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
|
|
|
|
if (writer->writeEntry(buffer)) {
|
|
|
|
if (writer->finishEntry()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
2015-11-24 19:11:46 -08:00
|
|
|
|
|
|
|
mContext.getDiagnostics()->error(
|
|
|
|
DiagMessage() << "failed to write resources.arsc to archive");
|
|
|
|
return false;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
|
2015-08-14 14:26:04 -07:00
|
|
|
IArchiveWriter* writer) {
|
|
|
|
BigBuffer buffer(1024);
|
|
|
|
XmlFlattenerOptions options = {};
|
|
|
|
options.keepRawValues = mOptions.staticLib;
|
|
|
|
options.maxSdkLevel = maxSdkLevel;
|
|
|
|
XmlFlattener flattener(&buffer, options);
|
|
|
|
if (!flattener.consume(&mContext, xmlRes)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
|
|
|
|
if (writer->startEntry(path, ArchiveEntry::kCompress)) {
|
|
|
|
if (writer->writeEntry(buffer)) {
|
|
|
|
if (writer->finishEntry()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
2015-11-24 19:11:46 -08:00
|
|
|
mContext.getDiagnostics()->error(
|
|
|
|
DiagMessage() << "failed to write " << path << " to archive");
|
|
|
|
return false;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2015-10-16 14:37:48 -07:00
|
|
|
bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
|
|
|
|
const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!mOptions.generateJavaClassPath) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string outPath = mOptions.generateJavaClassPath.value();
|
2015-10-16 14:37:48 -07:00
|
|
|
file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
|
2015-08-14 14:26:04 -07:00
|
|
|
file::mkdirs(outPath);
|
|
|
|
file::appendPath(&outPath, "R.java");
|
|
|
|
|
|
|
|
std::ofstream fout(outPath, std::ofstream::binary);
|
|
|
|
if (!fout) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
JavaClassGenerator generator(table, javaOptions);
|
2015-10-16 14:37:48 -07:00
|
|
|
if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
|
2015-10-21 14:42:43 -07:00
|
|
|
if (!mOptions.generateJavaClassPath) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string outPath = mOptions.generateJavaClassPath.value();
|
|
|
|
file::appendPath(&outPath,
|
|
|
|
file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage())));
|
|
|
|
file::mkdirs(outPath);
|
|
|
|
file::appendPath(&outPath, "Manifest.java");
|
|
|
|
|
|
|
|
std::ofstream fout(outPath, std::ofstream::binary);
|
|
|
|
if (!fout) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ManifestClassGenerator generator;
|
|
|
|
if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(),
|
|
|
|
manifestXml, &fout)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fout) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
bool writeProguardFile(const proguard::KeepSet& keepSet) {
|
|
|
|
if (!mOptions.generateProguardRulesPath) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
|
|
|
|
if (!fout) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
proguard::writeKeepSet(&fout, keepSet);
|
|
|
|
if (!fout) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
bool mergeStaticLibrary(const std::string& input) {
|
|
|
|
// TODO(adamlesinski): Load resources from a static library APK and merge the table into
|
|
|
|
// TableMerger.
|
|
|
|
mContext.getDiagnostics()->warn(DiagMessage()
|
|
|
|
<< "linking static libraries not supported yet: "
|
|
|
|
<< input);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
bool mergeResourceTable(io::IFile* file, bool override) {
|
2015-11-07 15:52:13 -08:00
|
|
|
if (mOptions.verbose) {
|
2015-11-24 19:11:46 -08:00
|
|
|
mContext.getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<io::IData> data = file->openAsData();
|
|
|
|
if (!data) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage(file->getSource())
|
|
|
|
<< "failed to open file");
|
|
|
|
return false;
|
2015-11-07 15:52:13 -08:00
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(),
|
|
|
|
data->size());
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!table) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!mTableMerger->merge(file->getSource(), table.get(), override)) {
|
2015-11-07 15:52:13 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool override) {
|
|
|
|
// Apply the package name used for this compilation phase if none was specified.
|
|
|
|
if (fileDesc->name.package.empty()) {
|
|
|
|
fileDesc->name.package = mContext.getCompilationPackage().toString();
|
2015-11-07 15:52:13 -08:00
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
// Mangle the name if necessary.
|
|
|
|
ResourceNameRef resName = fileDesc->name;
|
|
|
|
Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(fileDesc->name);
|
2015-11-07 15:52:13 -08:00
|
|
|
if (mangledName) {
|
|
|
|
resName = mangledName.value();
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
// If we are overriding resources, we supply a custom resolver function.
|
2015-11-07 15:52:13 -08:00
|
|
|
std::function<int(Value*,Value*)> resolver;
|
|
|
|
if (override) {
|
|
|
|
resolver = [](Value* a, Value* b) -> int {
|
|
|
|
int result = ResourceTable::resolveValueCollision(a, b);
|
|
|
|
if (result == 0) {
|
|
|
|
// Always accept the new value if it would normally conflict (override).
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// Otherwise use the default resolution.
|
|
|
|
resolver = ResourceTable::resolveValueCollision;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add this file to the table.
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!mFinalTable.addFileReference(resName, fileDesc->config, fileDesc->source,
|
|
|
|
util::utf8ToUtf16(buildResourceFileName(*fileDesc)),
|
2015-11-07 15:52:13 -08:00
|
|
|
resolver, mContext.getDiagnostics())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the exports of this file to the table.
|
2015-11-24 19:11:46 -08:00
|
|
|
for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
|
2015-11-07 15:52:13 -08:00
|
|
|
if (exportedSymbol.name.package.empty()) {
|
|
|
|
exportedSymbol.name.package = mContext.getCompilationPackage().toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
ResourceNameRef resName = exportedSymbol.name;
|
|
|
|
|
|
|
|
Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(
|
|
|
|
exportedSymbol.name);
|
|
|
|
if (mangledName) {
|
|
|
|
resName = mangledName.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<Id> id = util::make_unique<Id>();
|
2015-11-24 19:11:46 -08:00
|
|
|
id->setSource(fileDesc->source.withLine(exportedSymbol.line));
|
2015-11-07 15:52:13 -08:00
|
|
|
bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id),
|
2015-11-24 19:11:46 -08:00
|
|
|
mContext.getDiagnostics());
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!result) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
// Now add this file for later processing. Once the table is assigned IDs, we can compile
|
|
|
|
// this file.
|
|
|
|
mFilesToProcess.insert(FileToProcess{ std::move(fileDesc), file });
|
2015-11-07 15:52:13 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
/**
|
|
|
|
* Creates an io::IFileCollection from the ZIP archive and processes the files within.
|
|
|
|
*/
|
|
|
|
bool mergeArchive(const std::string& input, bool override) {
|
|
|
|
std::string errorStr;
|
|
|
|
std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
|
|
|
|
input, &errorStr);
|
|
|
|
if (!collection) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage(input) << errorStr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool error = false;
|
|
|
|
for (const std::unique_ptr<io::IFile>& file : *collection) {
|
|
|
|
if (!processFile(file.get(), override)) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure to move the collection into the set of IFileCollections.
|
|
|
|
mCollections.push_back(std::move(collection));
|
|
|
|
return !error;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool processFile(const std::string& path, bool override) {
|
|
|
|
if (util::stringEndsWith<char>(path, ".flata")) {
|
|
|
|
return mergeArchive(path, override);
|
|
|
|
}
|
|
|
|
|
|
|
|
io::IFile* file = mFileCollection->insertFile(path);
|
|
|
|
return processFile(file, override);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool processFile(io::IFile* file, bool override) {
|
|
|
|
const Source& src = file->getSource();
|
|
|
|
if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
|
|
|
|
return mergeResourceTable(file, override);
|
|
|
|
} else {
|
|
|
|
// Try opening the file and looking for an Export header.
|
|
|
|
std::unique_ptr<io::IData> data = file->openAsData();
|
|
|
|
if (!data) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage(src) << "failed to open");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
|
|
|
|
src, data->data(), data->size(), mContext.getDiagnostics());
|
|
|
|
if (resourceFile) {
|
|
|
|
return mergeCompiledFile(file, std::move(resourceFile), override);
|
|
|
|
}
|
2015-11-07 15:52:13 -08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
int run(const std::vector<std::string>& inputFiles) {
|
|
|
|
// Load the AndroidManifest.xml
|
2015-11-24 19:11:46 -08:00
|
|
|
std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
|
|
|
|
mContext.getDiagnostics());
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!manifestXml) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
|
|
|
|
mContext.mCompilationPackage = maybeAppInfo.value().package;
|
|
|
|
} else {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
|
|
|
|
<< "no package specified in <manifest> tag");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!util::isJavaPackageName(mContext.mCompilationPackage)) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
|
|
|
|
<< "invalid package name '"
|
|
|
|
<< mContext.mCompilationPackage
|
|
|
|
<< "'");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mContext.mNameMangler = util::make_unique<NameMangler>(
|
|
|
|
NameManglerPolicy{ mContext.mCompilationPackage });
|
2015-10-13 11:37:10 -07:00
|
|
|
|
|
|
|
if (mContext.mCompilationPackage == u"android") {
|
|
|
|
mContext.mPackageId = 0x01;
|
|
|
|
} else {
|
|
|
|
mContext.mPackageId = 0x7f;
|
|
|
|
}
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.mSymbols = createSymbolTableFromIncludePaths();
|
|
|
|
if (!mContext.mSymbols) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable);
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
if (mOptions.verbose) {
|
|
|
|
mContext.getDiagnostics()->note(
|
|
|
|
DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' "
|
|
|
|
<< "with package ID " << std::hex << (int) mContext.mPackageId);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
for (const std::string& input : inputFiles) {
|
|
|
|
if (!processFile(input, false)) {
|
2015-11-16 17:35:44 -08:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input");
|
|
|
|
return 1;
|
2015-11-07 15:52:13 -08:00
|
|
|
}
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
for (const std::string& input : mOptions.overlayFiles) {
|
|
|
|
if (!processFile(input, true)) {
|
2015-11-16 17:35:44 -08:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
|
|
|
|
return 1;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!verifyNoExternalPackages()) {
|
2015-08-14 14:26:04 -07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mOptions.staticLib) {
|
|
|
|
PrivateAttributeMover mover;
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!mover.consume(&mContext, &mFinalTable)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.getDiagnostics()->error(
|
|
|
|
DiagMessage() << "failed moving private attributes");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
IdAssigner idAssigner;
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!idAssigner.consume(&mContext, &mFinalTable)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
|
|
|
|
mContext.mCompilationPackage, mTableMerger->getMergedPackages() });
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.mSymbols = JoinedSymbolTableBuilder()
|
2015-11-07 15:52:13 -08:00
|
|
|
.addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
|
2015-08-14 14:26:04 -07:00
|
|
|
.addSymbolTable(std::move(mContext.mSymbols))
|
|
|
|
.build();
|
|
|
|
|
|
|
|
{
|
|
|
|
ReferenceLinker linker;
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!linker.consume(&mContext, &mFinalTable)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed linking references");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
proguard::KeepSet proguardKeepSet;
|
|
|
|
|
|
|
|
std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter();
|
|
|
|
if (!archiveWriter) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed to create archive");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
bool error = false;
|
2015-08-14 14:26:04 -07:00
|
|
|
{
|
2015-11-02 16:10:55 -08:00
|
|
|
ManifestFixerOptions manifestFixerOptions;
|
|
|
|
manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
|
|
|
|
manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
|
|
|
|
ManifestFixer manifestFixer(manifestFixerOptions);
|
|
|
|
if (!manifestFixer.consume(&mContext, manifestXml.get())) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
// AndroidManifest.xml has no resource name, but the CallSite is built from the name
|
|
|
|
// (aka, which package the AndroidManifest.xml is coming from).
|
|
|
|
// So we give it a package name so it can see local resources.
|
|
|
|
manifestXml->file.name.package = mContext.getCompilationPackage().toString();
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
XmlReferenceLinker manifestLinker;
|
|
|
|
if (manifestLinker.consume(&mContext, manifestXml.get())) {
|
|
|
|
if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath),
|
|
|
|
manifestXml.get(),
|
|
|
|
&proguardKeepSet)) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
2015-10-21 14:42:43 -07:00
|
|
|
if (mOptions.generateJavaClassPath) {
|
|
|
|
if (!writeManifestJavaFile(manifestXml.get())) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
|
|
|
|
archiveWriter.get())) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
if (error) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-16 15:07:54 -08:00
|
|
|
for (const FileToProcess& file : mFilesToProcess) {
|
2015-11-24 19:11:46 -08:00
|
|
|
const StringPiece path = file.file->getSource().path;
|
|
|
|
|
|
|
|
if (file.fileExport->name.type != ResourceType::kRaw &&
|
|
|
|
util::stringEndsWith<char>(path, ".xml.flat")) {
|
2015-08-14 14:26:04 -07:00
|
|
|
if (mOptions.verbose) {
|
2015-11-24 19:11:46 -08:00
|
|
|
mContext.getDiagnostics()->note(DiagMessage() << "linking " << path);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<io::IData> data = file.file->openAsData();
|
|
|
|
if (!data) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage(file.file->getSource())
|
|
|
|
<< "failed to open file");
|
|
|
|
return 1;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
std::unique_ptr<xml::XmlResource> xmlRes = loadBinaryXmlSkipFileExport(
|
2015-11-24 19:11:46 -08:00
|
|
|
file.file->getSource(), data->data(), data->size(),
|
|
|
|
mContext.getDiagnostics());
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!xmlRes) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
// Move the file description over.
|
|
|
|
xmlRes->file = std::move(*file.fileExport);
|
2015-08-14 14:26:04 -07:00
|
|
|
|
|
|
|
XmlReferenceLinker xmlLinker;
|
|
|
|
if (xmlLinker.consume(&mContext, xmlRes.get())) {
|
|
|
|
if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(),
|
|
|
|
&proguardKeepSet)) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Maybe<size_t> maxSdkLevel;
|
|
|
|
if (!mOptions.noAutoVersion) {
|
|
|
|
maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file), maxSdkLevel,
|
|
|
|
archiveWriter.get())) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mOptions.noAutoVersion) {
|
2015-11-07 15:52:13 -08:00
|
|
|
Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource(
|
2015-08-14 14:26:04 -07:00
|
|
|
xmlRes->file.name);
|
|
|
|
for (int sdkLevel : xmlLinker.getSdkLevels()) {
|
|
|
|
if (sdkLevel > xmlRes->file.config.sdkVersion &&
|
|
|
|
shouldGenerateVersionedResource(result.value().entry,
|
|
|
|
xmlRes->file.config,
|
|
|
|
sdkLevel)) {
|
|
|
|
xmlRes->file.config.sdkVersion = sdkLevel;
|
2015-11-24 19:11:46 -08:00
|
|
|
bool added = mFinalTable.addFileReference(
|
|
|
|
xmlRes->file.name,
|
|
|
|
xmlRes->file.config,
|
|
|
|
xmlRes->file.source,
|
|
|
|
util::utf8ToUtf16(buildResourceFileName(xmlRes->file)),
|
|
|
|
mContext.getDiagnostics());
|
|
|
|
if (!added) {
|
2015-08-14 14:26:04 -07:00
|
|
|
error = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file),
|
|
|
|
sdkLevel, archiveWriter.get())) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mOptions.verbose) {
|
2015-11-24 19:11:46 -08:00
|
|
|
mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!copyFileToArchive(file.file, buildResourceFileName(*file.fileExport), 0,
|
2015-08-14 14:26:04 -07:00
|
|
|
archiveWriter.get())) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed linking file resources");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mOptions.noAutoVersion) {
|
|
|
|
AutoVersioner versioner;
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!versioner.consume(&mContext, &mFinalTable)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!flattenTable(&mFinalTable, archiveWriter.get())) {
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mOptions.generateJavaClassPath) {
|
2015-10-16 14:37:48 -07:00
|
|
|
JavaClassGeneratorOptions options;
|
|
|
|
if (mOptions.staticLib) {
|
|
|
|
options.useFinal = false;
|
|
|
|
}
|
|
|
|
|
2015-11-07 11:51:23 -08:00
|
|
|
StringPiece16 actualPackage = mContext.getCompilationPackage();
|
|
|
|
StringPiece16 outputPackage = mContext.getCompilationPackage();
|
|
|
|
|
2015-10-16 14:37:48 -07:00
|
|
|
if (mOptions.privateSymbols) {
|
|
|
|
// If we defined a private symbols package, we only emit Public symbols
|
|
|
|
// to the original package, and private and public symbols to the private package.
|
|
|
|
|
|
|
|
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
|
2015-10-16 14:37:48 -07:00
|
|
|
mContext.getCompilationPackage(), options)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
|
2015-11-07 11:51:23 -08:00
|
|
|
outputPackage = mOptions.privateSymbols.value();
|
|
|
|
}
|
2015-10-16 14:37:48 -07:00
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
|
2015-11-07 11:51:23 -08:00
|
|
|
return 1;
|
|
|
|
}
|
2015-10-16 14:37:48 -07:00
|
|
|
|
2015-11-16 15:07:54 -08:00
|
|
|
for (const std::string& extraPackage : mOptions.extraJavaPackages) {
|
2015-11-07 15:52:13 -08:00
|
|
|
if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage),
|
2015-11-07 11:51:23 -08:00
|
|
|
options)) {
|
2015-10-16 14:37:48 -07:00
|
|
|
return 1;
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mOptions.generateProguardRulesPath) {
|
|
|
|
if (!writeProguardFile(proguardKeepSet)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mOptions.verbose) {
|
2015-11-07 15:52:13 -08:00
|
|
|
Debug::printTable(&mFinalTable);
|
|
|
|
for (; !mTableMerger->getFileMergeQueue()->empty();
|
|
|
|
mTableMerger->getFileMergeQueue()->pop()) {
|
|
|
|
const FileToMerge& f = mTableMerger->getFileMergeQueue()->front();
|
2015-08-14 14:26:04 -07:00
|
|
|
mContext.getDiagnostics()->note(
|
|
|
|
DiagMessage() << f.srcPath << " -> " << f.dstPath << " from (0x"
|
|
|
|
<< std::hex << (uintptr_t) f.srcTable << std::dec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2015-11-07 15:52:13 -08:00
|
|
|
|
|
|
|
private:
|
|
|
|
LinkOptions mOptions;
|
|
|
|
LinkContext mContext;
|
|
|
|
ResourceTable mFinalTable;
|
|
|
|
std::unique_ptr<TableMerger> mTableMerger;
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
io::FileCollection* mFileCollection;
|
|
|
|
std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
struct FileToProcess {
|
2015-11-24 19:11:46 -08:00
|
|
|
std::unique_ptr<ResourceFile> fileExport;
|
|
|
|
io::IFile* file;
|
2015-11-16 15:07:54 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct FileToProcessComparator {
|
|
|
|
bool operator()(const FileToProcess& a, const FileToProcess& b) {
|
2015-11-24 19:11:46 -08:00
|
|
|
return std::tie(a.fileExport->name, a.fileExport->config) <
|
|
|
|
std::tie(b.fileExport->name, b.fileExport->config);
|
2015-11-16 15:07:54 -08:00
|
|
|
}
|
2015-11-07 15:52:13 -08:00
|
|
|
};
|
2015-11-16 15:07:54 -08:00
|
|
|
|
|
|
|
std::set<FileToProcess, FileToProcessComparator> mFilesToProcess;
|
2015-08-14 14:26:04 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
int link(const std::vector<StringPiece>& args) {
|
|
|
|
LinkOptions options;
|
2015-10-16 14:37:48 -07:00
|
|
|
Maybe<std::string> privateSymbolsPackage;
|
2015-11-02 16:10:55 -08:00
|
|
|
Maybe<std::string> minSdkVersion, targetSdkVersion;
|
2015-11-16 15:07:54 -08:00
|
|
|
std::vector<std::string> extraJavaPackages;
|
2015-08-14 14:26:04 -07:00
|
|
|
Flags flags = Flags()
|
|
|
|
.requiredFlag("-o", "Output path", &options.outputPath)
|
|
|
|
.requiredFlag("--manifest", "Path to the Android manifest to build",
|
|
|
|
&options.manifestPath)
|
|
|
|
.optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
|
2015-11-07 15:52:13 -08:00
|
|
|
.optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. "
|
|
|
|
"The last conflicting resource given takes precedence.",
|
|
|
|
&options.overlayFiles)
|
2015-08-14 14:26:04 -07:00
|
|
|
.optionalFlag("--java", "Directory in which to generate R.java",
|
|
|
|
&options.generateJavaClassPath)
|
|
|
|
.optionalFlag("--proguard", "Output file for generated Proguard rules",
|
|
|
|
&options.generateProguardRulesPath)
|
|
|
|
.optionalSwitch("--no-auto-version",
|
|
|
|
"Disables automatic style and layout SDK versioning",
|
|
|
|
&options.noAutoVersion)
|
|
|
|
.optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
|
|
|
|
"by -o",
|
|
|
|
&options.outputToDirectory)
|
2015-11-02 16:10:55 -08:00
|
|
|
.optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
|
|
|
|
"AndroidManifest.xml", &minSdkVersion)
|
|
|
|
.optionalFlag("--target-sdk-version", "Default target SDK version to use for "
|
|
|
|
"AndroidManifest.xml", &targetSdkVersion)
|
2015-08-14 14:26:04 -07:00
|
|
|
.optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
|
2015-10-13 11:37:10 -07:00
|
|
|
.optionalFlag("--private-symbols", "Package name to use when generating R.java for "
|
2015-11-02 16:10:55 -08:00
|
|
|
"private symbols.\n"
|
|
|
|
"If not specified, public and private symbols will use the application's "
|
|
|
|
"package name", &privateSymbolsPackage)
|
2015-11-07 11:51:23 -08:00
|
|
|
.optionalFlagList("--extra-packages", "Generate the same R.java but with different "
|
2015-11-16 15:07:54 -08:00
|
|
|
"package names", &extraJavaPackages)
|
2015-08-14 14:26:04 -07:00
|
|
|
.optionalSwitch("-v", "Enables verbose logging", &options.verbose);
|
|
|
|
|
|
|
|
if (!flags.parse("aapt2 link", args, &std::cerr)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-10-16 14:37:48 -07:00
|
|
|
if (privateSymbolsPackage) {
|
|
|
|
options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
|
|
|
|
}
|
|
|
|
|
2015-11-02 16:10:55 -08:00
|
|
|
if (minSdkVersion) {
|
|
|
|
options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetSdkVersion) {
|
|
|
|
options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
|
|
|
|
}
|
|
|
|
|
2015-11-16 15:07:54 -08:00
|
|
|
// Populate the set of extra packages for which to generate R.java.
|
|
|
|
for (std::string& extraPackage : extraJavaPackages) {
|
|
|
|
// A given package can actually be a colon separated list of packages.
|
|
|
|
for (StringPiece package : util::split(extraPackage, ':')) {
|
|
|
|
options.extraJavaPackages.insert(package.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-07 15:52:13 -08:00
|
|
|
LinkCommand cmd(options);
|
2015-08-14 14:26:04 -07:00
|
|
|
return cmd.run(flags.getArgs());
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace aapt
|