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
This commit is contained in:
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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());
|
||||
|
||||
102
tools/aapt2/io/FileInputStream.cpp
Normal file
102
tools/aapt2/io/FileInputStream.cpp
Normal file
@@ -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
|
||||
63
tools/aapt2/io/FileInputStream.h
Normal file
63
tools/aapt2/io/FileInputStream.h
Normal file
@@ -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
|
||||
87
tools/aapt2/io/FileInputStream_test.cpp
Normal file
87
tools/aapt2/io/FileInputStream_test.cpp
Normal file
@@ -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
|
||||
50
tools/aapt2/io/StringInputStream.cpp
Normal file
50
tools/aapt2/io/StringInputStream.cpp
Normal file
@@ -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
|
||||
56
tools/aapt2/io/StringInputStream.h
Normal file
56
tools/aapt2/io/StringInputStream.h
Normal file
@@ -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
|
||||
72
tools/aapt2/io/StringInputStream_test.cpp
Normal file
72
tools/aapt2/io/StringInputStream_test.cpp
Normal file
@@ -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
|
||||
216
tools/aapt2/test/Builders.cpp
Normal file
216
tools/aapt2/test/Builders.cpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
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);
|
||||
|
||||
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;
|
||||
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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user