diff options
Diffstat (limited to 'tools/aapt2/cmd/Compile.cpp')
-rw-r--r-- | tools/aapt2/cmd/Compile.cpp | 303 |
1 files changed, 138 insertions, 165 deletions
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 2ba2cf7926b0..62c19fbfcdd3 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -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 |