diff options
author | 2017-08-02 14:57:43 -0700 | |
---|---|---|
committer | 2017-08-03 17:33:52 -0700 | |
commit | efeb7af13be4446ce4a511a2ca707691e9a67c1e (patch) | |
tree | 36dc0651bcad4b81faf41fe730754d96d6eea465 | |
parent | f0f9fa330772f5f6a13673bd1204251c464ce67b (diff) |
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
Test: manual
Change-Id: I597da51e33729ed1b98bf246e7e773337fd3fee8
23 files changed, 961 insertions, 430 deletions
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 <windows.h> +#include <shellapi.h> +// clang-format on +#endif + #include <iostream> #include <vector> +#include "android-base/utf8.h" #include "androidfw/StringPiece.h" #include "Diagnostics.h" @@ -43,7 +51,7 @@ extern int Optimize(const std::vector<android::StringPiece>& 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<std::string> 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<char* []> utf8_argv(new char*[utf8_args.size()]); + for (int i = 0; i < argc; i++) { + utf8_argv[i] = const_cast<char*>(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 = "<?xml version=\"1.0\" encoding=\"utf-8\"?> TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); - std::stringstream input(kXmlPreamble); - input << R"(<attr name="foo"/>)" << std::endl; ResourceTable table; ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {}); - xml::XmlPullParser xml_parser(input); + + std::string input = kXmlPreamble; + input += R"(<attr name="foo"/>)"; + 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 << "<resources>\n" << str << "\n</resources>" << std::endl; ResourceParserOptions parserOptions; ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config, parserOptions); - xml::XmlPullParser xmlParser(input); + + std::string input = kXmlPreamble; + input += "<resources>\n"; + input.append(str.data(), str.size()); + input += "\n</resources>"; + 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 <dirent.h> -#include <fstream> #include <string> #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<ResourcePathData> ExtractResourcePathData(const std::string& path, std::string* out_error) { std::vector<std::string> 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<ResourcePathData>* 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<xml::XmlResource> 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 e6bf3a6f9f56..c9c836d99eff 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<ResourceTable> 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<xml::XmlResource> 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 c56492c8af9d..b0ed792215df 100644 --- a/tools/aapt2/configuration/ConfigurationParser.cpp +++ b/tools/aapt2/configuration/ConfigurationParser.cpp @@ -22,13 +22,14 @@ #include <memory> #include <utility> -#include <android-base/file.h> -#include <android-base/logging.h> +#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; @@ -194,8 +196,7 @@ ConfigurationParser::ConfigurationParser(std::string contents) } Maybe<PostProcessingConfiguration> 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<size_t>(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<ZipWriter>(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 <errno.h> // for errno +#include <fcntl.h> // for O_RDONLY +#include <unistd.h> // 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<size_t>(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 <memory> +#include <string> + +#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<uint8_t[]> 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<const void**>(&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<const void**>(&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<const void**>(&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<const void**>(&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<const void**>(&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<const void**>(&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<const void**>(&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<const void**>(&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<const void**>(&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<const void**>(&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<Id>()); +} + +ResourceTableBuilder& ResourceTableBuilder::AddSimple(const StringPiece& name, + const ConfigDescription& config, + const ResourceId& id) { + return AddValue(name, config, id, util::make_unique<Id>()); +} + +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<Reference>(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<String>(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<String>(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<FileReference>(table_->string_pool.MakeRef(path))); +} + +ResourceTableBuilder& ResourceTableBuilder::AddFileReference(const StringPiece& name, + const StringPiece& path, + const ConfigDescription& config) { + return AddValue(name, config, {}, + util::make_unique<FileReference>(table_->string_pool.MakeRef(path))); +} + +ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, + std::unique_ptr<Value> value) { + return AddValue(name, {}, std::move(value)); +} + +ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, const ResourceId& id, + std::unique_ptr<Value> value) { + return AddValue(name, {}, id, std::move(value)); +} + +ResourceTableBuilder& ResourceTableBuilder::AddValue(const StringPiece& name, + const ConfigDescription& config, + const ResourceId& id, + std::unique_ptr<Value> 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<ResourceTable> ResourceTableBuilder::Build() { + return std::move(table_); +} + +std::unique_ptr<Reference> BuildReference(const StringPiece& ref, const Maybe<ResourceId>& id) { + std::unique_ptr<Reference> reference = util::make_unique<Reference>(ParseNameOrDie(ref)); + reference->id = id; + return reference; +} + +std::unique_ptr<BinaryPrimitive> 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<BinaryPrimitive>(value); +} + +AttributeBuilder::AttributeBuilder(bool weak) : attr_(util::make_unique<Attribute>(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<Attribute> 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<Item> 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<Item> value) { + AddItem(str, std::move(value)); + style_->entries.back().key.id = id; + return *this; +} + +std::unique_ptr<Style> StyleBuilder::Build() { + return std::move(style_); +} + +StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str, const Maybe<ResourceId>& id) { + styleable_->entries.push_back(Reference(ParseNameOrDie(str))); + styleable_->entries.back().id = id; + return *this; +} + +std::unique_ptr<Styleable> StyleableBuilder::Build() { + return std::move(styleable_); +} + +std::unique_ptr<xml::XmlResource> BuildXmlDom(const StringPiece& str) { + std::string input = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; + input.append(str.data(), str.size()); + StringInputStream in(input); + StdErrDiagnostics diag; + std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, Source("test.xml")); + CHECK(doc != nullptr) << "failed to parse inline XML string"; + return doc; +} + +std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context, + const StringPiece& str) { + std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str); + doc->file.name.package = context->GetCompilationPackage(); + return doc; +} + +} // namespace test +} // namespace aapt diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index 6b8207647471..d9f3912fb4c6 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -19,13 +19,13 @@ #include <memory> -#include "android-base/logging.h" #include "android-base/macros.h" +#include "Resource.h" #include "ResourceTable.h" #include "ResourceValues.h" -#include "test/Common.h" -#include "util/Util.h" +#include "process/IResourceTableConsumer.h" +#include "util/Maybe.h" #include "xml/XmlDom.h" namespace aapt { @@ -35,97 +35,37 @@ class ResourceTableBuilder { public: ResourceTableBuilder() = default; - StringPool* string_pool() { return &table_->string_pool; } - - ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id) { - ResourceTablePackage* package = table_->CreatePackage(package_name, id); - CHECK(package != nullptr); - return *this; - } - - ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {}) { - return AddValue(name, id, util::make_unique<Id>()); - } - + ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id); + ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {}); ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ConfigDescription& config, - const ResourceId& id = {}) { - return AddValue(name, config, id, util::make_unique<Id>()); - } - + const ResourceId& id = {}); ResourceTableBuilder& AddReference(const android::StringPiece& name, - const android::StringPiece& ref) { - return AddReference(name, {}, ref); - } - + const android::StringPiece& ref); ResourceTableBuilder& AddReference(const android::StringPiece& name, const ResourceId& id, - const android::StringPiece& ref) { - return AddValue(name, id, util::make_unique<Reference>(ParseNameOrDie(ref))); - } - + const android::StringPiece& ref); ResourceTableBuilder& AddString(const android::StringPiece& name, - const android::StringPiece& str) { - return AddString(name, {}, str); - } - + const android::StringPiece& str); ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id, - const android::StringPiece& str) { - return AddValue( - name, id, util::make_unique<String>(table_->string_pool.MakeRef(str))); - } - + const android::StringPiece& str); ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id, - const ConfigDescription& config, - const android::StringPiece& str) { - return AddValue(name, config, id, util::make_unique<String>( - table_->string_pool.MakeRef(str))); - } - + const ConfigDescription& config, const android::StringPiece& str); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, - const android::StringPiece& path) { - return AddFileReference(name, {}, path); - } - + const android::StringPiece& path); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const ResourceId& id, - const android::StringPiece& path) { - return AddValue(name, id, util::make_unique<FileReference>( - table_->string_pool.MakeRef(path))); - } - + const android::StringPiece& path); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const android::StringPiece& path, - const ConfigDescription& config) { - return AddValue(name, config, {}, util::make_unique<FileReference>( - table_->string_pool.MakeRef(path))); - } - - ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value) { - return AddValue(name, {}, std::move(value)); - } - + const ConfigDescription& config); + ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value); ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id, - std::unique_ptr<Value> value) { - return AddValue(name, {}, id, std::move(value)); - } - + std::unique_ptr<Value> value); ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config, - const ResourceId& id, std::unique_ptr<Value> value) { - ResourceName res_name = ParseNameOrDie(name); - CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value), - GetDiagnostics())); - return *this; - } - + const ResourceId& id, std::unique_ptr<Value> value); ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id, - SymbolState state, bool allow_new = false) { - 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; - } + SymbolState state, bool allow_new = false); - std::unique_ptr<ResourceTable> Build() { return std::move(table_); } + StringPool* string_pool(); + std::unique_ptr<ResourceTable> Build(); private: DISALLOW_COPY_AND_ASSIGN(ResourceTableBuilder); @@ -133,29 +73,16 @@ class ResourceTableBuilder { std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>(); }; -inline std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref, - const Maybe<ResourceId>& id = {}) { - std::unique_ptr<Reference> reference = - util::make_unique<Reference>(ParseNameOrDie(ref)); - reference->id = id; - return reference; -} - -inline std::unique_ptr<BinaryPrimitive> 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<BinaryPrimitive>(value); -} +std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref, + const Maybe<ResourceId>& id = {}); +std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data); template <typename T> class ValueBuilder { public: template <typename... Args> - explicit ValueBuilder(Args&&... args) - : value_(new T{std::forward<Args>(args)...}) {} + explicit ValueBuilder(Args&&... args) : value_(new T{std::forward<Args>(args)...}) { + } template <typename... Args> ValueBuilder& SetSource(Args&&... args) { @@ -168,7 +95,9 @@ class ValueBuilder { return *this; } - std::unique_ptr<Value> Build() { return std::move(value_); } + std::unique_ptr<Value> Build() { + return std::move(value_); + } private: DISALLOW_COPY_AND_ASSIGN(ValueBuilder); @@ -178,23 +107,10 @@ class ValueBuilder { class AttributeBuilder { public: - explicit AttributeBuilder(bool weak = false) - : attr_(util::make_unique<Attribute>(weak)) { - attr_->type_mask = android::ResTable_map::TYPE_ANY; - } - - AttributeBuilder& SetTypeMask(uint32_t typeMask) { - attr_->type_mask = typeMask; - return *this; - } - - AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value) { - attr_->symbols.push_back(Attribute::Symbol{ - Reference(ResourceName({}, ResourceType::kId, name)), value}); - return *this; - } - - std::unique_ptr<Attribute> Build() { return std::move(attr_); } + explicit AttributeBuilder(bool weak = false); + AttributeBuilder& SetTypeMask(uint32_t typeMask); + AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value); + std::unique_ptr<Attribute> Build(); private: DISALLOW_COPY_AND_ASSIGN(AttributeBuilder); @@ -205,27 +121,11 @@ class AttributeBuilder { class StyleBuilder { public: StyleBuilder() = default; - - StyleBuilder& SetParent(const android::StringPiece& str) { - style_->parent = Reference(ParseNameOrDie(str)); - return *this; - } - - StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value) { - style_->entries.push_back(Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)}); - return *this; - } - + StyleBuilder& SetParent(const android::StringPiece& str); + StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value); StyleBuilder& AddItem(const android::StringPiece& str, const ResourceId& id, - std::unique_ptr<Item> value) { - AddItem(str, std::move(value)); - style_->entries.back().key.id = id; - return *this; - } - - std::unique_ptr<Style> Build() { - return std::move(style_); - } + std::unique_ptr<Item> value); + std::unique_ptr<Style> Build(); private: DISALLOW_COPY_AND_ASSIGN(StyleBuilder); @@ -236,14 +136,8 @@ class StyleBuilder { class StyleableBuilder { public: StyleableBuilder() = default; - - StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {}) { - styleable_->entries.push_back(Reference(ParseNameOrDie(str))); - styleable_->entries.back().id = id; - return *this; - } - - std::unique_ptr<Styleable> Build() { return std::move(styleable_); } + StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {}); + std::unique_ptr<Styleable> Build(); private: DISALLOW_COPY_AND_ASSIGN(StyleableBuilder); @@ -251,22 +145,9 @@ class StyleableBuilder { std::unique_ptr<Styleable> styleable_ = util::make_unique<Styleable>(); }; -inline std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str) { - std::stringstream in; - in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str; - StdErrDiagnostics diag; - std::unique_ptr<xml::XmlResource> doc = - xml::Inflate(&in, &diag, Source("test.xml")); - CHECK(doc != nullptr) << "failed to parse inline XML string"; - return doc; -} - -inline std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName( - IAaptContext* context, const android::StringPiece& str) { - std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str); - doc->file.name.package = context->GetCompilationPackage(); - return doc; -} +std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str); +std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context, + const android::StringPiece& str); } // namespace test } // namespace aapt diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 1bf25947ea93..6f97efe37921 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -27,6 +27,8 @@ #include "android-base/errors.h" #include "android-base/file.h" #include "android-base/logging.h" +#include "android-base/unique_fd.h" +#include "android-base/utf8.h" #include "util/Util.h" @@ -35,14 +37,32 @@ #include <direct.h> #endif -using android::StringPiece; +using ::android::FileMap; +using ::android::StringPiece; +using ::android::base::ReadFileToString; +using ::android::base::SystemErrorCodeToString; +using ::android::base::unique_fd; namespace aapt { namespace file { -FileType GetFileType(const StringPiece& path) { +FileType GetFileType(const std::string& path) { +// TODO(adamlesinski): I'd like to move this to ::android::base::utf8 but Windows does some macro +// trickery with 'stat' and things don't override very well. +#ifdef _WIN32 + std::wstring path_utf16; + if (!::android::base::UTF8PathToWindowsLongPath(path.c_str(), &path_utf16)) { + return FileType::kNonexistant; + } + + struct _stat64 sb; + int result = _wstat64(path_utf16.c_str(), &sb); +#else struct stat sb; - if (stat(path.data(), &sb) < 0) { + int result = stat(path.c_str(), &sb); +#endif + + if (result == -1) { if (errno == ENOENT || errno == ENOTDIR) { return FileType::kNonexistant; } @@ -72,27 +92,18 @@ FileType GetFileType(const StringPiece& path) { } } -inline static int MkdirImpl(const StringPiece& path) { -#ifdef _WIN32 - return _mkdir(path.to_string().c_str()); -#else - return mkdir(path.to_string().c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP); -#endif -} - -bool mkdirs(const StringPiece& path) { - const char* start = path.begin(); - const char* end = path.end(); - for (const char* current = start; current != end; ++current) { - if (*current == sDirSep && current != start) { - StringPiece parent_path(start, current - start); - int result = MkdirImpl(parent_path); - if (result < 0 && errno != EEXIST) { - return false; - } +bool mkdirs(const std::string& path) { + constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP; + size_t current_pos = 0u; + while ((current_pos = path.find(sDirSep, current_pos)) != std::string::npos) { + std::string parent_path = path.substr(0, current_pos); + int result = ::android::base::utf8::mkdir(parent_path.c_str(), mode); + if (result < 0 && errno != EEXIST) { + return false; } + current_pos += 1; } - return MkdirImpl(path) == 0 || errno == EEXIST; + return ::android::base::utf8::mkdir(path.c_str(), mode) == 0 || errno == EEXIST; } StringPiece GetStem(const StringPiece& path) { @@ -129,10 +140,8 @@ StringPiece GetExtension(const StringPiece& path) { void AppendPath(std::string* base, StringPiece part) { CHECK(base != nullptr); - const bool base_has_trailing_sep = - (!base->empty() && *(base->end() - 1) == sDirSep); - const bool part_has_leading_sep = - (!part.empty() && *(part.begin()) == sDirSep); + const bool base_has_trailing_sep = (!base->empty() && *(base->end() - 1) == sDirSep); + const bool part_has_leading_sep = (!part.empty() && *(part.begin()) == sDirSep); if (base_has_trailing_sep && part_has_leading_sep) { // Remove the part's leading sep part = part.substr(1, part.size() - 1); @@ -151,31 +160,34 @@ std::string PackageToPath(const StringPiece& package) { return out_path; } -Maybe<android::FileMap> MmapPath(const StringPiece& path, - std::string* out_error) { - std::unique_ptr<FILE, decltype(fclose)*> f = {fopen(path.data(), "rb"), - fclose}; - if (!f) { - if (out_error) *out_error = android::base::SystemErrorCodeToString(errno); +Maybe<FileMap> MmapPath(const std::string& path, std::string* out_error) { + int flags = O_RDONLY | O_CLOEXEC | O_BINARY; + unique_fd fd(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), flags))); + if (fd == -1) { + if (out_error) { + *out_error = SystemErrorCodeToString(errno); + } return {}; } - int fd = fileno(f.get()); - struct stat filestats = {}; if (fstat(fd, &filestats) != 0) { - if (out_error) *out_error = android::base::SystemErrorCodeToString(errno); + if (out_error) { + *out_error = SystemErrorCodeToString(errno); + } return {}; } - android::FileMap filemap; + FileMap filemap; if (filestats.st_size == 0) { // mmap doesn't like a length of 0. Instead we return an empty FileMap. return std::move(filemap); } - if (!filemap.create(path.data(), fd, 0, filestats.st_size, true)) { - if (out_error) *out_error = android::base::SystemErrorCodeToString(errno); + if (!filemap.create(path.c_str(), fd, 0, filestats.st_size, true)) { + if (out_error) { + *out_error = SystemErrorCodeToString(errno); + } return {}; } return std::move(filemap); @@ -184,7 +196,7 @@ Maybe<android::FileMap> MmapPath(const StringPiece& path, bool AppendArgsFromFile(const StringPiece& path, std::vector<std::string>* out_arglist, std::string* out_error) { std::string contents; - if (!android::base::ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) { + if (!ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) { if (out_error) { *out_error = "failed to read argument-list file"; } @@ -270,7 +282,7 @@ Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDia const std::string root_dir = path.to_string(); std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir); if (!d) { - diag->Error(DiagMessage() << android::base::SystemErrorCodeToString(errno)); + diag->Error(DiagMessage() << SystemErrorCodeToString(errno)); return {}; } diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h index b3b1e484d27b..b6aa410f9421 100644 --- a/tools/aapt2/util/Files.h +++ b/tools/aapt2/util/Files.h @@ -50,79 +50,56 @@ enum class FileType { kSocket, }; -FileType GetFileType(const android::StringPiece& path); +FileType GetFileType(const std::string& path); -/* - * Appends a path to `base`, separated by the directory separator. - */ +// Appends a path to `base`, separated by the directory separator. void AppendPath(std::string* base, android::StringPiece part); -/* - * Makes all the directories in `path`. The last element in the path - * is interpreted as a directory. - */ -bool mkdirs(const android::StringPiece& path); +// Makes all the directories in `path`. The last element in the path is interpreted as a directory. +bool mkdirs(const std::string& path); -/** - * Returns all but the last part of the path. - */ +// Returns all but the last part of the path. android::StringPiece GetStem(const android::StringPiece& path); -/** - * Returns the last part of the path with extension. - */ +// Returns the last part of the path with extension. android::StringPiece GetFilename(const android::StringPiece& path); -/** - * Returns the extension of the path. This is the entire string after - * the first '.' of the last part of the path. - */ +// Returns the extension of the path. This is the entire string after the first '.' of the last part +// of the path. android::StringPiece GetExtension(const android::StringPiece& path); -/** - * Converts a package name (com.android.app) to a path: com/android/app - */ +// Converts a package name (com.android.app) to a path: com/android/app std::string PackageToPath(const android::StringPiece& package); -/** - * Creates a FileMap for the file at path. - */ -Maybe<android::FileMap> MmapPath(const android::StringPiece& path, std::string* out_error); +// Creates a FileMap for the file at path. +Maybe<android::FileMap> MmapPath(const std::string& path, std::string* out_error); -/** - * Reads the file at path and appends each line to the outArgList vector. - */ +// Reads the file at path and appends each line to the outArgList vector. bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist, std::string* out_error); -/* - * Filter that determines which resource files/directories are - * processed by AAPT. Takes a pattern string supplied by the user. - * Pattern format is specified in the FileFilter::SetPattern() method. - */ +// Filter that determines which resource files/directories are +// processed by AAPT. Takes a pattern string supplied by the user. +// Pattern format is specified in the FileFilter::SetPattern() method. class FileFilter { public: explicit FileFilter(IDiagnostics* diag) : diag_(diag) {} - /* - * Patterns syntax: - * - Delimiter is : - * - Entry can start with the flag ! to avoid printing a warning - * about the file being ignored. - * - Entry can have the flag "<dir>" to match only directories - * or <file> to match only files. Default is to match both. - * - Entry can be a simplified glob "<prefix>*" or "*<suffix>" - * where prefix/suffix must have at least 1 character (so that - * we don't match a '*' catch-all pattern.) - * - The special filenames "." and ".." are always ignored. - * - Otherwise the full string is matched. - * - match is not case-sensitive. - */ + // Patterns syntax: + // - Delimiter is : + // - Entry can start with the flag ! to avoid printing a warning + // about the file being ignored. + // - Entry can have the flag "<dir>" to match only directories + // or <file> to match only files. Default is to match both. + // - Entry can be a simplified glob "<prefix>*" or "*<suffix>" + // where prefix/suffix must have at least 1 character (so that + // we don't match a '*' catch-all pattern.) + // - The special filenames "." and ".." are always ignored. + // - Otherwise the full string is matched. + // - match is not case-sensitive. bool SetPattern(const android::StringPiece& pattern); - /** - * Applies the filter, returning true for pass, false for fail. - */ + // Applies the filter, returning true for pass, false for fail. bool operator()(const std::string& filename, FileType type) const; private: diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp index 885ab3e33fed..d6df7150214b 100644 --- a/tools/aapt2/xml/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -29,8 +29,9 @@ #include "XmlPullParser.h" #include "util/Util.h" -using android::StringPiece; -using android::StringPiece16; +using ::aapt::io::InputStream; +using ::android::StringPiece; +using ::android::StringPiece16; namespace aapt { namespace xml { @@ -189,40 +190,41 @@ static void XMLCALL CommentDataHandler(void* user_data, const char* comment) { stack->pending_comment += comment; } -std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source) { +std::unique_ptr<XmlResource> Inflate(InputStream* in, IDiagnostics* diag, const Source& source) { Stack stack; - XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep); - XML_SetUserData(parser, &stack); - XML_UseParserAsHandlerArg(parser); - XML_SetElementHandler(parser, StartElementHandler, EndElementHandler); - XML_SetNamespaceDeclHandler(parser, StartNamespaceHandler, EndNamespaceHandler); - XML_SetCharacterDataHandler(parser, CharacterDataHandler); - XML_SetCommentHandler(parser, CommentDataHandler); - - char buffer[1024]; - while (!in->eof()) { - in->read(buffer, sizeof(buffer) / sizeof(buffer[0])); - if (in->bad() && !in->eof()) { - stack.root = {}; - diag->Error(DiagMessage(source) << strerror(errno)); - break; - } - - if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) { - stack.root = {}; - diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser))) - << XML_ErrorString(XML_GetErrorCode(parser))); - break; + std::unique_ptr<std::remove_pointer<XML_Parser>::type, decltype(XML_ParserFree)*> parser = { + XML_ParserCreateNS(nullptr, kXmlNamespaceSep), XML_ParserFree}; + XML_SetUserData(parser.get(), &stack); + XML_UseParserAsHandlerArg(parser.get()); + XML_SetElementHandler(parser.get(), StartElementHandler, EndElementHandler); + XML_SetNamespaceDeclHandler(parser.get(), StartNamespaceHandler, EndNamespaceHandler); + XML_SetCharacterDataHandler(parser.get(), CharacterDataHandler); + XML_SetCommentHandler(parser.get(), CommentDataHandler); + + const char* buffer = nullptr; + size_t buffer_size = 0; + while (in->Next(reinterpret_cast<const void**>(&buffer), &buffer_size)) { + if (XML_Parse(parser.get(), buffer, buffer_size, false) == XML_STATUS_ERROR) { + diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser.get()))) + << XML_ErrorString(XML_GetErrorCode(parser.get()))); + return {}; } } - XML_ParserFree(parser); - if (stack.root) { - return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{}, - std::move(stack.root)); + if (in->HadError()) { + diag->Error(DiagMessage(source) << in->GetError()); + return {}; + } else { + // Finish off the parsing. + if (XML_Parse(parser.get(), nullptr, 0u, true) == XML_STATUS_ERROR) { + diag->Error(DiagMessage(source.WithLine(XML_GetCurrentLineNumber(parser.get()))) + << XML_ErrorString(XML_GetErrorCode(parser.get()))); + return {}; + } } - return {}; + return util::make_unique<XmlResource>(ResourceFile{{}, {}, source}, StringPool{}, + std::move(stack.root)); } static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPool* out_pool) { diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h index 2dc99d693148..54a70333fad2 100644 --- a/tools/aapt2/xml/XmlDom.h +++ b/tools/aapt2/xml/XmlDom.h @@ -17,7 +17,6 @@ #ifndef AAPT_XML_DOM_H #define AAPT_XML_DOM_H -#include <istream> #include <memory> #include <string> #include <vector> @@ -27,6 +26,7 @@ #include "Diagnostics.h" #include "Resource.h" #include "ResourceValues.h" +#include "io/Io.h" #include "util/Util.h" #include "xml/XmlUtil.h" @@ -37,9 +37,7 @@ class RawVisitor; class Element; -/** - * Base class for all XML nodes. - */ +// Base class for all XML nodes. class Node { public: Node* parent = nullptr; @@ -60,19 +58,14 @@ class Node { virtual std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) = 0; }; -/** - * Base class that implements the visitor methods for a - * subclass of Node. - */ +// Base class that implements the visitor methods for a subclass of Node. template <typename Derived> class BaseNode : public Node { public: virtual void Accept(RawVisitor* visitor) override; }; -/** - * A Namespace XML node. Can only have one child. - */ +// A Namespace XML node. Can only have one child. class Namespace : public BaseNode<Namespace> { public: std::string namespace_prefix; @@ -90,9 +83,7 @@ struct AaptAttribute { Maybe<ResourceId> id; }; -/** - * An XML attribute. - */ +// An XML attribute. struct Attribute { std::string namespace_uri; std::string name; @@ -102,9 +93,7 @@ struct Attribute { std::unique_ptr<Item> compiled_value; }; -/** - * An Element XML node. - */ +// An Element XML node. class Element : public BaseNode<Element> { public: std::string namespace_uri; @@ -124,9 +113,7 @@ class Element : public BaseNode<Element> { std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override; }; -/** - * A Text (CDATA) XML node. Can not have any children. - */ +// A Text (CDATA) XML node. Can not have any children. class Text : public BaseNode<Text> { public: std::string text; @@ -134,9 +121,7 @@ class Text : public BaseNode<Text> { std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override; }; -/** - * An XML resource with a source, name, and XML tree. - */ +// An XML resource with a source, name, and XML tree. class XmlResource { public: ResourceFile file; @@ -149,27 +134,20 @@ class XmlResource { std::unique_ptr<xml::Node> root; }; -/** - * Inflates an XML DOM from a text stream, logging errors to the logger. - * Returns the root node on success, or nullptr on failure. - */ -std::unique_ptr<XmlResource> Inflate(std::istream* in, IDiagnostics* diag, const Source& source); +// Inflates an XML DOM from an InputStream, logging errors to the logger. +// Returns the root node on success, or nullptr on failure. +std::unique_ptr<XmlResource> Inflate(io::InputStream* in, IDiagnostics* diag, const Source& source); -/** - * Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger. - * Returns the root node on success, or nullptr on failure. - */ +// Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger. +// Returns the root node on success, or nullptr on failure. std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnostics* diag, const Source& source); Element* FindRootElement(XmlResource* doc); Element* FindRootElement(Node* node); -/** - * A visitor interface for the different XML Node subtypes. This will not - * traverse into - * children. Use Visitor for that. - */ +// A visitor interface for the different XML Node subtypes. This will not traverse into children. +// Use Visitor for that. class RawVisitor { public: virtual ~RawVisitor() = default; @@ -179,18 +157,22 @@ class RawVisitor { virtual void Visit(Text* text) {} }; -/** - * Visitor whose default implementation visits the children nodes of any node. - */ +// Visitor whose default implementation visits the children nodes of any node. class Visitor : public RawVisitor { public: using RawVisitor::Visit; - void Visit(Namespace* node) override { VisitChildren(node); } + void Visit(Namespace* node) override { + VisitChildren(node); + } - void Visit(Element* node) override { VisitChildren(node); } + void Visit(Element* node) override { + VisitChildren(node); + } - void Visit(Text* text) override { VisitChildren(text); } + void Visit(Text* text) override { + VisitChildren(text); + } void VisitChildren(Node* node) { for (auto& child : node->children) { @@ -199,9 +181,7 @@ class Visitor : public RawVisitor { } }; -/** - * An XML DOM visitor that will record the package name for a namespace prefix. - */ +// An XML DOM visitor that will record the package name for a namespace prefix. class PackageAwareVisitor : public Visitor, public IPackageDeclStack { public: using Visitor::Visit; @@ -233,7 +213,9 @@ class NodeCastImpl : public RawVisitor { T* value = nullptr; - void Visit(T* v) override { value = v; } + void Visit(T* v) override { + value = v; + } }; template <typename T> diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index f0122e8c617a..1340ada6d953 100644 --- a/tools/aapt2/xml/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -16,23 +16,20 @@ #include "xml/XmlDom.h" -#include <sstream> #include <string> +#include "io/StringInputStream.h" #include "test/Test.h" +using ::aapt::io::StringInputStream; using ::testing::Eq; using ::testing::NotNull; using ::testing::SizeIs; namespace aapt { -constexpr const char* kXmlPreamble = - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; - TEST(XmlDomTest, Inflate) { - std::stringstream in(kXmlPreamble); - in << R"( + std::string input = R"(<?xml version="1.0" encoding="utf-8"?> <Layout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -41,9 +38,9 @@ TEST(XmlDomTest, Inflate) { android:layout_height="wrap_content" /> </Layout>)"; - const Source source("test.xml"); StdErrDiagnostics diag; - std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, source); + StringInputStream in(input); + std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, Source("test.xml")); ASSERT_THAT(doc, NotNull()); xml::Namespace* ns = xml::NodeCast<xml::Namespace>(doc->root.get()); diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp index c2a9c8283a6d..30bdc507303b 100644 --- a/tools/aapt2/xml/XmlPullParser.cpp +++ b/tools/aapt2/xml/XmlPullParser.cpp @@ -22,14 +22,15 @@ #include "xml/XmlPullParser.h" #include "xml/XmlUtil.h" -using android::StringPiece; +using ::aapt::io::InputStream; +using ::android::StringPiece; namespace aapt { namespace xml { constexpr char kXmlNamespaceSep = 1; -XmlPullParser::XmlPullParser(std::istream& in) : in_(in), empty_(), depth_(0) { +XmlPullParser::XmlPullParser(InputStream* in) : in_(in), empty_(), depth_(0) { parser_ = XML_ParserCreateNS(nullptr, kXmlNamespaceSep); XML_SetUserData(parser_, this); XML_SetElementHandler(parser_, StartElementHandler, EndElementHandler); @@ -40,30 +41,35 @@ XmlPullParser::XmlPullParser(std::istream& in) : in_(in), empty_(), depth_(0) { event_queue_.push(EventData{Event::kStartDocument, 0, depth_++}); } -XmlPullParser::~XmlPullParser() { XML_ParserFree(parser_); } +XmlPullParser::~XmlPullParser() { + XML_ParserFree(parser_); +} XmlPullParser::Event XmlPullParser::Next() { const Event currentEvent = event(); - if (currentEvent == Event::kBadDocument || - currentEvent == Event::kEndDocument) { + if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) { return currentEvent; } event_queue_.pop(); while (event_queue_.empty()) { - in_.read(buffer_, sizeof(buffer_) / sizeof(*buffer_)); + const char* buffer = nullptr; + size_t buffer_size = 0; + bool done = false; + if (!in_->Next(reinterpret_cast<const void**>(&buffer), &buffer_size)) { + if (in_->HadError()) { + error_ = in_->GetError(); + event_queue_.push(EventData{Event::kBadDocument}); + break; + } - const bool done = in_.eof(); - if (in_.bad() && !done) { - error_ = strerror(errno); - event_queue_.push(EventData{Event::kBadDocument}); - continue; + done = true; } - if (XML_Parse(parser_, buffer_, in_.gcount(), done) == XML_STATUS_ERROR) { + if (XML_Parse(parser_, buffer, buffer_size, done) == XML_STATUS_ERROR) { error_ = XML_ErrorString(XML_GetErrorCode(parser_)); event_queue_.push(EventData{Event::kBadDocument}); - continue; + break; } if (done) { diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h index cdeeefd13976..a00caa139061 100644 --- a/tools/aapt2/xml/XmlPullParser.h +++ b/tools/aapt2/xml/XmlPullParser.h @@ -31,6 +31,7 @@ #include "androidfw/StringPiece.h" #include "Resource.h" +#include "io/Io.h" #include "process/IResourceTableConsumer.h" #include "util/Maybe.h" #include "xml/XmlUtil.h" @@ -64,7 +65,7 @@ class XmlPullParser : public IPackageDeclStack { static bool SkipCurrentElement(XmlPullParser* parser); static bool IsGoodEvent(Event event); - explicit XmlPullParser(std::istream& in); + explicit XmlPullParser(io::InputStream* in); ~XmlPullParser(); /** @@ -169,9 +170,8 @@ class XmlPullParser : public IPackageDeclStack { std::vector<Attribute> attributes; }; - std::istream& in_; + io::InputStream* in_; XML_Parser parser_; - char buffer_[16384]; std::queue<EventData> event_queue_; std::string error_; const std::string empty_; @@ -228,18 +228,15 @@ inline ::std::ostream& operator<<(::std::ostream& out, return out; } -inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, - size_t start_depth) { +inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, size_t start_depth) { Event event; // First get back to the start depth. - while (IsGoodEvent(event = parser->Next()) && - parser->depth() > start_depth + 1) { + while (IsGoodEvent(event = parser->Next()) && parser->depth() > start_depth + 1) { } // Now look for the first good node. - while ((event != Event::kEndElement || parser->depth() > start_depth) && - IsGoodEvent(event)) { + while ((event != Event::kEndElement || parser->depth() > start_depth) && IsGoodEvent(event)) { switch (event) { case Event::kText: case Event::kComment: diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp index 1cce4850cac5..681d9d48173f 100644 --- a/tools/aapt2/xml/XmlPullParser_test.cpp +++ b/tools/aapt2/xml/XmlPullParser_test.cpp @@ -16,21 +16,22 @@ #include "xml/XmlPullParser.h" -#include <sstream> - #include "androidfw/StringPiece.h" +#include "io/StringInputStream.h" #include "test/Test.h" -using android::StringPiece; +using ::aapt::io::StringInputStream; +using ::android::StringPiece; namespace aapt { TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) { - std::stringstream str; - str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" - "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>"; - xml::XmlPullParser parser(str); + std::string str = + R"(<?xml version="1.0" encoding="utf-8"?> + <a><b><c xmlns:a="http://schema.org"><d/></c><e/></b></a>)"; + StringInputStream input(str); + xml::XmlPullParser parser(&input); const size_t depth_outer = parser.depth(); ASSERT_TRUE(xml::XmlPullParser::NextChildNode(&parser, depth_outer)); |