AAPT2: Compile --zip flag
Added a --zip flag similar to --dir that allows resources to be passed into "aapt2 compile" using a zip file. Also refactored Compile.cpp to be easier to mock and test in the future. Bug: 74574557 Test: aapt2_tests Change-Id: Idb90cb97e23a219525bdead38220cbf7bc6f3cab
This commit is contained in:
@@ -184,10 +184,7 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table
|
||||
std::unique_ptr<io::IFileCollectionIterator> iterator = apk_->Iterator();
|
||||
while (iterator->HasNext()) {
|
||||
io::IFile* file = iterator->Next();
|
||||
|
||||
std::string path = file->GetSource().path;
|
||||
// The name of the path has the format "<zip-file-name>@<path-to-file>".
|
||||
path = path.substr(path.find('@') + 1);
|
||||
|
||||
// Skip resources that are not referenced if requested.
|
||||
if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
|
||||
|
||||
@@ -31,12 +31,16 @@ namespace aapt {
|
||||
struct Source {
|
||||
std::string path;
|
||||
Maybe<size_t> line;
|
||||
Maybe<std::string> archive;
|
||||
|
||||
Source() = default;
|
||||
|
||||
inline Source(const android::StringPiece& path) : path(path.to_string()) { // NOLINT(implicit)
|
||||
}
|
||||
|
||||
inline Source(const android::StringPiece& path, const android::StringPiece& archive)
|
||||
: path(path.to_string()), archive(archive.to_string()) {}
|
||||
|
||||
inline Source(const android::StringPiece& path, size_t line)
|
||||
: path(path.to_string()), line(line) {}
|
||||
|
||||
@@ -45,10 +49,14 @@ struct Source {
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
if (line) {
|
||||
return ::android::base::StringPrintf("%s:%zd", path.c_str(), line.value());
|
||||
std::string s = path;
|
||||
if (archive) {
|
||||
s = ::android::base::StringPrintf("%s@%s", archive.value().c_str(), s.c_str());
|
||||
}
|
||||
return path;
|
||||
if (line) {
|
||||
s = ::android::base::StringPrintf("%s:%zd", s.c_str(), line.value());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -41,8 +41,10 @@
|
||||
#include "format/proto/ProtoSerialize.h"
|
||||
#include "io/BigBufferStream.h"
|
||||
#include "io/FileStream.h"
|
||||
#include "io/FileSystem.h"
|
||||
#include "io/StringStream.h"
|
||||
#include "io/Util.h"
|
||||
#include "io/ZipArchive.h"
|
||||
#include "util/Files.h"
|
||||
#include "util/Maybe.h"
|
||||
#include "util/Util.h"
|
||||
@@ -135,81 +137,20 @@ static std::string BuildIntermediateContainerFilename(const ResourcePathData& da
|
||||
return name.str();
|
||||
}
|
||||
|
||||
static bool IsHidden(const StringPiece& filename) {
|
||||
return util::StartsWith(filename, ".");
|
||||
}
|
||||
|
||||
// Walks the res directory structure, looking for resource files.
|
||||
static bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
|
||||
std::vector<ResourcePathData>* out_path_data) {
|
||||
const std::string& root_dir = options.res_dir.value();
|
||||
std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
|
||||
if (!d) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(root_dir) << "failed to open directory: "
|
||||
<< SystemErrorCodeToString(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
while (struct dirent* entry = readdir(d.get())) {
|
||||
if (IsHidden(entry->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string prefix_path = root_dir;
|
||||
file::AppendPath(&prefix_path, entry->d_name);
|
||||
|
||||
if (file::GetFileType(prefix_path) != file::FileType::kDirectory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir);
|
||||
if (!subdir) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(prefix_path) << "failed to open directory: "
|
||||
<< SystemErrorCodeToString(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
while (struct dirent* leaf_entry = readdir(subdir.get())) {
|
||||
if (IsHidden(leaf_entry->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string full_path = prefix_path;
|
||||
file::AppendPath(&full_path, leaf_entry->d_name);
|
||||
|
||||
std::string err_str;
|
||||
Maybe<ResourcePathData> path_data = ExtractResourcePathData(full_path, &err_str);
|
||||
if (!path_data) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(full_path) << err_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
out_path_data->push_back(std::move(path_data.value()));
|
||||
}
|
||||
}
|
||||
|
||||
// File-system directory enumeration order is platform-dependent. Sort the result to remove any
|
||||
// inconsistencies between platforms.
|
||||
std::sort(
|
||||
out_path_data->begin(), out_path_data->end(),
|
||||
[](const ResourcePathData& a, const ResourcePathData& b) { return a.source < b.source; });
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CompileTable(IAaptContext* context, const CompileOptions& options,
|
||||
const ResourcePathData& path_data, IArchiveWriter* writer,
|
||||
const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
|
||||
const std::string& output_path) {
|
||||
ResourceTable table;
|
||||
{
|
||||
FileInputStream fin(path_data.source.path);
|
||||
if (fin.HadError()) {
|
||||
auto fin = file->OpenInputStream();
|
||||
if (fin->HadError()) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
|
||||
<< "failed to open file: " << fin.GetError());
|
||||
<< "failed to open file: " << fin->GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the values file from XML.
|
||||
xml::XmlPullParser xml_parser(&fin);
|
||||
xml::XmlPullParser xml_parser(fin.get());
|
||||
|
||||
ResourceParserOptions parser_options;
|
||||
parser_options.error_on_positional_arguments = !options.legacy_mode;
|
||||
@@ -222,7 +163,7 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
|
||||
parser_options.visibility = options.visibility;
|
||||
|
||||
ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
|
||||
parser_options);
|
||||
parser_options);
|
||||
if (!res_parser.Parse(&xml_parser)) {
|
||||
return false;
|
||||
}
|
||||
@@ -408,7 +349,7 @@ static bool IsValidFile(IAaptContext* context, const std::string& input_path) {
|
||||
}
|
||||
|
||||
static bool CompileXml(IAaptContext* context, const CompileOptions& options,
|
||||
const ResourcePathData& path_data, IArchiveWriter* writer,
|
||||
const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
|
||||
const std::string& output_path) {
|
||||
if (context->IsVerbose()) {
|
||||
context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling XML");
|
||||
@@ -416,18 +357,17 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options,
|
||||
|
||||
std::unique_ptr<xml::XmlResource> xmlres;
|
||||
{
|
||||
FileInputStream fin(path_data.source.path);
|
||||
if (fin.HadError()) {
|
||||
auto fin = file->OpenInputStream();
|
||||
if (fin->HadError()) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
|
||||
<< "failed to open file: " << fin.GetError());
|
||||
<< "failed to open file: " << fin->GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
|
||||
}
|
||||
|
||||
if (!xmlres) {
|
||||
return false;
|
||||
xmlres = xml::Inflate(fin.get(), context->GetDiagnostics(), path_data.source);
|
||||
if (!xmlres) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
xmlres->file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
|
||||
@@ -508,7 +448,7 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options,
|
||||
}
|
||||
|
||||
static bool CompilePng(IAaptContext* context, const CompileOptions& options,
|
||||
const ResourcePathData& path_data, IArchiveWriter* writer,
|
||||
const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
|
||||
const std::string& output_path) {
|
||||
if (context->IsVerbose()) {
|
||||
context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling PNG");
|
||||
@@ -522,15 +462,17 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options,
|
||||
res_file.type = ResourceFile::Type::kPng;
|
||||
|
||||
{
|
||||
std::string content;
|
||||
if (!android::base::ReadFileToString(path_data.source.path, &content,
|
||||
true /*follow_symlinks*/)) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(path_data.source)
|
||||
<< "failed to open file: "
|
||||
<< SystemErrorCodeToString(errno));
|
||||
auto data = file->OpenAsData();
|
||||
if (!data) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(path_data.source) << "failed to open file ");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the file as a string
|
||||
char buffer_2[data->size()];
|
||||
memcpy(&buffer_2, data->data(), data->size());
|
||||
StringPiece content(buffer_2, data->size());
|
||||
|
||||
BigBuffer crunched_png_buffer(4096);
|
||||
io::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
|
||||
|
||||
@@ -598,7 +540,7 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options,
|
||||
if (context->IsVerbose()) {
|
||||
// For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
|
||||
// This will help catch exotic cases where the new code may generate larger PNGs.
|
||||
std::stringstream legacy_stream(content);
|
||||
std::stringstream legacy_stream(content.to_string());
|
||||
BigBuffer legacy_buffer(4096);
|
||||
Png png(context->GetDiagnostics());
|
||||
if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
|
||||
@@ -612,41 +554,31 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options,
|
||||
}
|
||||
|
||||
io::BigBufferInputStream buffer_in(&buffer);
|
||||
if (!WriteHeaderAndDataToWriter(output_path, res_file, &buffer_in, writer,
|
||||
context->GetDiagnostics())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return WriteHeaderAndDataToWriter(output_path, res_file, &buffer_in, writer,
|
||||
context->GetDiagnostics());
|
||||
}
|
||||
|
||||
static bool CompileFile(IAaptContext* context, const CompileOptions& options,
|
||||
const ResourcePathData& path_data, IArchiveWriter* writer,
|
||||
const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
|
||||
const std::string& output_path) {
|
||||
if (context->IsVerbose()) {
|
||||
context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling file");
|
||||
}
|
||||
|
||||
BigBuffer buffer(256);
|
||||
ResourceFile res_file;
|
||||
res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
|
||||
res_file.config = path_data.config;
|
||||
res_file.source = path_data.source;
|
||||
res_file.type = ResourceFile::Type::kUnknown;
|
||||
|
||||
std::string error_str;
|
||||
Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str);
|
||||
if (!f) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(path_data.source) << "failed to mmap file: "
|
||||
<< error_str);
|
||||
auto data = file->OpenAsData();
|
||||
if (!data) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(path_data.source) << "failed to open file ");
|
||||
return false;
|
||||
}
|
||||
|
||||
io::MmappedData mmapped_in(std::move(f.value()));
|
||||
if (!WriteHeaderAndDataToWriter(output_path, res_file, &mmapped_in, writer,
|
||||
context->GetDiagnostics())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return WriteHeaderAndDataToWriter(output_path, res_file, data.get(), writer,
|
||||
context->GetDiagnostics());
|
||||
}
|
||||
|
||||
class CompileContext : public IAaptContext {
|
||||
@@ -701,6 +633,79 @@ class CompileContext : public IAaptContext {
|
||||
bool verbose_ = false;
|
||||
};
|
||||
|
||||
int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
|
||||
CompileOptions& options) {
|
||||
bool error = false;
|
||||
|
||||
// Iterate over the input files in a stable, platform-independent manner
|
||||
auto file_iterator = inputs->Iterator();
|
||||
while (file_iterator->HasNext()) {
|
||||
auto file = file_iterator->Next();
|
||||
std::string path = file->GetSource().path;
|
||||
|
||||
// Skip hidden input files
|
||||
if (file::IsHidden(path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!options.res_zip && !IsValidFile(context, path)) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract resource type information from the full path
|
||||
std::string err_str;
|
||||
ResourcePathData path_data;
|
||||
if (auto maybe_path_data = ExtractResourcePathData(path, &err_str)) {
|
||||
path_data = maybe_path_data.value();
|
||||
} else {
|
||||
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine how to compile the file based on its type.
|
||||
auto compile_func = &CompileFile;
|
||||
if (path_data.resource_dir == "values" && path_data.extension == "xml") {
|
||||
compile_func = &CompileTable;
|
||||
// We use a different extension (not necessary anymore, but avoids altering the existing
|
||||
// build system logic).
|
||||
path_data.extension = "arsc";
|
||||
|
||||
} else if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
|
||||
if (*type != ResourceType::kRaw) {
|
||||
if (path_data.extension == "xml") {
|
||||
compile_func = &CompileXml;
|
||||
} else if ((!options.no_png_crunch && path_data.extension == "png")
|
||||
|| path_data.extension == "9.png") {
|
||||
compile_func = &CompilePng;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context->GetDiagnostics()->Error(DiagMessage()
|
||||
<< "invalid file path '" << path_data.source << "'");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Treat periods as a reserved character that should not be present in a file name
|
||||
// Legacy support for AAPT which did not reserve periods
|
||||
if (compile_func != &CompileFile && !options.legacy_mode
|
||||
&& std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) {
|
||||
error = true;
|
||||
context->GetDiagnostics()->Error(DiagMessage(file->GetSource())
|
||||
<< "file name cannot contain '.' other than for"
|
||||
<< " specifying the extension");
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string out_path = BuildIntermediateContainerFilename(path_data);
|
||||
error |= !compile_func(context, options, path_data, file, output_writer, out_path);
|
||||
}
|
||||
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
int CompileCommand::Action(const std::vector<std::string>& args) {
|
||||
CompileContext context(diagnostic_);
|
||||
context.SetVerbose(options_.verbose);
|
||||
@@ -720,37 +725,55 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IFileCollection> file_collection;
|
||||
std::unique_ptr<IArchiveWriter> archive_writer;
|
||||
|
||||
std::vector<ResourcePathData> input_data;
|
||||
if (options_.res_dir) {
|
||||
// Collect the resources files to compile
|
||||
if (options_.res_dir && options_.res_zip) {
|
||||
context.GetDiagnostics()->Error(DiagMessage()
|
||||
<< "only one of --dir and --zip can be specified");
|
||||
return 1;
|
||||
} else if (options_.res_dir) {
|
||||
if (!args.empty()) {
|
||||
// Can't have both files and a resource directory.
|
||||
context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
|
||||
Usage(&std::cerr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!LoadInputFilesFromDir(&context, options_, &input_data)) {
|
||||
// Load the files from the res directory
|
||||
std::string err;
|
||||
file_collection = io::FileCollection::Create(options_.res_dir.value(), &err);
|
||||
if (!file_collection) {
|
||||
context.GetDiagnostics()->Error(DiagMessage(options_.res_dir.value()) << err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
|
||||
} else if (options_.res_zip) {
|
||||
if (!args.empty()) {
|
||||
context.GetDiagnostics()->Error(DiagMessage() << "files given but --zip specified");
|
||||
Usage(&std::cerr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load a zip file containing a res directory
|
||||
std::string err;
|
||||
file_collection = io::ZipFileCollection::Create(options_.res_zip.value(), &err);
|
||||
if (!file_collection) {
|
||||
context.GetDiagnostics()->Error(DiagMessage(options_.res_zip.value()) << err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
|
||||
} else {
|
||||
input_data.reserve(args.size());
|
||||
auto collection = util::make_unique<io::FileCollection>();
|
||||
|
||||
// Collect data from the path for each input file.
|
||||
for (const std::string& arg : args) {
|
||||
std::string error_str;
|
||||
if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
|
||||
input_data.push_back(std::move(path_data.value()));
|
||||
} else {
|
||||
context.GetDiagnostics()->Error(DiagMessage() << error_str << " (" << arg << ")");
|
||||
return 1;
|
||||
}
|
||||
collection->InsertFile(arg);
|
||||
}
|
||||
|
||||
file_collection = std::move(collection);
|
||||
archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
|
||||
}
|
||||
|
||||
@@ -758,57 +781,7 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
for (ResourcePathData& path_data : input_data) {
|
||||
if (options_.verbose) {
|
||||
context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
|
||||
}
|
||||
|
||||
if (!IsValidFile(&context, path_data.source.path)) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine how to compile the file based on its type.
|
||||
auto compile_func = &CompileFile;
|
||||
if (path_data.resource_dir == "values" && path_data.extension == "xml") {
|
||||
compile_func = &CompileTable;
|
||||
// We use a different extension (not necessary anymore, but avoids altering the existing
|
||||
// build system logic).
|
||||
path_data.extension = "arsc";
|
||||
|
||||
} else if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
|
||||
if (*type != ResourceType::kRaw) {
|
||||
if (path_data.extension == "xml") {
|
||||
compile_func = &CompileXml;
|
||||
} else if ((!options_.no_png_crunch && path_data.extension == "png")
|
||||
|| path_data.extension == "9.png") {
|
||||
compile_func = &CompilePng;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context.GetDiagnostics()->Error(DiagMessage()
|
||||
<< "invalid file path '" << path_data.source << "'");
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Treat periods as a reserved character that should not be present in a file name
|
||||
// Legacy support for AAPT which did not reserve periods
|
||||
if (compile_func != &CompileFile && !options_.legacy_mode
|
||||
&& std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) {
|
||||
error = true;
|
||||
context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path
|
||||
<< "' name cannot contain '.' other than for"
|
||||
<< "specifying the extension");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compile the file.
|
||||
const std::string out_path = BuildIntermediateContainerFilename(path_data);
|
||||
error |= !compile_func(&context, options_, path_data, archive_writer.get(), out_path);
|
||||
}
|
||||
return error ? 1 : 0;
|
||||
return Compile(&context, file_collection.get(), archive_writer.get(), options_);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#define AAPT2_COMPILE_H
|
||||
|
||||
#include "androidfw/StringPiece.h"
|
||||
|
||||
#include "format/Archive.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "Command.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "ResourceTable.h"
|
||||
@@ -28,6 +29,7 @@ namespace aapt {
|
||||
struct CompileOptions {
|
||||
std::string output_path;
|
||||
Maybe<std::string> res_dir;
|
||||
Maybe<std::string> res_zip;
|
||||
Maybe<std::string> generate_text_symbols_path;
|
||||
Maybe<Visibility::Level> visibility;
|
||||
bool pseudolocalize = false;
|
||||
@@ -36,6 +38,7 @@ struct CompileOptions {
|
||||
bool verbose = false;
|
||||
};
|
||||
|
||||
/** Parses flags and compiles resources to be used in linking. */
|
||||
class CompileCommand : public Command {
|
||||
public:
|
||||
explicit CompileCommand(IDiagnostics* diagnostic) : Command("compile", "c"),
|
||||
@@ -43,6 +46,8 @@ class CompileCommand : public Command {
|
||||
SetDescription("Compiles resources to be linked into an apk.");
|
||||
AddRequiredFlag("-o", "Output path", &options_.output_path);
|
||||
AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir);
|
||||
AddOptionalFlag("--zip", "Zip file containing the res directory to scan for resources",
|
||||
&options_.res_zip);
|
||||
AddOptionalFlag("--output-text-symbols",
|
||||
"Generates a text file containing the resource symbols in the\n"
|
||||
"specified file", &options_.generate_text_symbols_path);
|
||||
@@ -51,10 +56,10 @@ class CompileCommand : public Command {
|
||||
AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
|
||||
AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
|
||||
&options_.legacy_mode);
|
||||
AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
|
||||
AddOptionalFlag("--visibility",
|
||||
"Sets the visibility of the compiled resources to the specified\n"
|
||||
"level. Accepted levels: public, private, default", &visibility_);
|
||||
AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
|
||||
}
|
||||
|
||||
int Action(const std::vector<std::string>& args) override;
|
||||
@@ -65,6 +70,8 @@ class CompileCommand : public Command {
|
||||
Maybe<std::string> visibility_;
|
||||
};
|
||||
|
||||
int Compile(IAaptContext* context, io::IFileCollection* inputs,
|
||||
IArchiveWriter* output_writer, CompileOptions& options);
|
||||
}// namespace aapt
|
||||
|
||||
#endif //AAPT2_COMPILE_H
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "android-base/file.h"
|
||||
#include "io/StringStream.h"
|
||||
#include "io/ZipArchive.h"
|
||||
#include "java/AnnotationProcessor.h"
|
||||
#include "test/Test.h"
|
||||
|
||||
@@ -29,7 +30,6 @@ int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
|
||||
args.push_back(path);
|
||||
args.push_back("-o");
|
||||
args.push_back(outDir);
|
||||
args.push_back("-v");
|
||||
if (legacy) {
|
||||
args.push_back("--legacy");
|
||||
}
|
||||
@@ -94,4 +94,56 @@ TEST(CompilerTest, MultiplePeriods) {
|
||||
ASSERT_EQ(remove(path5_out.c_str()), 0);
|
||||
}
|
||||
|
||||
}
|
||||
TEST(CompilerTest, DirInput) {
|
||||
StdErrDiagnostics diag;
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
|
||||
const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath())
|
||||
+ "/integration-tests/CompileTest/DirInput/res";
|
||||
const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
|
||||
+ "/integration-tests/CompileTest/DirInput/compiled.flata";
|
||||
remove(kOutputFlata.c_str());
|
||||
|
||||
std::vector<android::StringPiece> args;
|
||||
args.push_back("--dir");
|
||||
args.push_back(kResDir);
|
||||
args.push_back("-o");
|
||||
args.push_back(kOutputFlata);
|
||||
ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
|
||||
|
||||
// Check for the presence of the compiled files
|
||||
std::string err;
|
||||
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
|
||||
ASSERT_NE(zip, nullptr) << err;
|
||||
ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
|
||||
ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
|
||||
ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
|
||||
ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST(CompilerTest, ZipInput) {
|
||||
StdErrDiagnostics diag;
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
|
||||
const std::string kResZip = android::base::Dirname(android::base::GetExecutablePath())
|
||||
+ "/integration-tests/CompileTest/ZipInput/res.zip";
|
||||
const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
|
||||
+ "/integration-tests/CompileTest/ZipInput/compiled.flata";
|
||||
remove(kOutputFlata.c_str());
|
||||
|
||||
std::vector<android::StringPiece> args;
|
||||
args.push_back("--zip");
|
||||
args.push_back(kResZip);
|
||||
args.push_back("-o");
|
||||
args.push_back(kOutputFlata);
|
||||
ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
|
||||
|
||||
// Check for the presence of the compiled files
|
||||
std::string err;
|
||||
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
|
||||
ASSERT_NE(zip, nullptr) << err;
|
||||
ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
|
||||
ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
|
||||
ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
|
||||
ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
@@ -105,10 +105,7 @@ bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer
|
||||
std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
|
||||
while (iterator->HasNext()) {
|
||||
io::IFile* file = iterator->Next();
|
||||
|
||||
std::string path = file->GetSource().path;
|
||||
// The name of the path has the format "<zip-file-name>@<path-to-file>".
|
||||
path = path.substr(path.find('@') + 1);
|
||||
|
||||
// Manifest, resource table and resources have already been taken care of.
|
||||
if (path == kAndroidManifestPath ||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 103 B |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
</resources>
|
||||
BIN
tools/aapt2/integration-tests/CompileTest/ZipInput/res.zip
Normal file
BIN
tools/aapt2/integration-tests/CompileTest/ZipInput/res.zip
Normal file
Binary file not shown.
@@ -16,6 +16,9 @@
|
||||
|
||||
#include "io/FileSystem.h"
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "android-base/errors.h"
|
||||
#include "androidfw/StringPiece.h"
|
||||
#include "utils/FileMap.h"
|
||||
|
||||
@@ -26,6 +29,7 @@
|
||||
#include "util/Util.h"
|
||||
|
||||
using ::android::StringPiece;
|
||||
using ::android::base::SystemErrorCodeToString;
|
||||
|
||||
namespace aapt {
|
||||
namespace io {
|
||||
@@ -64,6 +68,50 @@ IFile* FileCollectionIterator::Next() {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<FileCollection> FileCollection::Create(const android::StringPiece& root,
|
||||
std::string* outError) {
|
||||
std::unique_ptr<FileCollection> collection =
|
||||
std::unique_ptr<FileCollection>(new FileCollection());
|
||||
|
||||
std::unique_ptr<DIR, decltype(closedir) *> d(opendir(root.data()), closedir);
|
||||
if (!d) {
|
||||
*outError = "failed to open directory: " + SystemErrorCodeToString(errno);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (struct dirent *entry = readdir(d.get())) {
|
||||
std::string prefix_path = root.to_string();
|
||||
file::AppendPath(&prefix_path, entry->d_name);
|
||||
|
||||
// The directory to iterate over looking for files
|
||||
if (file::GetFileType(prefix_path) != file::FileType::kDirectory
|
||||
|| file::IsHidden(prefix_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir);
|
||||
if (!subdir) {
|
||||
*outError = "failed to open directory: " + SystemErrorCodeToString(errno);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (struct dirent* leaf_entry = readdir(subdir.get())) {
|
||||
std::string full_path = prefix_path;
|
||||
file::AppendPath(&full_path, leaf_entry->d_name);
|
||||
|
||||
// Do not add folders to the file collection
|
||||
if (file::GetFileType(full_path) == file::FileType::kDirectory
|
||||
|| file::IsHidden(full_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
collection->InsertFile(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
IFile* FileCollection::InsertFile(const StringPiece& path) {
|
||||
return (files_[path.to_string()] = util::make_unique<RegularFile>(Source(path))).get();
|
||||
}
|
||||
|
||||
@@ -59,6 +59,10 @@ class FileCollection : public IFileCollection {
|
||||
public:
|
||||
FileCollection() = default;
|
||||
|
||||
/** Creates a file collection containing all files contained in the specified root directory. */
|
||||
static std::unique_ptr<FileCollection> Create(const android::StringPiece& path,
|
||||
std::string* outError);
|
||||
|
||||
// Adds a file located at path. Returns the IFile representation of that file.
|
||||
IFile* InsertFile(const android::StringPiece& path);
|
||||
IFile* FindFile(const android::StringPiece& path) override;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ziparchive/zip_archive.h"
|
||||
|
||||
#include "Source.h"
|
||||
#include "util/Files.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
using ::android::StringPiece;
|
||||
@@ -121,9 +122,14 @@ std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
|
||||
std::string zip_entry_path =
|
||||
std::string(reinterpret_cast<const char*>(zip_entry_name.name),
|
||||
zip_entry_name.name_length);
|
||||
std::string nested_path = path.to_string() + "@" + zip_entry_path;
|
||||
std::unique_ptr<IFile> file =
|
||||
util::make_unique<ZipFile>(collection->handle_, zip_data, Source(nested_path));
|
||||
|
||||
// Do not add folders to the file collection
|
||||
if (util::EndsWith(zip_entry_path, "/")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<IFile> file = util::make_unique<ZipFile>(collection->handle_, zip_data,
|
||||
Source(zip_entry_path, path.to_string()));
|
||||
collection->files_by_name_[zip_entry_path] = file.get();
|
||||
collection->files_.push_back(std::move(file));
|
||||
}
|
||||
@@ -132,6 +138,7 @@ std::unique_ptr<ZipFileCollection> ZipFileCollection::Create(
|
||||
if (out_error) *out_error = ErrorCodeString(result);
|
||||
return {};
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,10 @@ StringPiece GetExtension(const StringPiece& path) {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsHidden(const android::StringPiece& path) {
|
||||
return util::StartsWith(GetFilename(path), ".");
|
||||
}
|
||||
|
||||
void AppendPath(std::string* base, StringPiece part) {
|
||||
CHECK(base != nullptr);
|
||||
const bool base_has_trailing_sep = (!base->empty() && *(base->end() - 1) == sDirSep);
|
||||
|
||||
@@ -70,6 +70,9 @@ android::StringPiece GetFilename(const android::StringPiece& path);
|
||||
// of the path.
|
||||
android::StringPiece GetExtension(const android::StringPiece& path);
|
||||
|
||||
// Returns whether or not the name of the file or directory is a hidden file name
|
||||
bool IsHidden(const android::StringPiece& path);
|
||||
|
||||
// Converts a package name (com.android.app) to a path: com/android/app
|
||||
std::string PackageToPath(const android::StringPiece& package);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user