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
17ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <dirent.h>
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <fstream>
20ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <string>
21ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
22d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "android-base/errors.h"
23d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "android-base/file.h"
24d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "androidfw/StringPiece.h"
25d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "google/protobuf/io/coded_stream.h"
26d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
27d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
281ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ConfigDescription.h"
291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "Diagnostics.h"
301ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "Flags.h"
311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceParser.h"
321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceTable.h"
331ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "compile/IdAssigner.h"
345eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski#include "compile/InlineXmlFormatParser.h"
351ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "compile/Png.h"
36393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski#include "compile/PseudolocaleGenerator.h"
371ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "compile/XmlIdCollector.h"
38a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski#include "flatten/Archive.h"
391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "flatten/XmlFlattener.h"
4006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski#include "io/BigBufferOutputStream.h"
41d0f492db038c6210c1138865d816bfb134376538Adam Lesinski#include "io/Util.h"
4259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski#include "proto/ProtoSerialize.h"
431ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Files.h"
441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Maybe.h"
451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Util.h"
46467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlDom.h"
47467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlPullParser.h"
481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
49d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinskiusing android::StringPiece;
505eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinskiusing google::protobuf::io::CopyingOutputStreamAdaptor;
515eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski
521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskinamespace aapt {
531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistruct ResourcePathData {
55cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Source source;
56ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string resource_dir;
57cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string name;
58cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string extension;
59cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
60cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Original config str. We keep this because when we parse the config, we may
61cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // add on
62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // version qualifiers. We want to preserve the original input so the output is
63cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // easily
64cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // computed before hand.
65ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string config_str;
66cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ConfigDescription config;
671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/**
701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Resource file paths are expected to look like:
711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * [--/res/]type[-config]/name
721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
73ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
74ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                       std::string* out_error) {
75ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::vector<std::string> parts = util::Split(path, file::sDirSep);
76cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (parts.size() < 2) {
77ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (out_error) *out_error = "bad resource path";
78cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
79cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
80cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string& dir = parts[parts.size() - 2];
82ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece dir_str = dir;
83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
84ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece config_str;
85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ConfigDescription config;
86ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t dash_pos = dir.find('-');
87ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (dash_pos != std::string::npos) {
88ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    config_str = dir_str.substr(dash_pos + 1, dir.size() - (dash_pos + 1));
89ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!ConfigDescription::Parse(config_str, &config)) {
90ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (out_error) {
91ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        std::stringstream err_str;
92ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        err_str << "invalid configuration '" << config_str << "'";
93ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        *out_error = err_str.str();
94cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
95cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return {};
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
97ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    dir_str = dir_str.substr(0, dash_pos);
98cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
99cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string& filename = parts[parts.size() - 1];
101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece name = filename;
102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece extension;
103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t dot_pos = filename.find('.');
104ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (dot_pos != std::string::npos) {
105ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    extension = name.substr(dot_pos + 1, filename.size() - (dot_pos + 1));
106ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    name = name.substr(0, dot_pos);
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
109d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  return ResourcePathData{Source(path),          dir_str.to_string(),    name.to_string(),
110d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski                          extension.to_string(), config_str.to_string(), config};
1111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistruct CompileOptions {
114ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string output_path;
115ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<std::string> res_dir;
116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool pseudolocalize = false;
11728e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski  bool no_png_crunch = false;
118ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool legacy_mode = false;
119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool verbose = false;
1201ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
1211ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
122ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic std::string BuildIntermediateFilename(const ResourcePathData& data) {
123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::stringstream name;
124ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  name << data.resource_dir;
125ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!data.config_str.empty()) {
126ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    name << "-" << data.config_str;
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  name << "_" << data.name;
129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!data.extension.empty()) {
130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    name << "." << data.extension;
131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  name << ".flat";
133cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return name.str();
134a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
135a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
136ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool IsHidden(const StringPiece& filename) {
137ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return util::StartsWith(filename, ".");
138a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
139a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
140a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski/**
141a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski * Walks the res directory structure, looking for resource files.
142a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski */
143d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
144d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  std::vector<ResourcePathData>* out_path_data) {
145ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const std::string& root_dir = options.res_dir.value();
14606460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski  std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!d) {
14806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage()
14906460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                     << android::base::SystemErrorCodeToString(errno));
150cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
151cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
152a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
153cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  while (struct dirent* entry = readdir(d.get())) {
154ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (IsHidden(entry->d_name)) {
155cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
156a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    }
1579f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
158ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::string prefix_path = root_dir;
159ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    file::AppendPath(&prefix_path, entry->d_name);
1609f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
161ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (file::GetFileType(prefix_path) != file::FileType::kDirectory) {
162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
1631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
1641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
16506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir);
166ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!subdir) {
16706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage()
16806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
169cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
170393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
171393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
172ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    while (struct dirent* leaf_entry = readdir(subdir.get())) {
173ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (IsHidden(leaf_entry->d_name)) {
174cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
175cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
17683f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski
177ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string full_path = prefix_path;
178ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      file::AppendPath(&full_path, leaf_entry->d_name);
1799ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
180ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string err_str;
18106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      Maybe<ResourcePathData> path_data = ExtractResourcePathData(full_path, &err_str);
182ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!path_data) {
183ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        context->GetDiagnostics()->Error(DiagMessage() << err_str);
1841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
185cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1861ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
187ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_path_data->push_back(std::move(path_data.value()));
18859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
189cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
190cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
19259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
193ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileTable(IAaptContext* context, const CompileOptions& options,
194d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                         const ResourcePathData& path_data, IArchiveWriter* writer,
195ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         const std::string& output_path) {
196cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceTable table;
197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
198ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::ifstream fin(path_data.source.path, std::ifstream::binary);
199cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!fin) {
200ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
20106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
202cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
204cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
205cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Parse the values file from XML.
206ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    xml::XmlPullParser xml_parser(fin);
207cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
208ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceParserOptions parser_options;
209ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    parser_options.error_on_positional_arguments = !options.legacy_mode;
210cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
211cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // If the filename includes donottranslate, then the default translatable is
212cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // false.
213d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos;
214cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
215d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
216ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                              parser_options);
217ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!res_parser.Parse(&xml_parser)) {
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
221cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    fin.close();
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (options.pseudolocalize) {
225cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Generate pseudo-localized strings (en-XA and ar-XB).
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // These are created as weak symbols, and are only generated from default
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // configuration
228cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // strings and plurals.
229ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    PseudolocaleGenerator pseudolocale_generator;
230ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pseudolocale_generator.Consume(context, &table)) {
231cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
233cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
235cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Ensure we have the compilation package at least.
236ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  table.CreatePackage(context->GetCompilationPackage());
237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Assign an ID to any package that has resources.
239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (auto& pkg : table.packages) {
240cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!pkg->id) {
241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // If no package ID was set while parsing (public identifiers), auto
242cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // assign an ID.
243ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      pkg->id = context->GetPackageId();
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
246cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create the file/zip entry.
248ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
24906460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open");
250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
251cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
252cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
253cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
254ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
25706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    // ZeroCopyOutputStream interface.
258ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
259cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
260ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
261ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pb_table->SerializeToZeroCopyStream(&copying_adaptor)) {
26206460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
264cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
265cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
267ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
26806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish entry");
269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
27259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
2731ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
274d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, const ResourceFile& file,
275d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                         const BigBuffer& buffer, IArchiveWriter* writer,
27659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                         IDiagnostics* diag) {
277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
278ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
279ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to open file");
280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
283cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
284ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
28706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    // ZeroCopyOutputStream interface.
288ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
289ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
290cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
292ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1);
293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
29406460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
295ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteCompiledFile(compiled_file.get());
296ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteData(&buffer);
297cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
298ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (output_stream.HadError()) {
299ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag->Error(DiagMessage(output_path) << "failed to write data");
300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
302cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
303cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
304ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
305ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to finish writing data");
306cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
307cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
308cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
30959e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
3101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
311d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const ResourceFile& file,
312d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                       const android::FileMap& map, IArchiveWriter* writer,
31359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                       IDiagnostics* diag) {
314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
315ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
316ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to open file");
317cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
320cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
321ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
322cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
323cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
324ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    // ZeroCopyOutputStream interface.
325ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
326ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
327cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
328cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
329ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1);
330cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
331d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
332ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteCompiledFile(compiled_file.get());
333ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteData(map.getDataPtr(), map.getDataLength());
334cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
335ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (output_stream.HadError()) {
336ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag->Error(DiagMessage(output_path) << "failed to write data");
337cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
340cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
341ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
342ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to finish writing data");
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
344cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
345cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
3461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
348d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& output_path,
349d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  xml::XmlResource* xmlres, CompiledFileOutputStream* out) {
350cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(1024);
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  XmlFlattenerOptions xml_flattener_options;
352ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xml_flattener_options.keep_raw_values = true;
353ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  XmlFlattener flattener(&buffer, xml_flattener_options);
354ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!flattener.Consume(context, xmlres)) {
355cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
357cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
35806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski  std::unique_ptr<pb::CompiledFile> pb_compiled_file = SerializeCompiledFileToPb(xmlres->file);
359ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out->WriteCompiledFile(pb_compiled_file.get());
360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  out->WriteData(&buffer);
361cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
362cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (out->HadError()) {
36306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write data");
364cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
366cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
3675eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski}
3685eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski
369776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinskistatic bool IsValidFile(IAaptContext* context, const StringPiece& input_path) {
370776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  const file::FileType file_type = file::GetFileType(input_path);
371776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  if (file_type != file::FileType::kRegular && file_type != file::FileType::kSymlink) {
372776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    if (file_type == file::FileType::kDirectory) {
373776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path)
374776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                       << "resource file cannot be a directory");
375cc73e990e5381adfa605ccacad431231d9269893Adam Lesinski    } else if (file_type == file::FileType::kNonexistant) {
376cc73e990e5381adfa605ccacad431231d9269893Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path) << "file not found");
377776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    } else {
378776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path)
379776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                       << "not a valid resource file");
380776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    }
381776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    return false;
382776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  }
383776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  return true;
384776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski}
385776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski
386ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileXml(IAaptContext* context, const CompileOptions& options,
387d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const ResourcePathData& path_data, IArchiveWriter* writer,
388d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const std::string& output_path) {
389ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
39006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling XML");
391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
392cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
393ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<xml::XmlResource> xmlres;
394cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
395ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::ifstream fin(path_data.source.path, std::ifstream::binary);
396cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!fin) {
397ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
39806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
399cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
401cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
402ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
403cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    fin.close();
405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
407ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!xmlres) {
408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
411d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  xmlres->file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
412ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xmlres->file.config = path_data.config;
413ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xmlres->file.source = path_data.source;
414cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Collect IDs that are defined here.
416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  XmlIdCollector collector;
417ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!collector.Consume(context, xmlres.get())) {
418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
419cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
420cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
421cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Look for and process any <aapt:attr> tags and create sub-documents.
422ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  InlineXmlFormatParser inline_xml_format_parser;
423ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!inline_xml_format_parser.Consume(context, xmlres.get())) {
424cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
425cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
426cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
427cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
428ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
429d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open file");
430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
431cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
432cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
433cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
434ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
435cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
436cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
437cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // ZeroCopyOutputStream
438cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // interface.
439ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
440ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
441cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
442ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
443ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        inline_xml_format_parser.GetExtractedInlineXmlDocuments();
444cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
445cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
446ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1 + inline_documents.size());
447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
448d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    if (!FlattenXmlToOutStream(context, output_path, xmlres.get(), &output_stream)) {
449cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
450cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
451cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
452ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (auto& inline_xml_doc : inline_documents) {
453d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(), &output_stream)) {
4541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
455cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
4561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
457cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
4581ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
459ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
460d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish writing data");
461cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
462cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
463cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
4641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
4651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
466ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompilePng(IAaptContext* context, const CompileOptions& options,
467d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const ResourcePathData& path_data, IArchiveWriter* writer,
468d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const std::string& output_path) {
469ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
470d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling PNG");
471cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
472cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
473cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(4096);
474ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceFile res_file;
475d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
476ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.config = path_data.config;
477ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.source = path_data.source;
478cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
479cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
480cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::string content;
4812354b568379fe31ba4e774f7a92d4c685a60a2abAdam Lesinski    if (!android::base::ReadFileToString(path_data.source.path, &content,
4822354b568379fe31ba4e774f7a92d4c685a60a2abAdam Lesinski                                         true /*follow_symlinks*/)) {
483d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
484d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
485cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
486cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
487cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
488ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    BigBuffer crunched_png_buffer(4096);
48906460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    io::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
490cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
491cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Ensure that we only keep the chunks we care about if we end up
492cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // using the original PNG instead of the crunched one.
493ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    PngChunkFilter png_chunk_filter(content);
494cc73e990e5381adfa605ccacad431231d9269893Adam Lesinski    std::unique_ptr<Image> image = ReadPng(context, path_data.source, &png_chunk_filter);
495cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!image) {
496cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
499ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::unique_ptr<NinePatch> nine_patch;
500ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (path_data.extension == "9.png") {
501cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::string err;
50206460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      nine_patch = NinePatch::Create(image->rows.get(), image->width, image->height, &err);
503ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!nine_patch) {
504ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        context->GetDiagnostics()->Error(DiagMessage() << err);
5051ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
506cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
507cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
508cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Remove the 1px border around the NinePatch.
509cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Basically the row array is shifted up by 1, and the length is treated
510cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // as height - 2.
511cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // For each row, shift the array to the left by 1, and treat the length as
512cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // width - 2.
513cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      image->width -= 2;
514cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      image->height -= 2;
51506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
516cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (int32_t h = 0; h < image->height; h++) {
517cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
518cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
519cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
520ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (context->IsVerbose()) {
521d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "9-patch: "
522d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                                                      << *nine_patch);
523cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
524cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
526cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Write the crunched PNG.
52706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    if (!WritePng(context, image.get(), nine_patch.get(), &crunched_png_buffer_out, {})) {
528cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
529cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
530cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
531ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (nine_patch != nullptr ||
532ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        crunched_png_buffer_out.ByteCount() <= png_chunk_filter.ByteCount()) {
533cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // No matter what, we must use the re-encoded PNG, even if it is larger.
534cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // 9-patch images must be re-encoded since their borders are stripped.
535ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      buffer.AppendBuffer(std::move(crunched_png_buffer));
536cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
537cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // The re-encoded PNG is larger than the original, and there is
538cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // no mandatory transformation. Use the original.
539ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (context->IsVerbose()) {
54006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski        context->GetDiagnostics()->Note(DiagMessage(path_data.source)
54106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                        << "original PNG is smaller than crunched PNG"
54206460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                        << ", using original");
543cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
544cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
54506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      png_chunk_filter.Rewind();
546ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      BigBuffer filtered_png_buffer(4096);
54706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      io::BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
54806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      io::Copy(&filtered_png_buffer_out, &png_chunk_filter);
549ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      buffer.AppendBuffer(std::move(filtered_png_buffer));
5501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
5511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
552ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (context->IsVerbose()) {
55306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
55406460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      // This will help catch exotic cases where the new code may generate larger PNGs.
555ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream legacy_stream(content);
556ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      BigBuffer legacy_buffer(4096);
557ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Png png(context->GetDiagnostics());
558ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
5591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
561cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
562ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Note(DiagMessage(path_data.source)
563ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                      << "legacy=" << legacy_buffer.size()
564cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                      << " new=" << buffer.size());
5651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
566cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
5671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
568ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!WriteHeaderAndBufferToWriter(output_path, res_file, buffer, writer,
569ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                    context->GetDiagnostics())) {
570cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
571cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
572cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
573cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
574cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
575ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileFile(IAaptContext* context, const CompileOptions& options,
576d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                        const ResourcePathData& path_data, IArchiveWriter* writer,
577ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                        const std::string& output_path) {
578ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
579d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling file");
580cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
581cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
582cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(256);
583ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceFile res_file;
584d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
585ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.config = path_data.config;
586ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.source = path_data.source;
587ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
588ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string error_str;
589ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str);
590cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!f) {
591776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(path_data.source) << "failed to mmap file: "
592776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                     << error_str);
593cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
594cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
595cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
596ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!WriteHeaderAndMmapToWriter(output_path, res_file, f.value(), writer,
597ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                  context->GetDiagnostics())) {
598cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
599cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
600cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
6011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
6021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
6031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskiclass CompileContext : public IAaptContext {
604cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
605820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
606820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  }
607820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington
608b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  PackageType GetPackageType() override {
609b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    // Every compilation unit starts as an app and then gets linked as potentially something else.
610b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    return PackageType::kApp;
611b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  }
612b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski
613d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  void SetVerbose(bool val) {
614d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    verbose_ = val;
615d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
616355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
617d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  bool IsVerbose() override {
618d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return verbose_;
619d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
620355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
621d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  IDiagnostics* GetDiagnostics() override {
622820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington    return diagnostics_;
623d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
6241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
625ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  NameMangler* GetNameMangler() override {
626cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    abort();
627cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return nullptr;
628cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
6291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
630ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const std::string& GetCompilationPackage() override {
631cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    static std::string empty;
632cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return empty;
633cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
6341ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
635d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  uint8_t GetPackageId() override {
636d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0x0;
637d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
6381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
639ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  SymbolTable* GetExternalSymbols() override {
640cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    abort();
641cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return nullptr;
642cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
643fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
644d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  int GetMinSdkVersion() override {
645d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0;
646d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
64764587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski
648cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
649820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  IDiagnostics* diagnostics_;
650ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool verbose_ = false;
6511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
6521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
6531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/**
654cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * Entry point for compilation phase. Parses arguments and dispatches to the
655cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * correct steps.
6561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
657820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warringtonint Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
658820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  CompileContext context(diagnostics);
659cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  CompileOptions options;
660cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
661cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool verbose = false;
662cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Flags flags =
663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      Flags()
664ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .RequiredFlag("-o", "Output path", &options.output_path)
665d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
666ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .OptionalSwitch("--pseudo-localize",
667cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          "Generate resources for pseudo-locales "
668cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          "(en-XA and ar-XB)",
669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          &options.pseudolocalize)
67028e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski          .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
671d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
672d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                          &options.legacy_mode)
673ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .OptionalSwitch("-v", "Enables verbose logging", &verbose);
674ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
675cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
676cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
677cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
678ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  context.SetVerbose(verbose);
679cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
680ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<IArchiveWriter> archive_writer;
681cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
682ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::vector<ResourcePathData> input_data;
683ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (options.res_dir) {
684ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!flags.GetArgs().empty()) {
685cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Can't have both files and a resource directory.
686d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
687ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      flags.Usage("aapt2 compile", &std::cerr);
688cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 1;
689cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
690cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
691ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!LoadInputFilesFromDir(&context, options, &input_data)) {
692cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 1;
693cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
694cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
695d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path);
696cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
697cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
698ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    input_data.reserve(flags.GetArgs().size());
699cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
700cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Collect data from the path for each input file.
701ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const std::string& arg : flags.GetArgs()) {
702ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string error_str;
703d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
704ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        input_data.push_back(std::move(path_data.value()));
705cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
706d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context.GetDiagnostics()->Error(DiagMessage() << error_str << " (" << arg << ")");
7071ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return 1;
708cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
7091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
7101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
711d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path);
712cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
713355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
714ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!archive_writer) {
715dfaecafbe9fb7c5800191edfe3aa5d5938705204Adam Lesinski    return 1;
716cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
7171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
718cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
719ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (ResourcePathData& path_data : input_data) {
720cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (options.verbose) {
721d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
722cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
7231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
724776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    if (!IsValidFile(&context, path_data.source.path)) {
725776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      error = true;
726776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      continue;
727776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    }
728776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski
729ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (path_data.resource_dir == "values") {
730cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Overwrite the extension.
731ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      path_data.extension = "arsc";
732a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
733ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const std::string output_filename = BuildIntermediateFilename(path_data);
734d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (!CompileTable(&context, options, path_data, archive_writer.get(), output_filename)) {
735cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
736cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
737a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
738a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    } else {
739ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const std::string output_filename = BuildIntermediateFilename(path_data);
740d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
741cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (*type != ResourceType::kRaw) {
742ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          if (path_data.extension == "xml") {
743d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompileXml(&context, options, path_data, archive_writer.get(), output_filename)) {
744cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
745a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            }
74628e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski          } else if (!options.no_png_crunch &&
74728e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski                     (path_data.extension == "png" || path_data.extension == "9.png")) {
748d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompilePng(&context, options, path_data, archive_writer.get(), output_filename)) {
749cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
7501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
751cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          } else {
752d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
753cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
7541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
755cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
756cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
757d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
758cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            error = true;
759cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
7601ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
761cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
762d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context.GetDiagnostics()->Error(DiagMessage() << "invalid file path '" << path_data.source
763d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                                      << "'");
764cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
765cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
766cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
767cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
768cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
769cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
770cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
771cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
772cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return 0;
7731ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
7741ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
775cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
776