Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a40e972f authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT2: Support compiling a res/ directory and output to zip

This allows us to compile an entire directory and output to a single
file. This is important to support generated resources in the make
build, since we may not know what resources get generated.

The link step will accept the zip and read the contents of it as if they
were passed in on the command line.

Change-Id: If1a51b0abe772350c24074353eb4989953c2e0cb
parent 1cffc196
Loading
Loading
Loading
Loading
+154 −65
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "compile/IdAssigner.h"
#include "compile/Png.h"
#include "compile/XmlIdCollector.h"
#include "flatten/Archive.h"
#include "flatten/FileExportWriter.h"
#include "flatten/TableFlattener.h"
#include "flatten/XmlFlattener.h"
@@ -31,6 +32,7 @@
#include "xml/XmlDom.h"
#include "xml/XmlPullParser.h"

#include <dirent.h>
#include <fstream>
#include <string>

@@ -90,7 +92,7 @@ static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
    }

    return ResourcePathData{
            Source{ path },
            Source(path),
            util::utf8ToUtf16(dirStr),
            util::utf8ToUtf16(name),
            extension.toString(),
@@ -101,25 +103,79 @@ static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,

struct CompileOptions {
    std::string outputPath;
    Maybe<std::string> resDir;
    Maybe<std::u16string> product;
    bool verbose = false;
};

static std::string buildIntermediateFilename(const std::string outDir,
                                             const ResourcePathData& data) {
static std::string buildIntermediateFilename(const ResourcePathData& data) {
    std::stringstream name;
    name << data.resourceDir;
    if (!data.configStr.empty()) {
        name << "-" << data.configStr;
    }
    name << "_" << data.name << "." << data.extension << ".flat";
    std::string outPath = outDir;
    file::appendPath(&outPath, name.str());
    return outPath;
    return name.str();
}

static bool isHidden(const StringPiece& filename) {
    return util::stringStartsWith<char>(filename, ".");
}

/**
 * Walks the res directory structure, looking for resource files.
 */
static bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
                                  std::vector<ResourcePathData>* outPathData) {
    const std::string& rootDir = options.resDir.value();
    std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()), closedir);
    if (!d) {
        context->getDiagnostics()->error(DiagMessage() << strerror(errno));
        return false;
    }

    while (struct dirent* entry = readdir(d.get())) {
        if (isHidden(entry->d_name)) {
            continue;
        }

        std::string prefixPath = rootDir;
        file::appendPath(&prefixPath, entry->d_name);

        if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
            continue;
        }

        std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()), closedir);
        if (!subDir) {
            context->getDiagnostics()->error(DiagMessage() << strerror(errno));
            return false;
        }

        while (struct dirent* leafEntry = readdir(subDir.get())) {
            if (isHidden(leafEntry->d_name)) {
                continue;
            }

            std::string fullPath = prefixPath;
            file::appendPath(&fullPath, leafEntry->d_name);

            std::string errStr;
            Maybe<ResourcePathData> pathData = extractResourcePathData(fullPath, &errStr);
            if (!pathData) {
                context->getDiagnostics()->error(DiagMessage() << errStr);
                return false;
            }

            outPathData->push_back(std::move(pathData.value()));
        }
    }
    return true;
}

static bool compileTable(IAaptContext* context, const CompileOptions& options,
                         const ResourcePathData& pathData, const std::string& outputPath) {
                         const ResourcePathData& pathData, IArchiveWriter* writer,
                         const std::string& outputPath) {
    ResourceTable table;
    {
        std::ifstream fin(pathData.source.path, std::ifstream::binary);
@@ -150,6 +206,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options,
    // Ensure we have the compilation package at least.
    table.createPackage(context->getCompilationPackage());

    // 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.
@@ -172,23 +229,24 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options,
        return false;
    }

    // Build the output filename.
    std::ofstream fout(outputPath, std::ofstream::binary);
    if (!fout) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
    if (!writer->startEntry(outputPath, 0)) {
        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
        return false;
    }

    // Write it to disk.
    if (!util::writeAll(fout, buffer)) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
        return false;
    }
    if (writer->writeEntry(buffer)) {
        if (writer->finishEntry()) {
            return true;
        }
    }

    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
    return false;
}

