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 "ConfigDescription.h"
|
|
|
|
#include "Diagnostics.h"
|
|
|
|
#include "Flags.h"
|
|
|
|
#include "ResourceParser.h"
|
|
|
|
#include "ResourceTable.h"
|
|
|
|
#include "compile/IdAssigner.h"
|
|
|
|
#include "compile/Png.h"
|
2015-12-17 13:03:11 -08:00
|
|
|
#include "compile/PseudolocaleGenerator.h"
|
2015-08-14 14:26:04 -07:00
|
|
|
#include "compile/XmlIdCollector.h"
|
2015-11-24 19:11:46 -08:00
|
|
|
#include "flatten/Archive.h"
|
2015-08-14 14:26:04 -07:00
|
|
|
#include "flatten/XmlFlattener.h"
|
2016-02-04 15:59:23 -08:00
|
|
|
#include "proto/ProtoSerialize.h"
|
2015-08-14 14:26:04 -07:00
|
|
|
#include "util/Files.h"
|
|
|
|
#include "util/Maybe.h"
|
|
|
|
#include "util/Util.h"
|
2015-11-16 17:35:44 -08:00
|
|
|
#include "xml/XmlDom.h"
|
|
|
|
#include "xml/XmlPullParser.h"
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
|
|
|
|
#include <google/protobuf/io/coded_stream.h>
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
#include <dirent.h>
|
2015-08-14 14:26:04 -07:00
|
|
|
#include <fstream>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
namespace aapt {
|
|
|
|
|
|
|
|
struct ResourcePathData {
|
|
|
|
Source source;
|
|
|
|
std::u16string resourceDir;
|
|
|
|
std::u16string name;
|
|
|
|
std::string extension;
|
|
|
|
|
|
|
|
// Original config str. We keep this because when we parse the config, we may add on
|
|
|
|
// version qualifiers. We want to preserve the original input so the output is easily
|
|
|
|
// computed before hand.
|
|
|
|
std::string configStr;
|
|
|
|
ConfigDescription config;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resource file paths are expected to look like:
|
|
|
|
* [--/res/]type[-config]/name
|
|
|
|
*/
|
|
|
|
static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
|
|
|
|
std::string* outError) {
|
|
|
|
std::vector<std::string> parts = util::split(path, file::sDirSep);
|
|
|
|
if (parts.size() < 2) {
|
|
|
|
if (outError) *outError = "bad resource path";
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string& dir = parts[parts.size() - 2];
|
|
|
|
StringPiece dirStr = dir;
|
|
|
|
|
|
|
|
StringPiece configStr;
|
|
|
|
ConfigDescription config;
|
|
|
|
size_t dashPos = dir.find('-');
|
|
|
|
if (dashPos != std::string::npos) {
|
|
|
|
configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
|
|
|
|
if (!ConfigDescription::parse(configStr, &config)) {
|
|
|
|
if (outError) {
|
|
|
|
std::stringstream errStr;
|
|
|
|
errStr << "invalid configuration '" << configStr << "'";
|
|
|
|
*outError = errStr.str();
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
dirStr = dirStr.substr(0, dashPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string& filename = parts[parts.size() - 1];
|
|
|
|
StringPiece name = filename;
|
|
|
|
StringPiece extension;
|
|
|
|
size_t dotPos = filename.find('.');
|
|
|
|
if (dotPos != std::string::npos) {
|
|
|
|
extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
|
|
|
|
name = name.substr(0, dotPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ResourcePathData{
|
2015-11-24 19:11:46 -08:00
|
|
|
Source(path),
|
2015-08-14 14:26:04 -07:00
|
|
|
util::utf8ToUtf16(dirStr),
|
|
|
|
util::utf8ToUtf16(name),
|
|
|
|
extension.toString(),
|
|
|
|
configStr.toString(),
|
|
|
|
config
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CompileOptions {
|
|
|
|
std::string outputPath;
|
2015-11-24 19:11:46 -08:00
|
|
|
Maybe<std::string> resDir;
|
2015-12-17 13:03:11 -08:00
|
|
|
bool pseudolocalize = false;
|
2016-01-11 10:42:19 -08:00
|
|
|
bool legacyMode = false;
|
2015-08-14 14:26:04 -07:00
|
|
|
bool verbose = false;
|
|
|
|
};
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
static std::string buildIntermediateFilename(const ResourcePathData& data) {
|
2015-08-14 14:26:04 -07:00
|
|
|
std::stringstream name;
|
|
|
|
name << data.resourceDir;
|
|
|
|
if (!data.configStr.empty()) {
|
|
|
|
name << "-" << data.configStr;
|
|
|
|
}
|
2016-01-11 13:10:24 -08:00
|
|
|
name << "_" << data.name;
|
|
|
|
if (!data.extension.empty()) {
|
|
|
|
name << "." << data.extension;
|
|
|
|
}
|
|
|
|
name << ".flat";
|
2015-11-24 19:11:46 -08:00
|
|
|
return name.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isHidden(const StringPiece& filename) {
|
|
|
|
return util::stringStartsWith<char>(filename, ".");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Walks the res directory structure, looking for resource files.
|
|
|
|
*/
|
|
|
|
static bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
|
|
|
|
std::vector<ResourcePathData>* outPathData) {
|
|
|
|
const std::string& rootDir = options.resDir.value();
|
|
|
|
std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()), closedir);
|
|
|
|
if (!d) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage() << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (struct dirent* entry = readdir(d.get())) {
|
|
|
|
if (isHidden(entry->d_name)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string prefixPath = rootDir;
|
|
|
|
file::appendPath(&prefixPath, entry->d_name);
|
|
|
|
|
|
|
|
if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()), closedir);
|
|
|
|
if (!subDir) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage() << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (struct dirent* leafEntry = readdir(subDir.get())) {
|
|
|
|
if (isHidden(leafEntry->d_name)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string fullPath = prefixPath;
|
|
|
|
file::appendPath(&fullPath, leafEntry->d_name);
|
|
|
|
|
|
|
|
std::string errStr;
|
|
|
|
Maybe<ResourcePathData> pathData = extractResourcePathData(fullPath, &errStr);
|
|
|
|
if (!pathData) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage() << errStr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
outPathData->push_back(std::move(pathData.value()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool compileTable(IAaptContext* context, const CompileOptions& options,
|
2015-11-24 19:11:46 -08:00
|
|
|
const ResourcePathData& pathData, IArchiveWriter* writer,
|
|
|
|
const std::string& outputPath) {
|
2015-08-14 14:26:04 -07:00
|
|
|
ResourceTable table;
|
|
|
|
{
|
|
|
|
std::ifstream fin(pathData.source.path, std::ifstream::binary);
|
|
|
|
if (!fin) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Parse the values file from XML.
|
2015-11-16 17:35:44 -08:00
|
|
|
xml::XmlPullParser xmlParser(fin);
|
2015-11-04 13:51:45 -08:00
|
|
|
|
|
|
|
ResourceParserOptions parserOptions;
|
2016-01-11 10:42:19 -08:00
|
|
|
parserOptions.errorOnPositionalArguments = !options.legacyMode;
|
2015-11-04 13:51:45 -08:00
|
|
|
|
|
|
|
// If the filename includes donottranslate, then the default translatable is false.
|
|
|
|
parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos;
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
ResourceParser resParser(context->getDiagnostics(), &table, pathData.source,
|
2015-11-04 13:51:45 -08:00
|
|
|
pathData.config, parserOptions);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!resParser.parse(&xmlParser)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fin.close();
|
|
|
|
}
|
|
|
|
|
2015-12-17 13:03:11 -08:00
|
|
|
if (options.pseudolocalize) {
|
|
|
|
// Generate pseudo-localized strings (en-XA and ar-XB).
|
|
|
|
// These are created as weak symbols, and are only generated from default configuration
|
|
|
|
// strings and plurals.
|
|
|
|
PseudolocaleGenerator pseudolocaleGenerator;
|
|
|
|
if (!pseudolocaleGenerator.consume(context, &table)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-07 11:51:23 -08:00
|
|
|
// Ensure we have the compilation package at least.
|
|
|
|
table.createPackage(context->getCompilationPackage());
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
// Assign an ID to any package that has resources.
|
2015-11-07 11:51:23 -08:00
|
|
|
for (auto& pkg : table.packages) {
|
|
|
|
if (!pkg->id) {
|
|
|
|
// If no package ID was set while parsing (public identifiers), auto assign an ID.
|
|
|
|
pkg->id = context->getPackageId();
|
|
|
|
}
|
2015-10-13 11:37:10 -07:00
|
|
|
}
|
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
// Create the file/zip entry.
|
|
|
|
if (!writer->startEntry(outputPath, 0)) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
|
|
|
|
|
|
|
|
// Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
|
|
|
|
{
|
|
|
|
google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
|
|
|
|
|
|
|
|
if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!writer->finishEntry()) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to finish entry");
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
2016-02-04 15:59:23 -08:00
|
|
|
return true;
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
static bool writeHeaderAndBufferToWriter(const StringPiece& outputPath, const ResourceFile& file,
|
|
|
|
const BigBuffer& buffer, IArchiveWriter* writer,
|
|
|
|
IDiagnostics* diag) {
|
|
|
|
// Start the entry so we can write the header.
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!writer->startEntry(outputPath, 0)) {
|
2016-02-04 15:59:23 -08:00
|
|
|
diag->error(DiagMessage(outputPath) << "failed to open file");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the header.
|
|
|
|
std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
|
|
|
|
|
|
|
|
{
|
|
|
|
// The stream must be destroyed before we finish the entry, or else
|
|
|
|
// some data won't be flushed.
|
|
|
|
// Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
|
|
|
|
// interface.
|
|
|
|
google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
|
|
|
|
CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
|
|
|
|
for (const BigBuffer::Block& block : buffer) {
|
|
|
|
if (!outputStream.Write(block.buffer.get(), block.size)) {
|
|
|
|
diag->error(DiagMessage(outputPath) << "failed to write data");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!writer->finishEntry()) {
|
|
|
|
diag->error(DiagMessage(outputPath) << "failed to finish writing data");
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
2016-02-04 15:59:23 -08:00
|
|
|
return true;
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
static bool writeHeaderAndMmapToWriter(const StringPiece& outputPath, const ResourceFile& file,
|
|
|
|
const android::FileMap& map, IArchiveWriter* writer,
|
|
|
|
IDiagnostics* diag) {
|
|
|
|
// Start the entry so we can write the header.
|
|
|
|
if (!writer->startEntry(outputPath, 0)) {
|
|
|
|
diag->error(DiagMessage(outputPath) << "failed to open file");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the header.
|
|
|
|
std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
|
|
|
|
|
|
|
|
{
|
|
|
|
// The stream must be destroyed before we finish the entry, or else
|
|
|
|
// some data won't be flushed.
|
|
|
|
// Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
|
|
|
|
// interface.
|
|
|
|
google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
|
|
|
|
CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
|
|
|
|
if (!outputStream.Write(map.getDataPtr(), map.getDataLength())) {
|
|
|
|
diag->error(DiagMessage(outputPath) << "failed to write data");
|
|
|
|
return false;
|
2015-11-24 19:11:46 -08:00
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
2015-11-24 19:11:46 -08:00
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
if (!writer->finishEntry()) {
|
|
|
|
diag->error(DiagMessage(outputPath) << "failed to finish writing data");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool compileXml(IAaptContext* context, const CompileOptions& options,
|
2015-11-24 19:11:46 -08:00
|
|
|
const ResourcePathData& pathData, IArchiveWriter* writer,
|
|
|
|
const std::string& outputPath) {
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2015-11-16 17:35:44 -08:00
|
|
|
std::unique_ptr<xml::XmlResource> xmlRes;
|
2015-08-14 14:26:04 -07:00
|
|
|
{
|
|
|
|
std::ifstream fin(pathData.source.path, std::ifstream::binary);
|
|
|
|
if (!fin) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
|
|
|
|
|
|
|
|
fin.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!xmlRes) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect IDs that are defined here.
|
|
|
|
XmlIdCollector collector;
|
|
|
|
if (!collector.consume(context, xmlRes.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
xmlRes->file.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
|
2015-08-14 14:26:04 -07:00
|
|
|
xmlRes->file.config = pathData.config;
|
|
|
|
xmlRes->file.source = pathData.source;
|
|
|
|
|
|
|
|
BigBuffer buffer(1024);
|
|
|
|
XmlFlattenerOptions xmlFlattenerOptions;
|
|
|
|
xmlFlattenerOptions.keepRawValues = true;
|
2016-02-04 15:59:23 -08:00
|
|
|
XmlFlattener flattener(&buffer, xmlFlattenerOptions);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!flattener.consume(context, xmlRes.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
if (!writeHeaderAndBufferToWriter(outputPath, xmlRes->file, buffer, writer,
|
|
|
|
context->getDiagnostics())) {
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
2016-02-04 15:59:23 -08:00
|
|
|
return true;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool compilePng(IAaptContext* context, const CompileOptions& options,
|
2015-11-24 19:11:46 -08:00
|
|
|
const ResourcePathData& pathData, IArchiveWriter* writer,
|
|
|
|
const std::string& outputPath) {
|
2015-08-14 14:26:04 -07:00
|
|
|
BigBuffer buffer(4096);
|
|
|
|
ResourceFile resFile;
|
2015-11-24 19:11:46 -08:00
|
|
|
resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
|
2015-08-14 14:26:04 -07:00
|
|
|
resFile.config = pathData.config;
|
|
|
|
resFile.source = pathData.source;
|
|
|
|
|
|
|
|
{
|
|
|
|
std::ifstream fin(pathData.source.path, std::ifstream::binary);
|
|
|
|
if (!fin) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Png png(context->getDiagnostics());
|
2016-02-04 15:59:23 -08:00
|
|
|
if (!png.process(pathData.source, &fin, &buffer, {})) {
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
|
|
|
|
context->getDiagnostics())) {
|
2015-08-14 14:26:04 -07:00
|
|
|
return false;
|
|
|
|
}
|
2016-02-04 15:59:23 -08:00
|
|
|
return true;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool compileFile(IAaptContext* context, const CompileOptions& options,
|
2015-11-24 19:11:46 -08:00
|
|
|
const ResourcePathData& pathData, IArchiveWriter* writer,
|
|
|
|
const std::string& outputPath) {
|
2015-08-14 14:26:04 -07:00
|
|
|
BigBuffer buffer(256);
|
|
|
|
ResourceFile resFile;
|
2015-11-24 19:11:46 -08:00
|
|
|
resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
|
2015-08-14 14:26:04 -07:00
|
|
|
resFile.config = pathData.config;
|
|
|
|
resFile.source = pathData.source;
|
|
|
|
|
|
|
|
std::string errorStr;
|
|
|
|
Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
|
|
|
|
if (!f) {
|
|
|
|
context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-02-04 15:59:23 -08:00
|
|
|
if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
|
|
|
|
context->getDiagnostics())) {
|
2016-01-11 13:10:24 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
class CompileContext : public IAaptContext {
|
|
|
|
public:
|
2016-02-13 20:26:45 -08:00
|
|
|
void setVerbose(bool val) {
|
|
|
|
mVerbose = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool verbose() override {
|
|
|
|
return mVerbose;
|
|
|
|
}
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
IDiagnostics* getDiagnostics() override {
|
|
|
|
return &mDiagnostics;
|
|
|
|
}
|
|
|
|
|
|
|
|
NameMangler* getNameMangler() override {
|
|
|
|
abort();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-02-18 18:33:06 -08:00
|
|
|
const std::u16string& getCompilationPackage() override {
|
|
|
|
static std::u16string empty;
|
|
|
|
return empty;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t getPackageId() override {
|
2015-10-13 11:37:10 -07:00
|
|
|
return 0x0;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
2016-02-18 18:33:06 -08:00
|
|
|
SymbolTable* getExternalSymbols() override {
|
2015-08-14 14:26:04 -07:00
|
|
|
abort();
|
|
|
|
return nullptr;
|
|
|
|
}
|
2016-02-18 18:33:06 -08:00
|
|
|
|
|
|
|
private:
|
|
|
|
StdErrDiagnostics mDiagnostics;
|
|
|
|
bool mVerbose = false;
|
|
|
|
|
2015-08-14 14:26:04 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
|
|
|
|
*/
|
|
|
|
int compile(const std::vector<StringPiece>& args) {
|
2016-02-13 20:26:45 -08:00
|
|
|
CompileContext context;
|
2015-08-14 14:26:04 -07:00
|
|
|
CompileOptions options;
|
|
|
|
|
2016-02-13 20:26:45 -08:00
|
|
|
bool verbose = false;
|
2015-08-14 14:26:04 -07:00
|
|
|
Flags flags = Flags()
|
|
|
|
.requiredFlag("-o", "Output path", &options.outputPath)
|
2015-11-24 19:11:46 -08:00
|
|
|
.optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
|
2015-12-17 13:03:11 -08:00
|
|
|
.optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
|
|
|
|
"(en-XA and ar-XB)", &options.pseudolocalize)
|
2016-01-11 10:42:19 -08:00
|
|
|
.optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
|
|
|
|
&options.legacyMode)
|
2016-02-13 20:26:45 -08:00
|
|
|
.optionalSwitch("-v", "Enables verbose logging", &verbose);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (!flags.parse("aapt2 compile", args, &std::cerr)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-02-13 20:26:45 -08:00
|
|
|
context.setVerbose(verbose);
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
std::unique_ptr<IArchiveWriter> archiveWriter;
|
2015-08-14 14:26:04 -07:00
|
|
|
|
|
|
|
std::vector<ResourcePathData> inputData;
|
2015-11-24 19:11:46 -08:00
|
|
|
if (options.resDir) {
|
|
|
|
if (!flags.getArgs().empty()) {
|
|
|
|
// Can't have both files and a resource directory.
|
|
|
|
context.getDiagnostics()->error(DiagMessage() << "files given but --dir specified");
|
|
|
|
flags.usage("aapt2 compile", &std::cerr);
|
|
|
|
return 1;
|
|
|
|
}
|
2015-08-14 14:26:04 -07:00
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!loadInputFilesFromDir(&context, options, &inputData)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
return 1;
|
|
|
|
}
|
2015-11-24 19:11:46 -08:00
|
|
|
|
|
|
|
archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
inputData.reserve(flags.getArgs().size());
|
|
|
|
|
|
|
|
// Collect data from the path for each input file.
|
|
|
|
for (const std::string& arg : flags.getArgs()) {
|
|
|
|
std::string errorStr;
|
|
|
|
if (Maybe<ResourcePathData> pathData = extractResourcePathData(arg, &errorStr)) {
|
|
|
|
inputData.push_back(std::move(pathData.value()));
|
|
|
|
} else {
|
|
|
|
context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg << ")");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(), options.outputPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!archiveWriter) {
|
|
|
|
return false;
|
2015-08-14 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool error = false;
|
|
|
|
for (ResourcePathData& pathData : inputData) {
|
|
|
|
if (options.verbose) {
|
|
|
|
context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pathData.resourceDir == u"values") {
|
|
|
|
// Overwrite the extension.
|
|
|
|
pathData.extension = "arsc";
|
|
|
|
|
2015-11-24 19:11:46 -08:00
|
|
|
const std::string outputFilename = buildIntermediateFilename(pathData);
|
|
|
|
if (!compileTable(&context, options, pathData, archiveWriter.get(), outputFilename)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2015-11-24 19:11:46 -08:00
|
|
|
const std::string outputFilename = buildIntermediateFilename(pathData);
|
2015-08-14 14:26:04 -07:00
|
|
|
if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
|
|
|
|
if (*type != ResourceType::kRaw) {
|
|
|
|
if (pathData.extension == "xml") {
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!compileXml(&context, options, pathData, archiveWriter.get(),
|
|
|
|
outputFilename)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
} else if (pathData.extension == "png" || pathData.extension == "9.png") {
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!compilePng(&context, options, pathData, archiveWriter.get(),
|
|
|
|
outputFilename)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
} else {
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!compileFile(&context, options, pathData, archiveWriter.get(),
|
|
|
|
outputFilename)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2015-11-24 19:11:46 -08:00
|
|
|
if (!compileFile(&context, options, pathData, archiveWriter.get(),
|
|
|
|
outputFilename)) {
|
2015-08-14 14:26:04 -07:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
context.getDiagnostics()->error(
|
|
|
|
DiagMessage() << "invalid file path '" << pathData.source << "'");
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace aapt
|