From 48a765945449c240b1aea0be40522a14b35a642e Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Thu, 8 Jun 2017 11:10:18 -0700 Subject: AAPT2: Mark version 2.17 and update readme Test: none Change-Id: I6429b1c3acbcbf2244a5a5c95bc48a5d66bdaebe --- tools/aapt2/Main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 1d2e3a4f2df0..5978cdd154b9 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -27,7 +27,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "16"; +static const char* sMinorVersion = "17"; int PrintVersion() { std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "." -- cgit v1.2.3-59-g8ed1b From f903d5f15ae03795a03c0e15935ffe1db8e870ae Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 10 Jul 2017 04:19:28 -0700 Subject: AAPT2: Bump version number to v2.18 Test: none Change-Id: I6e3b4c95a7d7ba416ba368e5b19782dbffa04fd8 --- tools/aapt2/Main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 5978cdd154b9..c5d38abcdf71 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -27,7 +27,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "17"; +static const char* sMinorVersion = "18"; int PrintVersion() { std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "." -- cgit v1.2.3-59-g8ed1b From 5b54ca2d72c410aa34363b0f3bb0fe1666954aea Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Wed, 2 Aug 2017 14:57:43 -0700 Subject: AAPT2: Fix windows unicode path issues Mingw64 was being difficult, so instead of defining a wmain entrypoint, the command line parameters are parsed manually using built-in Windows methods that support Unicode. The results are converted to UTF8 and handled just like the rest of the linux/mac version of the code. This also removes dependencies on std::istream in favour of a FileInputStream which calls the appropriate unicode version of open to read a file. No speed regressions found on Linux or MacOS. Bug: 62336414 Bug: 63830502 Bug: 65645766 Test: manual Change-Id: I597da51e33729ed1b98bf246e7e773337fd3fee8 Merged-In: I597da51e33729ed1b98bf246e7e773337fd3fee8 --- tools/aapt2/Android.bp | 3 + tools/aapt2/Main.cpp | 38 +++- tools/aapt2/ResourceParser_test.cpp | 20 +- tools/aapt2/cmd/Compile.cpp | 58 +++--- tools/aapt2/cmd/Link.cpp | 16 +- tools/aapt2/configuration/ConfigurationParser.cpp | 9 +- tools/aapt2/flatten/Archive.cpp | 16 +- tools/aapt2/io/FileInputStream.cpp | 102 ++++++++++ tools/aapt2/io/FileInputStream.h | 63 +++++++ tools/aapt2/io/FileInputStream_test.cpp | 87 +++++++++ tools/aapt2/io/StringInputStream.cpp | 50 +++++ tools/aapt2/io/StringInputStream.h | 56 ++++++ tools/aapt2/io/StringInputStream_test.cpp | 72 ++++++++ tools/aapt2/test/Builders.cpp | 216 ++++++++++++++++++++++ tools/aapt2/test/Builders.h | 201 ++++---------------- tools/aapt2/util/Files.cpp | 92 +++++---- tools/aapt2/util/Files.h | 79 +++----- tools/aapt2/xml/XmlDom.cpp | 62 ++++--- tools/aapt2/xml/XmlDom.h | 76 +++----- tools/aapt2/xml/XmlDom_test.cpp | 13 +- tools/aapt2/xml/XmlPullParser.cpp | 32 ++-- tools/aapt2/xml/XmlPullParser.h | 15 +- tools/aapt2/xml/XmlPullParser_test.cpp | 15 +- 23 files changed, 961 insertions(+), 430 deletions(-) create mode 100644 tools/aapt2/io/FileInputStream.cpp create mode 100644 tools/aapt2/io/FileInputStream.h create mode 100644 tools/aapt2/io/FileInputStream_test.cpp create mode 100644 tools/aapt2/io/StringInputStream.cpp create mode 100644 tools/aapt2/io/StringInputStream.h create mode 100644 tools/aapt2/io/StringInputStream_test.cpp create mode 100644 tools/aapt2/test/Builders.cpp (limited to 'tools/aapt2/Main.cpp') diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 14d05fdf6ee8..53794e641b0f 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -92,7 +92,9 @@ cc_library_host_static { "flatten/XmlFlattener.cpp", "io/BigBufferStreams.cpp", "io/File.cpp", + "io/FileInputStream.cpp", "io/FileSystem.cpp", + "io/StringInputStream.cpp", "io/Util.cpp", "io/ZipArchive.cpp", "link/AutoVersioner.cpp", @@ -164,6 +166,7 @@ cc_library_host_shared { cc_test_host { name: "aapt2_tests", srcs: [ + "test/Builders.cpp", "test/Common.cpp", "**/*_test.cpp", ], diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index c5d38abcdf71..89cce5ffca53 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -14,9 +14,17 @@ * limitations under the License. */ +#ifdef _WIN32 +// clang-format off +#include +#include +// clang-format on +#endif + #include #include +#include "android-base/utf8.h" #include "androidfw/StringPiece.h" #include "Diagnostics.h" @@ -43,7 +51,7 @@ extern int Optimize(const std::vector& args); } // namespace aapt -int main(int argc, char** argv) { +int MainImpl(int argc, char** argv) { if (argc >= 2) { argv += 1; argc -= 1; @@ -74,7 +82,31 @@ int main(int argc, char** argv) { std::cerr << "no command specified\n"; } - std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." - << std::endl; + std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|version] ..." << std::endl; return 1; } + +int main(int argc, char** argv) { +#ifdef _WIN32 + LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &argc); + CHECK(wide_argv != nullptr) << "invalid command line parameters passed to process"; + + std::vector utf8_args; + for (int i = 0; i < argc; i++) { + std::string utf8_arg; + if (!::android::base::WideToUTF8(wide_argv[i], &utf8_arg)) { + std::cerr << "error converting input arguments to UTF-8" << std::endl; + return 1; + } + utf8_args.push_back(std::move(utf8_arg)); + } + LocalFree(wide_argv); + + std::unique_ptr utf8_argv(new char*[utf8_args.size()]); + for (int i = 0; i < argc; i++) { + utf8_argv[i] = const_cast(utf8_args[i].c_str()); + } + argv = utf8_argv.get(); +#endif + return MainImpl(argc, argv); +} diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 1683c64a6a5c..971b45eff35f 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -22,9 +22,11 @@ #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" +#include "io/StringInputStream.h" #include "test/Test.h" #include "xml/XmlPullParser.h" +using ::aapt::io::StringInputStream; using ::aapt::test::StrValueEq; using ::aapt::test::ValueEq; using ::android::ResTable_map; @@ -43,11 +45,13 @@ constexpr const char* kXmlPreamble = " TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) { std::unique_ptr context = test::ContextBuilder().Build(); - std::stringstream input(kXmlPreamble); - input << R"()" << std::endl; ResourceTable table; ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {}); - xml::XmlPullParser xml_parser(input); + + std::string input = kXmlPreamble; + input += R"()"; + StringInputStream in(input); + xml::XmlPullParser xml_parser(&in); ASSERT_FALSE(parser.Parse(&xml_parser)); } @@ -62,12 +66,16 @@ class ResourceParserTest : public ::testing::Test { } ::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) { - std::stringstream input(kXmlPreamble); - input << "\n" << str << "\n" << std::endl; ResourceParserOptions parserOptions; ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config, parserOptions); - xml::XmlPullParser xmlParser(input); + + std::string input = kXmlPreamble; + input += "\n"; + input.append(str.data(), str.size()); + input += "\n"; + StringInputStream in(input); + xml::XmlPullParser xmlParser(&in); if (parser.Parse(&xmlParser)) { return ::testing::AssertionSuccess(); } diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index b64cd8c432d4..8536edb34b70 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -16,11 +16,11 @@ #include -#include #include #include "android-base/errors.h" #include "android-base/file.h" +#include "android-base/utf8.h" #include "androidfw/StringPiece.h" #include "google/protobuf/io/coded_stream.h" #include "google/protobuf/io/zero_copy_stream_impl_lite.h" @@ -38,6 +38,7 @@ #include "flatten/Archive.h" #include "flatten/XmlFlattener.h" #include "io/BigBufferOutputStream.h" +#include "io/FileInputStream.h" #include "io/Util.h" #include "proto/ProtoSerialize.h" #include "util/Files.h" @@ -46,8 +47,9 @@ #include "xml/XmlDom.h" #include "xml/XmlPullParser.h" -using android::StringPiece; -using google::protobuf::io::CopyingOutputStreamAdaptor; +using ::aapt::io::FileInputStream; +using ::android::StringPiece; +using ::google::protobuf::io::CopyingOutputStreamAdaptor; namespace aapt { @@ -57,19 +59,14 @@ struct ResourcePathData { std::string name; std::string extension; - // Original config str. We keep this because when we parse the config, we may - // add on - // version qualifiers. We want to preserve the original input so the output is - // easily + // Original config str. We keep this because when we parse the config, we may add on + // version qualifiers. We want to preserve the original input so the output is easily // computed before hand. std::string config_str; ConfigDescription config; }; -/** - * Resource file paths are expected to look like: - * [--/res/]type[-config]/name - */ +// Resource file paths are expected to look like: [--/res/]type[-config]/name static Maybe ExtractResourcePathData(const std::string& path, std::string* out_error) { std::vector parts = util::Split(path, file::sDirSep); @@ -137,9 +134,7 @@ static bool IsHidden(const StringPiece& filename) { return util::StartsWith(filename, "."); } -/** - * Walks the res directory structure, looking for resource files. - */ +// Walks the res directory structure, looking for resource files. static bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options, std::vector* out_path_data) { const std::string& root_dir = options.res_dir.value(); @@ -195,22 +190,20 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options, const std::string& output_path) { ResourceTable table; { - std::ifstream fin(path_data.source.path, std::ifstream::binary); - if (!fin) { + FileInputStream fin(path_data.source.path); + if (fin.HadError()) { context->GetDiagnostics()->Error(DiagMessage(path_data.source) - << "failed to open file: " - << android::base::SystemErrorCodeToString(errno)); + << "failed to open file: " << fin.GetError()); return false; } // Parse the values file from XML. - xml::XmlPullParser xml_parser(fin); + xml::XmlPullParser xml_parser(&fin); ResourceParserOptions parser_options; parser_options.error_on_positional_arguments = !options.legacy_mode; - // If the filename includes donottranslate, then the default translatable is - // false. + // If the filename includes donottranslate, then the default translatable is false. parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos; ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config, @@ -218,8 +211,6 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options, if (!res_parser.Parse(&xml_parser)) { return false; } - - fin.close(); } if (options.pseudolocalize) { @@ -239,8 +230,7 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options, // Assign an ID to any package that has resources. for (auto& pkg : table.packages) { if (!pkg->id) { - // If no package ID was set while parsing (public identifiers), auto - // assign an ID. + // If no package ID was set while parsing (public identifiers), auto assign an ID. pkg->id = context->GetPackageId(); } } @@ -367,7 +357,7 @@ static bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& outp return true; } -static bool IsValidFile(IAaptContext* context, const StringPiece& input_path) { +static bool IsValidFile(IAaptContext* context, const std::string& input_path) { const file::FileType file_type = file::GetFileType(input_path); if (file_type != file::FileType::kRegular && file_type != file::FileType::kSymlink) { if (file_type == file::FileType::kDirectory) { @@ -393,17 +383,14 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, std::unique_ptr xmlres; { - std::ifstream fin(path_data.source.path, std::ifstream::binary); - if (!fin) { + FileInputStream fin(path_data.source.path); + if (fin.HadError()) { context->GetDiagnostics()->Error(DiagMessage(path_data.source) - << "failed to open file: " - << android::base::SystemErrorCodeToString(errno)); + << "failed to open file: " << fin.GetError()); return false; } xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source); - - fin.close(); } if (!xmlres) { @@ -432,12 +419,9 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, return false; } - // Make sure CopyingOutputStreamAdaptor is deleted before we call - // writer->FinishEntry(). + // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry(). { - // Wrap our IArchiveWriter with an adaptor that implements the - // ZeroCopyOutputStream - // interface. + // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface. CopyingOutputStreamAdaptor copying_adaptor(writer); CompiledFileOutputStream output_stream(©ing_adaptor); diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 5ad0cdd995ed..4b59eb333ff6 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -40,6 +40,7 @@ #include "flatten/TableFlattener.h" #include "flatten/XmlFlattener.h" #include "io/BigBufferInputStream.h" +#include "io/FileInputStream.h" #include "io/FileSystem.h" #include "io/Util.h" #include "io/ZipArchive.h" @@ -61,8 +62,9 @@ #include "util/Files.h" #include "xml/XmlDom.h" -using android::StringPiece; -using android::base::StringPrintf; +using ::aapt::io::FileInputStream; +using ::android::StringPiece; +using ::android::base::StringPrintf; namespace aapt { @@ -284,13 +286,11 @@ static std::unique_ptr LoadTableFromPb(const Source& source, cons return table; } -/** - * Inflates an XML file from the source path. - */ +// Inflates an XML file from the source path. static std::unique_ptr LoadXml(const std::string& path, IDiagnostics* diag) { - std::ifstream fin(path, std::ifstream::binary); - if (!fin) { - diag->Error(DiagMessage(path) << strerror(errno)); + FileInputStream fin(path); + if (fin.HadError()) { + diag->Error(DiagMessage(path) << "failed to load XML file: " << fin.GetError()); return {}; } return xml::Inflate(&fin, diag, Source(path)); diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp index d051120b9445..567b0da85816 100644 --- a/tools/aapt2/configuration/ConfigurationParser.cpp +++ b/tools/aapt2/configuration/ConfigurationParser.cpp @@ -22,13 +22,14 @@ #include #include -#include -#include +#include "android-base/file.h" +#include "android-base/logging.h" #include "ConfigDescription.h" #include "Diagnostics.h" #include "io/File.h" #include "io/FileSystem.h" +#include "io/StringInputStream.h" #include "util/Maybe.h" #include "util/Util.h" #include "xml/XmlActionExecutor.h" @@ -49,6 +50,7 @@ using ::aapt::configuration::Group; using ::aapt::configuration::Locale; using ::aapt::io::IFile; using ::aapt::io::RegularFile; +using ::aapt::io::StringInputStream; using ::aapt::util::TrimWhitespace; using ::aapt::xml::Element; using ::aapt::xml::FindRootElement; @@ -182,8 +184,7 @@ ConfigurationParser::ConfigurationParser(std::string contents) } Maybe ConfigurationParser::Parse() { - std::istringstream in(contents_); - + StringInputStream in(contents_); auto doc = xml::Inflate(&in, diag_, Source("config.xml")); if (!doc) { return {}; diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp index 826f91b4a2fd..5f8bd063f9b0 100644 --- a/tools/aapt2/flatten/Archive.cpp +++ b/tools/aapt2/flatten/Archive.cpp @@ -23,12 +23,14 @@ #include "android-base/errors.h" #include "android-base/macros.h" +#include "android-base/utf8.h" #include "androidfw/StringPiece.h" #include "ziparchive/zip_writer.h" #include "util/Files.h" -using android::StringPiece; +using ::android::StringPiece; +using ::android::base::SystemErrorCodeToString; namespace aapt { @@ -58,11 +60,11 @@ class DirectoryWriter : public IArchiveWriter { std::string full_path = dir_; file::AppendPath(&full_path, path); - file::mkdirs(file::GetStem(full_path)); + file::mkdirs(file::GetStem(full_path).to_string()); - file_ = {fopen(full_path.data(), "wb"), fclose}; + file_ = {::android::base::utf8::fopen(full_path.c_str(), "wb"), fclose}; if (!file_) { - error_ = android::base::SystemErrorCodeToString(errno); + error_ = SystemErrorCodeToString(errno); return false; } return true; @@ -74,7 +76,7 @@ class DirectoryWriter : public IArchiveWriter { } if (fwrite(data, 1, len, file_.get()) != static_cast(len)) { - error_ = android::base::SystemErrorCodeToString(errno); + error_ = SystemErrorCodeToString(errno); file_.reset(nullptr); return false; } @@ -121,9 +123,9 @@ class ZipFileWriter : public IArchiveWriter { ZipFileWriter() = default; bool Open(const StringPiece& path) { - file_ = {fopen(path.data(), "w+b"), fclose}; + file_ = {::android::base::utf8::fopen(path.to_string().c_str(), "w+b"), fclose}; if (!file_) { - error_ = android::base::SystemErrorCodeToString(errno); + error_ = SystemErrorCodeToString(errno); return false; } writer_ = util::make_unique(file_.get()); diff --git a/tools/aapt2/io/FileInputStream.cpp b/tools/aapt2/io/FileInputStream.cpp new file mode 100644 index 000000000000..07dbb5a98add --- /dev/null +++ b/tools/aapt2/io/FileInputStream.cpp @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#include "io/FileInputStream.h" + +#include // for errno +#include // for O_RDONLY +#include // for read + +#include "android-base/errors.h" +#include "android-base/file.h" // for O_BINARY +#include "android-base/macros.h" +#include "android-base/utf8.h" + +using ::android::base::SystemErrorCodeToString; + +namespace aapt { +namespace io { + +FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity) + : FileInputStream(::android::base::utf8::open(path.c_str(), O_RDONLY | O_BINARY), + buffer_capacity) { +} + +FileInputStream::FileInputStream(int fd, size_t buffer_capacity) + : fd_(fd), + buffer_capacity_(buffer_capacity), + buffer_offset_(0u), + buffer_size_(0u), + total_byte_count_(0u) { + if (fd_ == -1) { + error_ = SystemErrorCodeToString(errno); + } else { + buffer_.reset(new uint8_t[buffer_capacity_]); + } +} + +bool FileInputStream::Next(const void** data, size_t* size) { + if (HadError()) { + return false; + } + + // Deal with any remaining bytes after BackUp was called. + if (buffer_offset_ != buffer_size_) { + *data = buffer_.get() + buffer_offset_; + *size = buffer_size_ - buffer_offset_; + total_byte_count_ += buffer_size_ - buffer_offset_; + buffer_offset_ = buffer_size_; + return true; + } + + ssize_t n = TEMP_FAILURE_RETRY(read(fd_, buffer_.get(), buffer_capacity_)); + if (n < 0) { + error_ = SystemErrorCodeToString(errno); + fd_.reset(); + return false; + } + + buffer_size_ = static_cast(n); + buffer_offset_ = buffer_size_; + total_byte_count_ += buffer_size_; + + *data = buffer_.get(); + *size = buffer_size_; + return buffer_size_ != 0u; +} + +void FileInputStream::BackUp(size_t count) { + if (count > buffer_offset_) { + count = buffer_offset_; + } + buffer_offset_ -= count; + total_byte_count_ -= count; +} + +size_t FileInputStream::ByteCount() const { + return total_byte_count_; +} + +bool FileInputStream::HadError() const { + return !error_.empty(); +} + +std::string FileInputStream::GetError() const { + return error_; +} + +} // namespace io +} // namespace aapt diff --git a/tools/aapt2/io/FileInputStream.h b/tools/aapt2/io/FileInputStream.h new file mode 100644 index 000000000000..6beb9a186ce5 --- /dev/null +++ b/tools/aapt2/io/FileInputStream.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef AAPT_IO_FILEINPUTSTREAM_H +#define AAPT_IO_FILEINPUTSTREAM_H + +#include "io/Io.h" + +#include +#include + +#include "android-base/macros.h" +#include "android-base/unique_fd.h" + +namespace aapt { +namespace io { + +class FileInputStream : public InputStream { + public: + explicit FileInputStream(const std::string& path, size_t buffer_capacity = 4096); + + // Takes ownership of `fd`. + explicit FileInputStream(int fd, size_t buffer_capacity = 4096); + + bool Next(const void** data, size_t* size) override; + + void BackUp(size_t count) override; + + size_t ByteCount() const override; + + bool HadError() const override; + + std::string GetError() const override; + + private: + DISALLOW_COPY_AND_ASSIGN(FileInputStream); + + android::base::unique_fd fd_; + std::string error_; + std::unique_ptr buffer_; + size_t buffer_capacity_; + size_t buffer_offset_; + size_t buffer_size_; + size_t total_byte_count_; +}; + +} // namespace io +} // namespace aapt + +#endif // AAPT_IO_FILEINPUTSTREAM_H diff --git a/tools/aapt2/io/FileInputStream_test.cpp b/tools/aapt2/io/FileInputStream_test.cpp new file mode 100644 index 000000000000..7314ab7beeba --- /dev/null +++ b/tools/aapt2/io/FileInputStream_test.cpp @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#include "io/FileInputStream.h" + +#include "android-base/macros.h" +#include "android-base/test_utils.h" + +#include "test/Test.h" + +using ::android::StringPiece; +using ::testing::Eq; +using ::testing::NotNull; +using ::testing::StrEq; + +namespace aapt { +namespace io { + +TEST(FileInputStreamTest, NextAndBackup) { + std::string input = "this is a cool string"; + TemporaryFile file; + ASSERT_THAT(TEMP_FAILURE_RETRY(write(file.fd, input.c_str(), input.size())), Eq(21)); + lseek64(file.fd, 0, SEEK_SET); + + // Use a small buffer size so that we can call Next() a few times. + FileInputStream in(file.fd, 10u); + ASSERT_FALSE(in.HadError()); + EXPECT_THAT(in.ByteCount(), Eq(0u)); + + const char* buffer; + size_t size; + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)) << in.GetError(); + ASSERT_THAT(size, Eq(10u)); + ASSERT_THAT(buffer, NotNull()); + EXPECT_THAT(in.ByteCount(), Eq(10u)); + EXPECT_THAT(StringPiece(buffer, size), Eq("this is a ")); + + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)); + ASSERT_THAT(size, Eq(10u)); + ASSERT_THAT(buffer, NotNull()); + EXPECT_THAT(in.ByteCount(), Eq(20u)); + EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin")); + + in.BackUp(5u); + EXPECT_THAT(in.ByteCount(), Eq(15u)); + + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)); + ASSERT_THAT(size, Eq(5u)); + ASSERT_THAT(buffer, NotNull()); + ASSERT_THAT(in.ByteCount(), Eq(20u)); + EXPECT_THAT(StringPiece(buffer, size), Eq("strin")); + + // Backup 1 more than possible. Should clamp. + in.BackUp(11u); + EXPECT_THAT(in.ByteCount(), Eq(10u)); + + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)); + ASSERT_THAT(size, Eq(10u)); + ASSERT_THAT(buffer, NotNull()); + ASSERT_THAT(in.ByteCount(), Eq(20u)); + EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin")); + + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)); + ASSERT_THAT(size, Eq(1u)); + ASSERT_THAT(buffer, NotNull()); + ASSERT_THAT(in.ByteCount(), Eq(21u)); + EXPECT_THAT(StringPiece(buffer, size), Eq("g")); + + EXPECT_FALSE(in.Next(reinterpret_cast(&buffer), &size)); + EXPECT_FALSE(in.HadError()); +} + +} // namespace io +} // namespace aapt diff --git a/tools/aapt2/io/StringInputStream.cpp b/tools/aapt2/io/StringInputStream.cpp new file mode 100644 index 000000000000..51a18a7d8a9f --- /dev/null +++ b/tools/aapt2/io/StringInputStream.cpp @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#include "io/StringInputStream.h" + +using ::android::StringPiece; + +namespace aapt { +namespace io { + +StringInputStream::StringInputStream(const StringPiece& str) : str_(str), offset_(0u) { +} + +bool StringInputStream::Next(const void** data, size_t* size) { + if (offset_ == str_.size()) { + return false; + } + + *data = str_.data() + offset_; + *size = str_.size() - offset_; + offset_ = str_.size(); + return true; +} + +void StringInputStream::BackUp(size_t count) { + if (count > offset_) { + count = offset_; + } + offset_ -= count; +} + +size_t StringInputStream::ByteCount() const { + return offset_; +} + +} // namespace io +} // namespace aapt diff --git a/tools/aapt2/io/StringInputStream.h b/tools/aapt2/io/StringInputStream.h new file mode 100644 index 000000000000..ff5b112ef274 --- /dev/null +++ b/tools/aapt2/io/StringInputStream.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef AAPT_IO_STRINGINPUTSTREAM_H +#define AAPT_IO_STRINGINPUTSTREAM_H + +#include "io/Io.h" + +#include "android-base/macros.h" +#include "androidfw/StringPiece.h" + +namespace aapt { +namespace io { + +class StringInputStream : public InputStream { + public: + explicit StringInputStream(const android::StringPiece& str); + + bool Next(const void** data, size_t* size) override; + + void BackUp(size_t count) override; + + size_t ByteCount() const override; + + inline bool HadError() const override { + return false; + } + + inline std::string GetError() const override { + return {}; + } + + private: + DISALLOW_COPY_AND_ASSIGN(StringInputStream); + + android::StringPiece str_; + size_t offset_; +}; + +} // namespace io +} // namespace aapt + +#endif // AAPT_IO_STRINGINPUTSTREAM_H diff --git a/tools/aapt2/io/StringInputStream_test.cpp b/tools/aapt2/io/StringInputStream_test.cpp new file mode 100644 index 000000000000..cc57bc498313 --- /dev/null +++ b/tools/aapt2/io/StringInputStream_test.cpp @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#include "io/StringInputStream.h" + +#include "test/Test.h" + +using ::android::StringPiece; +using ::testing::Eq; +using ::testing::NotNull; +using ::testing::StrEq; + +namespace aapt { +namespace io { + +TEST(StringInputStreamTest, OneCallToNextShouldReturnEntireBuffer) { + constexpr const size_t kCount = 1000; + std::string input; + input.resize(kCount, 0x7f); + input[0] = 0x00; + input[kCount - 1] = 0xff; + StringInputStream in(input); + + const char* buffer; + size_t size; + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)); + ASSERT_THAT(size, Eq(kCount)); + ASSERT_THAT(buffer, NotNull()); + + EXPECT_THAT(buffer[0], Eq(0x00)); + EXPECT_THAT(buffer[kCount - 1], Eq('\xff')); + + EXPECT_FALSE(in.Next(reinterpret_cast(&buffer), &size)); + EXPECT_FALSE(in.HadError()); +} + +TEST(StringInputStreamTest, BackUp) { + std::string input = "hello this is a string"; + StringInputStream in(input); + + const char* buffer; + size_t size; + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)); + ASSERT_THAT(size, Eq(input.size())); + ASSERT_THAT(buffer, NotNull()); + EXPECT_THAT(in.ByteCount(), Eq(input.size())); + + in.BackUp(6u); + EXPECT_THAT(in.ByteCount(), Eq(input.size() - 6u)); + + ASSERT_TRUE(in.Next(reinterpret_cast(&buffer), &size)); + ASSERT_THAT(size, Eq(6u)); + ASSERT_THAT(buffer, NotNull()); + ASSERT_THAT(buffer, StrEq("string")); + EXPECT_THAT(in.ByteCount(), Eq(input.size())); +} + +} // namespace io +} // namespace aapt diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp new file mode 100644 index 000000000000..b5795451e749 --- /dev/null +++ b/tools/aapt2/test/Builders.cpp @@ -0,0 +1,216 @@ +/* + * 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. + */ + +#include "test/Builders.h" + +#include "android-base/logging.h" +#include "androidfw/StringPiece.h" + +#include "io/StringInputStream.h" +#include "test/Common.h" +#include "util/Util.h" + +using ::aapt::io::StringInputStream; +using ::android::StringPiece; + +namespace aapt { +namespace test { + +ResourceTableBuilder& ResourceTableBuilder::SetPackageId(const StringPiece& package_name, + uint8_t id) { + ResourceTablePackage* package = table_->CreatePackage(package_name, id); + CHECK(package != nullptr); + return *this; +} + +ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name, + const ResourceId& id) { + return AddValue(name, id, util::make_unique()); +} + +ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name, + const ConfigDescription& config, + const ResourceId& id) { + return AddValue(name, config, id, util::make_unique()); +} + +ResourceTableBuilder& ResourceTableBuilder::AddReference(const StringPiece& name, + const StringPiece& ref) { + return AddReference(name, {}, ref); +} + +ResourceTableBuilder& ResourceTableBuilder::AddReference(const StringPiece& name, + const ResourceId& id, + const StringPiece& ref) { + return AddValue(name, id, util::make_unique(ParseNameOrDie(ref))); +} + +ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, + const StringPiece& str) { + return AddString(name, {}, str); +} + +ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, const ResourceId& id, + const StringPiece& str) { + return AddValue(name, id, util::make_unique(table_->string_pool.MakeRef(str))); +} + +ResourceTableBuilder& ResourceTableBuilder::AddString(const StringPiece& name, const ResourceId& id, + const ConfigDescription& config, + const StringPiece& str) { + return AddValue(name, config, id, util::make_unique(table_->string_pool.MakeRef(str))); +} + +ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name, + const StringPiece& path) { + return AddFileReference(name, {}, path); +} + +ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name, + const ResourceId& id, + const StringPiece& path) { + return AddValue(name, id, util::make_unique(table_->string_pool.MakeRef(path))); +} + +ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name, + const StringPiece& path, + const ConfigDescription& config) { + return AddValue(name, config, {}, + util::make_unique(table_->string_pool.MakeRef(path))); +} + +ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, + std::unique_ptr value) { + return AddValue(name, {}, std::move(value)); +} + +ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, const ResourceId& id, + std::unique_ptr value) { + return AddValue(name, {}, id, std::move(value)); +} + +ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, + const ConfigDescription& config, + const ResourceId& id, + std::unique_ptr value) { + ResourceName res_name = ParseNameOrDie(name); + CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value), + GetDiagnostics())); + return *this; +} + +ResourceTableBuilder& ResourceTableBuilder::SetSymbolState(const StringPiece& name, + const ResourceId& id, SymbolState state, + bool allow_new) { + ResourceName res_name = ParseNameOrDie(name); + Symbol symbol; + symbol.state = state; + symbol.allow_new = allow_new; + CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics())); + return *this; +} + +StringPool* ResourceTableBuilder::string_pool() { + return &table_->string_pool; +} + +std::unique_ptr ResourceTableBuilder::Build() { + return std::move(table_); +} + +std::unique_ptr BuildReference(const StringPiece& ref, const Maybe& id) { + std::unique_ptr reference = util::make_unique(ParseNameOrDie(ref)); + reference->id = id; + return reference; +} + +std::unique_ptr BuildPrimitive(uint8_t type, uint32_t data) { + android::Res_value value = {}; + value.size = sizeof(value); + value.dataType = type; + value.data = data; + return util::make_unique(value); +} + +AttributeBuilder::AttributeBuilder(bool weak) : attr_(util::make_unique(weak)) { + attr_->type_mask = android::ResTable_map::TYPE_ANY; +} + +AttributeBuilder& AttributeBuilder::SetTypeMask(uint32_t typeMask) { + attr_->type_mask = typeMask; + return *this; +} + +AttributeBuilder& AttributeBuilder::AddItem(const StringPiece& name, uint32_t value) { + attr_->symbols.push_back( + Attribute::Symbol{Reference(ResourceName({}, ResourceType::kId, name)), value}); + return *this; +} + +std::unique_ptr AttributeBuilder::Build() { + return std::move(attr_); +} + +StyleBuilder& StyleBuilder::SetParent(const StringPiece& str) { + style_->parent = Reference(ParseNameOrDie(str)); + return *this; +} + +StyleBuilder& StyleBuilder::AddItem(const StringPiece& str, std::unique_ptr value) { + style_->entries.push_back(Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)}); + return *this; +} + +StyleBuilder& StyleBuilder::AddItem(const StringPiece& str, const ResourceId& id, + std::unique_ptr value) { + AddItem(str, std::move(value)); + style_->entries.back().key.id = id; + return *this; +} + +std::unique_ptr