diff options
author | Ryan Mitchell <rtmitchell@google.com> | 2018-08-16 17:26:08 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-08-16 17:26:08 +0000 |
commit | ead275e19fc9ffbd751b3956a84e27844b97ffe7 (patch) | |
tree | f19915c04e52949cb29908ef1e0f6588ad98d766 | |
parent | a60283fdd1f2c67259e8b0c10d88ecfb60a1328b (diff) | |
parent | f3649d669059b924ce9eb3eb7909cbf0a2ed31a8 (diff) |
Merge "AAPT2: Compile --zip flag"
-rw-r--r-- | tools/aapt2/LoadedApk.cpp | 3 | ||||
-rw-r--r-- | tools/aapt2/Source.h | 12 | ||||
-rw-r--r-- | tools/aapt2/cmd/Compile.cpp | 303 | ||||
-rw-r--r-- | tools/aapt2/cmd/Compile.h | 11 | ||||
-rw-r--r-- | tools/aapt2/cmd/Compile_test.cpp | 56 | ||||
-rw-r--r-- | tools/aapt2/cmd/Convert.cpp | 3 | ||||
-rw-r--r-- | tools/aapt2/integration-tests/CompileTest/DirInput/res/drawable/image.png | bin | 0 -> 103 bytes | |||
-rw-r--r-- | tools/aapt2/integration-tests/CompileTest/DirInput/res/layout/layout.xml | 19 | ||||
-rw-r--r-- | tools/aapt2/integration-tests/CompileTest/DirInput/res/values/values.xml | 18 | ||||
-rw-r--r-- | tools/aapt2/integration-tests/CompileTest/ZipInput/res.zip | bin | 0 -> 2140 bytes | |||
-rw-r--r-- | tools/aapt2/io/FileSystem.cpp | 48 | ||||
-rw-r--r-- | tools/aapt2/io/FileSystem.h | 4 | ||||
-rw-r--r-- | tools/aapt2/io/ZipArchive.cpp | 13 | ||||
-rw-r--r-- | tools/aapt2/util/Files.cpp | 4 | ||||
-rw-r--r-- | tools/aapt2/util/Files.h | 3 |
15 files changed, 317 insertions, 180 deletions
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index b6ee1ad83ce3..a73d56c8f951 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -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()) { diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h index 0f312d6998f1..92934c343960 100644 --- a/tools/aapt2/Source.h +++ b/tools/aapt2/Source.h @@ -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 { + 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; } }; 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 diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h index 41519520fda1..c429d5f5d4b2 100644 --- a/tools/aapt2/cmd/Compile.h +++ b/tools/aapt2/cmd/Compile.h @@ -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 diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp index d21addf4a081..dd5198ce86da 100644 --- a/tools/aapt2/cmd/Compile_test.cpp +++ b/tools/aapt2/cmd/Compile_test.cpp @@ -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); } -}
\ No newline at end of file +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 diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index d57eaa1ba145..86b1f4c54deb 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -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 || diff --git a/tools/aapt2/integration-tests/CompileTest/DirInput/res/drawable/image.png b/tools/aapt2/integration-tests/CompileTest/DirInput/res/drawable/image.png Binary files differnew file mode 100644 index 000000000000..1a3731bbc8b8 --- /dev/null +++ b/tools/aapt2/integration-tests/CompileTest/DirInput/res/drawable/image.png diff --git a/tools/aapt2/integration-tests/CompileTest/DirInput/res/layout/layout.xml b/tools/aapt2/integration-tests/CompileTest/DirInput/res/layout/layout.xml new file mode 100644 index 000000000000..e5835ed1a169 --- /dev/null +++ b/tools/aapt2/integration-tests/CompileTest/DirInput/res/layout/layout.xml @@ -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" /> diff --git a/tools/aapt2/integration-tests/CompileTest/DirInput/res/values/values.xml b/tools/aapt2/integration-tests/CompileTest/DirInput/res/values/values.xml new file mode 100644 index 000000000000..62ab6526ef7e --- /dev/null +++ b/tools/aapt2/integration-tests/CompileTest/DirInput/res/values/values.xml @@ -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>
\ No newline at end of file diff --git a/tools/aapt2/integration-tests/CompileTest/ZipInput/res.zip b/tools/aapt2/integration-tests/CompileTest/ZipInput/res.zip Binary files differnew file mode 100644 index 000000000000..00e396d812c7 --- /dev/null +++ b/tools/aapt2/integration-tests/CompileTest/ZipInput/res.zip diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp index 1387d2218ed4..16a20f4cb09d 100644 --- a/tools/aapt2/io/FileSystem.cpp +++ b/tools/aapt2/io/FileSystem.cpp @@ -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(); } diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h index 6be8807735f1..fb6bf6eeabbc 100644 --- a/tools/aapt2/io/FileSystem.h +++ b/tools/aapt2/io/FileSystem.h @@ -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; diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp index 269b6c5a12e1..8e6d7137640a 100644 --- a/tools/aapt2/io/ZipArchive.cpp +++ b/tools/aapt2/io/ZipArchive.cpp @@ -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; } diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 5a8ff0926483..7cd023bca369 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -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); diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h index b26e4fa26de6..219e1a07af95 100644 --- a/tools/aapt2/util/Files.h +++ b/tools/aapt2/util/Files.h @@ -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); |