11ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/*
21ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Copyright (C) 2015 The Android Open Source Project
31ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *
41ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
51ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * you may not use this file except in compliance with the License.
61ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * You may obtain a copy of the License at
71ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *
81ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
91ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *
101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Unless required by applicable law or agreed to in writing, software
111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * See the License for the specific language governing permissions and
141ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * limitations under the License.
151ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
161ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ConfigDescription.h"
181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "Diagnostics.h"
191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "Flags.h"
201ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceParser.h"
211ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceTable.h"
221ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "compile/IdAssigner.h"
231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "compile/Png.h"
24393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski#include "compile/PseudolocaleGenerator.h"
251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "compile/XmlIdCollector.h"
26a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski#include "flatten/Archive.h"
271ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "flatten/XmlFlattener.h"
2859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski#include "proto/ProtoSerialize.h"
291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Files.h"
301ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Maybe.h"
311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Util.h"
32467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlDom.h"
33467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlPullParser.h"
341ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
3659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski#include <google/protobuf/io/coded_stream.h>
3759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
38a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski#include <dirent.h>
391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include <fstream>
401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include <string>
411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskinamespace aapt {
431ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistruct ResourcePathData {
451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    Source source;
461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::u16string resourceDir;
471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::u16string name;
481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::string extension;
491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    // Original config str. We keep this because when we parse the config, we may add on
511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    // version qualifiers. We want to preserve the original input so the output is easily
521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    // computed before hand.
531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::string configStr;
541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    ConfigDescription config;
551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
571ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/**
581ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Resource file paths are expected to look like:
591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * [--/res/]type[-config]/name
601ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistatic Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                                                       std::string* outError) {
631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::vector<std::string> parts = util::split(path, file::sDirSep);
641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (parts.size() < 2) {
651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (outError) *outError = "bad resource path";
661ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return {};
671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::string& dir = parts[parts.size() - 2];
701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    StringPiece dirStr = dir;
711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    StringPiece configStr;
731ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    ConfigDescription config;
741ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    size_t dashPos = dir.find('-');
751ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (dashPos != std::string::npos) {
761ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
771ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!ConfigDescription::parse(configStr, &config)) {
781ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (outError) {
791ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                std::stringstream errStr;
801ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                errStr << "invalid configuration '" << configStr << "'";
811ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                *outError = errStr.str();
821ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
831ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            return {};
841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
851ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        dirStr = dirStr.substr(0, dashPos);
861ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
871ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
881ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::string& filename = parts[parts.size() - 1];
891ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    StringPiece name = filename;
901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    StringPiece extension;
911ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    size_t dotPos = filename.find('.');
921ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (dotPos != std::string::npos) {
931ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
941ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        name = name.substr(0, dotPos);
951ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
961ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
971ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    return ResourcePathData{
98a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            Source(path),
991ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            util::utf8ToUtf16(dirStr),
1001ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            util::utf8ToUtf16(name),
1011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            extension.toString(),
1021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            configStr.toString(),
1031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            config
1041ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    };
1051ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1061ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1071ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistruct CompileOptions {
1081ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::string outputPath;
109a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    Maybe<std::string> resDir;
110393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    bool pseudolocalize = false;
111979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski    bool legacyMode = false;
1121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    bool verbose = false;
1131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
1141ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
115a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinskistatic std::string buildIntermediateFilename(const ResourcePathData& data) {
1161ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::stringstream name;
1171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    name << data.resourceDir;
1181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!data.configStr.empty()) {
1191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        name << "-" << data.configStr;
1201ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
12152364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    name << "_" << data.name;
12252364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    if (!data.extension.empty()) {
12352364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        name << "." << data.extension;
12452364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    }
12552364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    name << ".flat";
126a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    return name.str();
127a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
128a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
129a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinskistatic bool isHidden(const StringPiece& filename) {
130a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    return util::stringStartsWith<char>(filename, ".");
131a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
132a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
133a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski/**
134a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski * Walks the res directory structure, looking for resource files.
135a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski */
136a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinskistatic bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
137a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                                  std::vector<ResourcePathData>* outPathData) {
138a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    const std::string& rootDir = options.resDir.value();
139a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()), closedir);
140a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    if (!d) {
141a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        context->getDiagnostics()->error(DiagMessage() << strerror(errno));
142a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        return false;
143a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    }
144a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
145a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    while (struct dirent* entry = readdir(d.get())) {
146a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        if (isHidden(entry->d_name)) {
147a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            continue;
148a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        }
149a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
150a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        std::string prefixPath = rootDir;
151a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        file::appendPath(&prefixPath, entry->d_name);
152a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
153a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
154a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            continue;
155a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        }
156a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
157a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()), closedir);
158a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        if (!subDir) {
159a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            context->getDiagnostics()->error(DiagMessage() << strerror(errno));
160a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            return false;
161a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        }
162a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
163a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        while (struct dirent* leafEntry = readdir(subDir.get())) {
164a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            if (isHidden(leafEntry->d_name)) {
165a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                continue;
166a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            }
167a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
168a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            std::string fullPath = prefixPath;
169a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            file::appendPath(&fullPath, leafEntry->d_name);
170a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
171a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            std::string errStr;
172a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            Maybe<ResourcePathData> pathData = extractResourcePathData(fullPath, &errStr);
173a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            if (!pathData) {
174a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                context->getDiagnostics()->error(DiagMessage() << errStr);
175a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                return false;
176a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            }
177a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
178a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            outPathData->push_back(std::move(pathData.value()));
179a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        }
180a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    }
181a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    return true;
1821ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1831ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistatic bool compileTable(IAaptContext* context, const CompileOptions& options,
185a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                         const ResourcePathData& pathData, IArchiveWriter* writer,
186a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                         const std::string& outputPath) {
1871ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    ResourceTable table;
1881ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    {
1891ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        std::ifstream fin(pathData.source.path, std::ifstream::binary);
1901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!fin) {
1911ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
1921ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            return false;
1931ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
1941ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1951ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1961ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        // Parse the values file from XML.
197467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski        xml::XmlPullParser xmlParser(fin);
1989f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
1999f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski        ResourceParserOptions parserOptions;
200979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski        parserOptions.errorOnPositionalArguments = !options.legacyMode;
2019f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
2029f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski        // If the filename includes donottranslate, then the default translatable is false.
2039f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski        parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos;
2049f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
2051ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        ResourceParser resParser(context->getDiagnostics(), &table, pathData.source,
2069f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski                                 pathData.config, parserOptions);
2071ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!resParser.parse(&xmlParser)) {
2081ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            return false;
2091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
2101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
2111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        fin.close();
2121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
2131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
214393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    if (options.pseudolocalize) {
215393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        // Generate pseudo-localized strings (en-XA and ar-XB).
216393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        // These are created as weak symbols, and are only generated from default configuration
217393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        // strings and plurals.
218393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        PseudolocaleGenerator pseudolocaleGenerator;
219393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        if (!pseudolocaleGenerator.consume(context, &table)) {
220393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski            return false;
221393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski        }
222393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
223393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
22483f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski    // Ensure we have the compilation package at least.
22583f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski    table.createPackage(context->getCompilationPackage());
22683f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski
227a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    // Assign an ID to any package that has resources.
22883f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski    for (auto& pkg : table.packages) {
22983f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski        if (!pkg->id) {
23083f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski            // If no package ID was set while parsing (public identifiers), auto assign an ID.
23183f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski            pkg->id = context->getPackageId();
23283f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski        }
2339ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski    }
2349ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
23559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    // Create the file/zip entry.
23659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writer->startEntry(outputPath, 0)) {
23759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
2381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
2391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
2401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
24159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
24259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
24359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
24459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    {
24559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
24659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
24759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
24859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski            context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
24959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski            return false;
25059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        }
25159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
25259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
25359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writer->finishEntry()) {
25459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to finish entry");
2551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
2561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
25759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    return true;
25859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
2591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
26059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinskistatic bool writeHeaderAndBufferToWriter(const StringPiece& outputPath, const ResourceFile& file,
26159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                         const BigBuffer& buffer, IArchiveWriter* writer,
26259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                         IDiagnostics* diag) {
26359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    // Start the entry so we can write the header.
264a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    if (!writer->startEntry(outputPath, 0)) {
26559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        diag->error(DiagMessage(outputPath) << "failed to open file");
26659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        return false;
26759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
26859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
26959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    // Create the header.
27059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
27159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
27259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    {
27359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // The stream must be destroyed before we finish the entry, or else
27459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // some data won't be flushed.
27559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
27659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // interface.
27759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
27859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
27959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        for (const BigBuffer::Block& block : buffer) {
28059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski            if (!outputStream.Write(block.buffer.get(), block.size)) {
28159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                diag->error(DiagMessage(outputPath) << "failed to write data");
28259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                return false;
28359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski            }
28459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        }
28559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
28659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
28759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writer->finishEntry()) {
28859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        diag->error(DiagMessage(outputPath) << "failed to finish writing data");
2891ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
2901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
29159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    return true;
29259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
2931ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
29459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinskistatic bool writeHeaderAndMmapToWriter(const StringPiece& outputPath, const ResourceFile& file,
29559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                       const android::FileMap& map, IArchiveWriter* writer,
29659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                       IDiagnostics* diag) {
29759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    // Start the entry so we can write the header.
29859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writer->startEntry(outputPath, 0)) {
29959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        diag->error(DiagMessage(outputPath) << "failed to open file");
30059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        return false;
30159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
30259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
30359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    // Create the header.
30459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
30559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
30659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    {
30759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // The stream must be destroyed before we finish the entry, or else
30859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // some data won't be flushed.
30959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
31059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        // interface.
31159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
31259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
31359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        if (!outputStream.Write(map.getDataPtr(), map.getDataLength())) {
31459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski            diag->error(DiagMessage(outputPath) << "failed to write data");
31559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski            return false;
316a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        }
3171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
318a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
31959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writer->finishEntry()) {
32059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        diag->error(DiagMessage(outputPath) << "failed to finish writing data");
32159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        return false;
32259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
32359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    return true;
3241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistatic bool compileXml(IAaptContext* context, const CompileOptions& options,
327a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                       const ResourcePathData& pathData, IArchiveWriter* writer,
328a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                       const std::string& outputPath) {
3291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
330467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    std::unique_ptr<xml::XmlResource> xmlRes;
3311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    {
3321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        std::ifstream fin(pathData.source.path, std::ifstream::binary);
3331ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!fin) {
3341ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
3351ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            return false;
3361ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
3371ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
3391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        fin.close();
3411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
3421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3431ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!xmlRes) {
3441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
3451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
3461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    // Collect IDs that are defined here.
3481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    XmlIdCollector collector;
3491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!collector.consume(context, xmlRes.get())) {
3501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
3511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
3521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
353a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    xmlRes->file.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
3541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    xmlRes->file.config = pathData.config;
3551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    xmlRes->file.source = pathData.source;
3561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3571ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    BigBuffer buffer(1024);
3581ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    XmlFlattenerOptions xmlFlattenerOptions;
3591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    xmlFlattenerOptions.keepRawValues = true;
36059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    XmlFlattener flattener(&buffer, xmlFlattenerOptions);
3611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!flattener.consume(context, xmlRes.get())) {
3621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
3631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
3641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
36559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writeHeaderAndBufferToWriter(outputPath, xmlRes->file, buffer, writer,
36659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                      context->getDiagnostics())) {
3671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
3681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
36959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    return true;
3701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistatic bool compilePng(IAaptContext* context, const CompileOptions& options,
373a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                       const ResourcePathData& pathData, IArchiveWriter* writer,
374a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                       const std::string& outputPath) {
3751ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    BigBuffer buffer(4096);
3761ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    ResourceFile resFile;
377a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
3781ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    resFile.config = pathData.config;
3791ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    resFile.source = pathData.source;
3801ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3811ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    {
3821ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        std::ifstream fin(pathData.source.path, std::ifstream::binary);
3831ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (!fin) {
3841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
3851ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            return false;
3861ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
3871ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
3881ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        Png png(context->getDiagnostics());
38959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski        if (!png.process(pathData.source, &fin, &buffer, {})) {
3901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            return false;
3911ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
3921ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
3931ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
39459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
39559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                      context->getDiagnostics())) {
3961ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
3971ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
39859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    return true;
3991ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
4001ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
4011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistatic bool compileFile(IAaptContext* context, const CompileOptions& options,
402a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                        const ResourcePathData& pathData, IArchiveWriter* writer,
403a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                        const std::string& outputPath) {
4041ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    BigBuffer buffer(256);
4051ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    ResourceFile resFile;
406a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
4071ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    resFile.config = pathData.config;
4081ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    resFile.source = pathData.source;
4091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
4101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::string errorStr;
4111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
4121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!f) {
4131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
4141ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
4151ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
4161ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
41759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
41859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                    context->getDiagnostics())) {
41952364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski        return false;
42052364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    }
42152364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    return true;
4221ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
4231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
4241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskiclass CompileContext : public IAaptContext {
4251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskipublic:
426355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski    void setVerbose(bool val) {
427355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski        mVerbose = val;
428355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski    }
429355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
430355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski    bool verbose() override {
431355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski        return mVerbose;
432355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski    }
433355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
4341ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    IDiagnostics* getDiagnostics() override {
4351ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski       return &mDiagnostics;
4361ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
4371ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
4381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    NameMangler* getNameMangler() override {
4391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski       abort();
4401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski       return nullptr;
4411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
4421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
44364587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski    const std::u16string& getCompilationPackage() override {
44464587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski        static std::u16string empty;
44564587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski        return empty;
4461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
4471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
4481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    uint8_t getPackageId() override {
4499ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski       return 0x0;
4501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
4511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
45264587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski    SymbolTable* getExternalSymbols() override {
4531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski       abort();
4541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski       return nullptr;
4551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
45664587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski
45764587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinskiprivate:
45864587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski    StdErrDiagnostics mDiagnostics;
45964587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski    bool mVerbose = false;
46064587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski
4611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
4621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
4631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/**
4641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
4651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
4661ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskiint compile(const std::vector<StringPiece>& args) {
467355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski    CompileContext context;
4681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    CompileOptions options;
4691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
470355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski    bool verbose = false;
4711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    Flags flags = Flags()
4721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            .requiredFlag("-o", "Output path", &options.outputPath)
473a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
474393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski            .optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
475393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski                            "(en-XA and ar-XB)", &options.pseudolocalize)
476979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski            .optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
477979ccb2e6f3f1f7f00a448eb440a85daf033dc9eAdam Lesinski                            &options.legacyMode)
478355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski            .optionalSwitch("-v", "Enables verbose logging", &verbose);
4791ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (!flags.parse("aapt2 compile", args, &std::cerr)) {
4801ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return 1;
4811ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
4821ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
483355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski    context.setVerbose(verbose);
484355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
485a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    std::unique_ptr<IArchiveWriter> archiveWriter;
4861ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
4871ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    std::vector<ResourcePathData> inputData;
488a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    if (options.resDir) {
489a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        if (!flags.getArgs().empty()) {
490a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            // Can't have both files and a resource directory.
491a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            context.getDiagnostics()->error(DiagMessage() << "files given but --dir specified");
492a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            flags.usage("aapt2 compile", &std::cerr);
493a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            return 1;
494a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        }
4951ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
496a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        if (!loadInputFilesFromDir(&context, options, &inputData)) {
4971ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            return 1;
4981ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
499a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
500a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);
501a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
502a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    } else {
503a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        inputData.reserve(flags.getArgs().size());
504a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
505a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        // Collect data from the path for each input file.
506a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        for (const std::string& arg : flags.getArgs()) {
507a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            std::string errorStr;
508a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            if (Maybe<ResourcePathData> pathData = extractResourcePathData(arg, &errorStr)) {
509a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                inputData.push_back(std::move(pathData.value()));
510a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            } else {
511a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg << ")");
512a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                return 1;
513a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            }
514a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        }
515a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
516a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(), options.outputPath);
517a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    }
518a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
519a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    if (!archiveWriter) {
520a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski        return false;
5211ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
5221ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    bool error = false;
5241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    for (ResourcePathData& pathData : inputData) {
5251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (options.verbose) {
5261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing");
5271ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
5281ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        if (pathData.resourceDir == u"values") {
5301ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            // Overwrite the extension.
5311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            pathData.extension = "arsc";
5321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
533a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            const std::string outputFilename = buildIntermediateFilename(pathData);
534a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            if (!compileTable(&context, options, pathData, archiveWriter.get(), outputFilename)) {
5351ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
5361ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
5371ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        } else {
539a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            const std::string outputFilename = buildIntermediateFilename(pathData);
5401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
5411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                if (*type != ResourceType::kRaw) {
5421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    if (pathData.extension == "xml") {
543a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                        if (!compileXml(&context, options, pathData, archiveWriter.get(),
544a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                                        outputFilename)) {
5451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                            error = true;
5461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                        }
5471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    } else if (pathData.extension == "png" || pathData.extension == "9.png") {
548a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                        if (!compilePng(&context, options, pathData, archiveWriter.get(),
549a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                                        outputFilename)) {
5501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                            error = true;
5511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                        }
5521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    } else {
553a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                        if (!compileFile(&context, options, pathData, archiveWriter.get(),
554a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                                         outputFilename)) {
5551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                            error = true;
5561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                        }
5571ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    }
5581ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                } else {
559a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                    if (!compileFile(&context, options, pathData, archiveWriter.get(),
560a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski                                     outputFilename)) {
5611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                        error = true;
5621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                    }
5631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                }
5641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            } else {
5651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                context.getDiagnostics()->error(
5661ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                        DiagMessage() << "invalid file path '" << pathData.source << "'");
5671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                error = true;
5681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
5691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
5701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
5711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    if (error) {
5731ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return 1;
5741ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
5751ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    return 0;
5761ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
5771ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5781ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski} // namespace aapt
579