static bool compileXml(IAaptContext* context, const CompileOptions& options,
                       const ResourcePathData& pathData, const std::string& outputPath) {
                       const ResourcePathData& pathData, IArchiveWriter* writer,
                       const std::string& outputPath) {

    std::unique_ptr<xml::XmlResource> xmlRes;

@@ -214,7 +272,7 @@ static bool compileXml(IAaptContext* context, const CompileOptions& options,
        return false;
    }

    xmlRes->file.name = ResourceName{ {}, *parseResourceType(pathData.resourceDir), pathData.name };
    xmlRes->file.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
    xmlRes->file.config = pathData.config;
    xmlRes->file.source = pathData.source;

@@ -230,25 +288,27 @@ static bool compileXml(IAaptContext* context, const CompileOptions& options,

    fileExportWriter.finish();

    std::ofstream fout(outputPath, std::ofstream::binary);
    if (!fout) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
    if (!writer->startEntry(outputPath, 0)) {
        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
        return false;
    }

    // Write it to disk.
    if (!util::writeAll(fout, buffer)) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
        return false;
    }
    if (writer->writeEntry(buffer)) {
        if (writer->finishEntry()) {
            return true;
        }
    }

    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
    return false;
}

static bool compilePng(IAaptContext* context, const CompileOptions& options,
                       const ResourcePathData& pathData, const std::string& outputPath) {
                       const ResourcePathData& pathData, IArchiveWriter* writer,
                       const std::string& outputPath) {
    BigBuffer buffer(4096);
    ResourceFile resFile;
    resFile.name = ResourceName{ {}, *parseResourceType(pathData.resourceDir), pathData.name };
    resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
    resFile.config = pathData.config;
    resFile.source = pathData.source;

@@ -269,24 +329,27 @@ static bool compilePng(IAaptContext* context, const CompileOptions& options,

    fileExportWriter.finish();

    std::ofstream fout(outputPath, std::ofstream::binary);
    if (!fout) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
    if (!writer->startEntry(outputPath, 0)) {
        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
        return false;
    }

    if (!util::writeAll(fout, buffer)) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
        return false;
    }
    if (writer->writeEntry(buffer)) {
        if (writer->finishEntry()) {
            return true;
        }
    }

    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
    return false;
}

