Loading tools/aapt2/LoadedApk.cpp +0 −3 Original line number Diff line number Diff line Loading @@ -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()) { Loading tools/aapt2/Source.h +10 −2 Original line number Diff line number Diff line Loading @@ -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) {} Loading @@ -45,10 +49,14 @@ struct Source { } std::string to_string() const { std::string s = path; if (archive) { s = ::android::base::StringPrintf("%s@%s", archive.value().c_str(), s.c_str()); } if (line) { return ::android::base::StringPrintf("%s:%zd", path.c_str(), line.value()); s = ::android::base::StringPrintf("%s:%zd", s.c_str(), line.value()); } return path; return s; } }; Loading tools/aapt2/cmd/Compile.cpp +138 −165 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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; Loading Loading @@ -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"); Loading @@ -416,19 +357,18 @@ 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); } 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); xmlres->file.config = path_data.config; Loading Loading @@ -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"); Loading @@ -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); Loading Loading @@ -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, {})) { Loading @@ -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 { Loading Loading @@ -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); Loading @@ -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); } Loading @@ -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 tools/aapt2/cmd/Compile.h +9 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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; Loading @@ -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"), Loading @@ -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); Loading @@ -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; Loading @@ -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 tools/aapt2/cmd/Compile_test.cpp +54 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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"); } Loading Loading @@ -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 No newline at end of file Loading
tools/aapt2/LoadedApk.cpp +0 −3 Original line number Diff line number Diff line Loading @@ -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()) { Loading
tools/aapt2/Source.h +10 −2 Original line number Diff line number Diff line Loading @@ -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) {} Loading @@ -45,10 +49,14 @@ struct Source { } std::string to_string() const { std::string s = path; if (archive) { s = ::android::base::StringPrintf("%s@%s", archive.value().c_str(), s.c_str()); } if (line) { return ::android::base::StringPrintf("%s:%zd", path.c_str(), line.value()); s = ::android::base::StringPrintf("%s:%zd", s.c_str(), line.value()); } return path; return s; } }; Loading
tools/aapt2/cmd/Compile.cpp +138 −165 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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; Loading Loading @@ -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"); Loading @@ -416,19 +357,18 @@ 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); } 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); xmlres->file.config = path_data.config; Loading Loading @@ -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"); Loading @@ -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); Loading Loading @@ -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, {})) { Loading @@ -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 { Loading Loading @@ -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); Loading @@ -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); } Loading @@ -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
tools/aapt2/cmd/Compile.h +9 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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; Loading @@ -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"), Loading @@ -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); Loading @@ -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; Loading @@ -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
tools/aapt2/cmd/Compile_test.cpp +54 −2 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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"); } Loading Loading @@ -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 No newline at end of file