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:
Adam Lesinski
2017-08-02 14:57:43 -07:00
parent f0f9fa3307
commit efeb7af13b
23 changed files with 960 additions and 429 deletions

View File

@@ -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",
],

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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(&copying_adaptor);

View File

@@ -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));

View File

@@ -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 {};

View File

@@ -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());

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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 {};
}

View File

@@ -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:

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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());

View File

@@ -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) {

View File

@@ -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:

View File

@@ -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));