static bool compileFile(IAaptContext* context, const CompileOptions& options,
                        const ResourcePathData& pathData, const std::string& outputPath) {
                        const ResourcePathData& pathData, IArchiveWriter* writer,
                        const std::string& outputPath) {
    BigBuffer buffer(256);
    ResourceFile resFile;
    resFile.name = ResourceName{ {}, *parseResourceType(pathData.resourceDir), pathData.name };
    resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
    resFile.config = pathData.config;
    resFile.source = pathData.source;

@@ -299,9 +362,8 @@ static bool compileFile(IAaptContext* context, const CompileOptions& options,
        return false;
    }

    std::ofstream fout(outputPath, std::ofstream::binary);
    if (!fout) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
    if (!writer->startEntry(outputPath, 0)) {
        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
        return false;
    }

@@ -309,17 +371,18 @@ static bool compileFile(IAaptContext* context, const CompileOptions& options,
    // the buffer the entire file.
    fileExportWriter.getChunkHeader()->size =
            util::hostToDevice32(buffer.size() + f.value().getDataLength());
    if (!util::writeAll(fout, buffer)) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
        return false;

    if (writer->writeEntry(buffer)) {
        if (writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
            if (writer->finishEntry()) {
                return true;
            }
        }
    }

    if (!fout.write((const char*) f.value().getDataPtr(), f.value().getDataLength())) {
        context->getDiagnostics()->error(DiagMessage(Source{ outputPath }) << strerror(errno));
    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
    return false;
}
    return true;
}

class CompileContext : public IAaptContext {
private:
@@ -359,6 +422,7 @@ int compile(const std::vector<StringPiece>& args) {
    Flags flags = Flags()
            .requiredFlag("-o", "Output path", &options.outputPath)
            .optionalFlag("--product", "Product type to compile", &product)
            .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
            .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
    if (!flags.parse("aapt2 compile", args, &std::cerr)) {
        return 1;
@@ -369,8 +433,24 @@ int compile(const std::vector<StringPiece>& args) {
    }

    CompileContext context;
    std::unique_ptr<IArchiveWriter> archiveWriter;

    std::vector<ResourcePathData> inputData;
    if (options.resDir) {
        if (!flags.getArgs().empty()) {
            // Can't have both files and a resource directory.
            context.getDiagnostics()->error(DiagMessage() << "files given but --dir specified");
            flags.usage("aapt2 compile", &std::cerr);
            return 1;
        }

        if (!loadInputFilesFromDir(&context, options, &inputData)) {
            return 1;
        }

        archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);

    } else {
        inputData.reserve(flags.getArgs().size());

        // Collect data from the path for each input file.
@@ -384,6 +464,13 @@ int compile(const std::vector<StringPiece>& args) {
            }
        }

        archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(), options.outputPath);
    }

    if (!archiveWriter) {
        return false;
    }

    bool error = false;
    for (ResourcePathData& pathData : inputData) {
        if (options.verbose) {
@@ -394,32 +481,34 @@ int compile(const std::vector<StringPiece>& args) {
            // Overwrite the extension.
            pathData.extension = "arsc";

            const std::string outputFilename = buildIntermediateFilename(
                    options.outputPath, pathData);
            if (!compileTable(&context, options, pathData, outputFilename)) {
            const std::string outputFilename = buildIntermediateFilename(pathData);
            if (!compileTable(&context, options, pathData, archiveWriter.get(), outputFilename)) {
                error = true;
            }

        } else {
            const std::string outputFilename = buildIntermediateFilename(options.outputPath,
                                                                         pathData);
            const std::string outputFilename = buildIntermediateFilename(pathData);
            if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
                if (*type != ResourceType::kRaw) {
                    if (pathData.extension == "xml") {
                        if (!compileXml(&context, options, pathData, outputFilename)) {
                        if (!compileXml(&context, options, pathData, archiveWriter.get(),
                                        outputFilename)) {
                            error = true;
                        }
                    } else if (pathData.extension == "png" || pathData.extension == "9.png") {
                        if (!compilePng(&context, options, pathData, outputFilename)) {
                        if (!compilePng(&context, options, pathData, archiveWriter.get(),
                                        outputFilename)) {
                            error = true;
                        }
                    } else {
                        if (!compileFile(&context, options, pathData, outputFilename)) {
                        if (!compileFile(&context, options, pathData, archiveWriter.get(),
                                         outputFilename)) {
                            error = true;
                        }
                    }
                } else {
                    if (!compileFile(&context, options, pathData, outputFilename)) {
                    if (!compileFile(&context, options, pathData, archiveWriter.get(),
                                     outputFilename)) {
                        error = true;
                    }
                }
+89 −86
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@
#include "util/Files.h"
#include "util/StringPiece.h"

#include <fstream>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
@@ -30,70 +30,85 @@ namespace {

struct DirectoryWriter : public IArchiveWriter {
    std::string mOutDir;
    std::vector<std::unique_ptr<ArchiveEntry>> mEntries;
    std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };

    explicit DirectoryWriter(const StringPiece& outDir) : mOutDir(outDir.toString()) {
    bool open(IDiagnostics* diag, const StringPiece& outDir) {
        mOutDir = outDir.toString();
        file::FileType type = file::getFileType(mOutDir);
        if (type == file::FileType::kNonexistant) {
            diag->error(DiagMessage() << "directory " << mOutDir << " does not exist");
            return false;
        } else if (type != file::FileType::kDirectory) {
            diag->error(DiagMessage() << mOutDir << " is not a directory");
            return false;
        }

    ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
                             const BigBuffer& buffer) override {
        std::string fullPath = mOutDir;
        file::appendPath(&fullPath, path);
        file::mkdirs(file::getStem(fullPath));

        std::ofstream fout(fullPath, std::ofstream::binary);
        if (!fout) {
            return nullptr;
        return true;
    }

        if (!util::writeAll(fout, buffer)) {
            return nullptr;
        }

        mEntries.push_back(util::make_unique<ArchiveEntry>(fullPath, flags, buffer.size()));
        return mEntries.back().get();
    bool startEntry(const StringPiece& path, uint32_t flags) override {
        if (mFile) {
            return false;
        }

    ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags, android::FileMap* fileMap,
                             size_t offset, size_t len) override {
        std::string fullPath = mOutDir;
        file::appendPath(&fullPath, path);
        file::mkdirs(file::getStem(fullPath));

        std::ofstream fout(fullPath, std::ofstream::binary);
        if (!fout) {
            return nullptr;
        mFile = { fopen(fullPath.data(), "wb"), fclose };
        if (!mFile) {
            return false;
        }
        return true;
    }

        if (!fout.write((const char*) fileMap->getDataPtr() + offset, len)) {
            return nullptr;
    bool writeEntry(const BigBuffer& buffer) override {
        if (!mFile) {
            return false;
        }

        mEntries.push_back(util::make_unique<ArchiveEntry>(fullPath, flags, len));
        return mEntries.back().get();
        for (const BigBuffer::Block& b : buffer) {
            if (fwrite(b.buffer.get(), 1, b.size, mFile.get()) != b.size) {
                mFile.reset(nullptr);
                return false;
            }
        }
        return true;
    }

    virtual ~DirectoryWriter() {
    bool writeEntry(const void* data, size_t len) override {
        if (fwrite(data, 1, len, mFile.get()) != len) {
            mFile.reset(nullptr);
            return false;
        }
        return true;
    }

    bool finishEntry() override {
        if (!mFile) {
            return false;
        }
        mFile.reset(nullptr);
        return true;
    }
};

struct ZipFileWriter : public IArchiveWriter {
    FILE* mFile;
    std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
    std::unique_ptr<ZipWriter> mWriter;
    std::vector<std::unique_ptr<ArchiveEntry>> mEntries;

    explicit ZipFileWriter(const StringPiece& path) {
        mFile = fopen(path.data(), "w+b");
        if (mFile) {
            mWriter = util::make_unique<ZipWriter>(mFile);
    bool open(IDiagnostics* diag, const StringPiece& path) {
        mFile = { fopen(path.data(), "w+b"), fclose };
        if (!mFile) {
            diag->error(DiagMessage() << "failed to open " << path << ": " << strerror(errno));
            return false;
        }
        mWriter = util::make_unique<ZipWriter>(mFile.get());
        return true;
    }

    ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
                             const BigBuffer& buffer) override {
    bool startEntry(const StringPiece& path, uint32_t flags) override {
        if (!mWriter) {
            return nullptr;
            return false;
        }

        size_t zipFlags = 0;
@@ -107,75 +122,63 @@ struct ZipFileWriter : public IArchiveWriter {

        int32_t result = mWriter->StartEntry(path.data(), zipFlags);
        if (result != 0) {
            return nullptr;
        }

        for (const BigBuffer::Block& b : buffer) {
            result = mWriter->WriteBytes(reinterpret_cast<const uint8_t*>(b.buffer.get()), b.size);
            if (result != 0) {
                return nullptr;
            return false;
        }
        return true;
    }

        result = mWriter->FinishEntry();
    bool writeEntry(const void* data, size_t len) override {
        int32_t result = mWriter->WriteBytes(data, len);
        if (result != 0) {
            return nullptr;
        }

        mEntries.push_back(util::make_unique<ArchiveEntry>(path.toString(), flags, buffer.size()));
        return mEntries.back().get();
            return false;
        }

    ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags, android::FileMap* fileMap,
                             size_t offset, size_t len) override {
        if (!mWriter) {
            return nullptr;
        return true;
    }

        size_t zipFlags = 0;
        if (flags & ArchiveEntry::kCompress) {
            zipFlags |= ZipWriter::kCompress;
        }

        if (flags & ArchiveEntry::kAlign) {
            zipFlags |= ZipWriter::kAlign32;
        }

        int32_t result = mWriter->StartEntry(path.data(), zipFlags);
    bool writeEntry(const BigBuffer& buffer) override {
        for (const BigBuffer::Block& b : buffer) {
            int32_t result = mWriter->WriteBytes(b.buffer.get(), b.size);
            if (result != 0) {
            return nullptr;
                return false;
            }

        result = mWriter->WriteBytes((const char*) fileMap->getDataPtr() + offset, len);
        if (result != 0) {
            return nullptr;
        }
        return true;
    }

        result = mWriter->FinishEntry();
    bool finishEntry() override {
        int32_t result = mWriter->FinishEntry();
        if (result != 0) {
            return nullptr;
            return false;
        }

        mEntries.push_back(util::make_unique<ArchiveEntry>(path.toString(), flags, len));
        return mEntries.back().get();
        return true;
    }

    virtual ~ZipFileWriter() {
        if (mWriter) {
            mWriter->Finish();
            fclose(mFile);
        }
    }
};

} // namespace

std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(const StringPiece& path) {
    return util::make_unique<DirectoryWriter>(path);
std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
                                                             const StringPiece& path) {

    std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
    if (!writer->open(diag, path)) {
        return {};
    }
    return std::move(writer);
}

std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(const StringPiece& path) {
    return util::make_unique<ZipFileWriter>(path);
std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(IDiagnostics* diag,
                                                           const StringPiece& path) {
    std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
    if (!writer->open(diag, path)) {
        return {};
    }
    return std::move(writer);
}

} // namespace aapt
+9 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef AAPT_FLATTEN_ARCHIVE_H
#define AAPT_FLATTEN_ARCHIVE_H

#include "Diagnostics.h"
#include "util/BigBuffer.h"
#include "util/Files.h"
#include "util/StringPiece.h"
@@ -42,15 +43,17 @@ struct ArchiveEntry {
struct IArchiveWriter {
    virtual ~IArchiveWriter() = default;

    virtual ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
                                     const BigBuffer& buffer) = 0;
    virtual ArchiveEntry* writeEntry(const StringPiece& path, uint32_t flags,
                                     android::FileMap* fileMap, size_t offset, size_t len) = 0;
    virtual bool startEntry(const StringPiece& path, uint32_t flags) = 0;
    virtual bool writeEntry(const BigBuffer& buffer) = 0;
    virtual bool writeEntry(const void* data, size_t len) = 0;
    virtual bool finishEntry() = 0;
};

std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(const StringPiece& path);
std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
                                                             const StringPiece& path);

std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(const StringPiece& path);
std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(IDiagnostics* diag,
                                                           const StringPiece& path);

} // namespace aapt

tools/aapt2/io/Data.h

0 → 100644
+85 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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_DATA_H
#define AAPT_IO_DATA_H

#include <utils/FileMap.h>

#include <memory>

namespace aapt {
namespace io {

/**
 * Interface for a block of contiguous memory. An instance of this interface owns the data.
 */
class IData {
public:
    virtual ~IData() = default;

    virtual const void* data() const = 0;
    virtual size_t size() const = 0;
};

/**
 * Implementation of IData that exposes a memory mapped file. The mmapped file is owned by this
 * object.
 */
class MmappedData : public IData {
public:
    explicit MmappedData(android::FileMap&& map) : mMap(std::forward<android::FileMap>(map)) {
    }

    const void* data() const override {
        return mMap.getDataPtr();
    }

    size_t size() const override {
        return mMap.getDataLength();
    }

private:
    android::FileMap mMap;
};

/**
 * Implementation of IData that exposes a block of memory that was malloc'ed (new'ed). The
 * memory is owned by this object.
 */
class MallocData : public IData {
public:
    MallocData(std::unique_ptr<const uint8_t[]> data, size_t size) :
            mData(std::move(data)), mSize(size) {
    }

    const void* data() const override {
        return mData.get();
    }

    size_t size() const override {
        return mSize;
    }

private:
    std::unique_ptr<const uint8_t[]> mData;
    size_t mSize;
};

} // namespace io
} // namespace aapt

#endif /* AAPT_IO_DATA_H */

tools/aapt2/io/File.h

0 → 100644
+72 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading