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 <string>
20ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
21d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "android-base/errors.h"
22d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "android-base/file.h"
235b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski#include "android-base/utf8.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"
415b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski#include "io/FileInputStream.h"
42d0f492db038c6210c1138865d816bfb134376538Adam Lesinski#include "io/Util.h"
4359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski#include "proto/ProtoSerialize.h"
441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Files.h"
451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Maybe.h"
461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Util.h"
47467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlDom.h"
48467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski#include "xml/XmlPullParser.h"
491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
505b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinskiusing ::aapt::io::FileInputStream;
515b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinskiusing ::android::StringPiece;
525b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinskiusing ::google::protobuf::io::CopyingOutputStreamAdaptor;
535eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski
541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskinamespace aapt {
551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistruct ResourcePathData {
57cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Source source;
58ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string resource_dir;
59cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string name;
60cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string extension;
61cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
625b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski  // Original config str. We keep this because when we parse the config, we may add on
635b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski  // version qualifiers. We want to preserve the original input so the output is easily
64cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // computed before hand.
65ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string config_str;
66cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ConfigDescription config;
671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
695b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski// Resource file paths are expected to look like: [--/res/]type[-config]/name
70ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
71ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                       std::string* out_error) {
72ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::vector<std::string> parts = util::Split(path, file::sDirSep);
73cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (parts.size() < 2) {
74ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (out_error) *out_error = "bad resource path";
75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
76cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
78cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string& dir = parts[parts.size() - 2];
79ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece dir_str = dir;
80cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
81ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece config_str;
82cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ConfigDescription config;
83ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t dash_pos = dir.find('-');
84ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (dash_pos != std::string::npos) {
85ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    config_str = dir_str.substr(dash_pos + 1, dir.size() - (dash_pos + 1));
86ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!ConfigDescription::Parse(config_str, &config)) {
87ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (out_error) {
88ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        std::stringstream err_str;
89ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        err_str << "invalid configuration '" << config_str << "'";
90ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        *out_error = err_str.str();
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return {};
93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
94ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    dir_str = dir_str.substr(0, dash_pos);
95cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
97cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::string& filename = parts[parts.size() - 1];
98cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece name = filename;
99cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece extension;
100ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  size_t dot_pos = filename.find('.');
101ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (dot_pos != std::string::npos) {
102ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    extension = name.substr(dot_pos + 1, filename.size() - (dot_pos + 1));
103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    name = name.substr(0, dot_pos);
104cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
106d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  return ResourcePathData{Source(path),          dir_str.to_string(),    name.to_string(),
107d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski                          extension.to_string(), config_str.to_string(), config};
1081ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
1101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskistruct CompileOptions {
111ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string output_path;
112ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<std::string> res_dir;
113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool pseudolocalize = false;
11428e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski  bool no_png_crunch = false;
115ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool legacy_mode = false;
116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool verbose = false;
1171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
1181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
119ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic std::string BuildIntermediateFilename(const ResourcePathData& data) {
120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::stringstream name;
121ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  name << data.resource_dir;
122ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!data.config_str.empty()) {
123ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    name << "-" << data.config_str;
124cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
125cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  name << "_" << data.name;
126cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!data.extension.empty()) {
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    name << "." << data.extension;
128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  name << ".flat";
130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return name.str();
131a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
132a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool IsHidden(const StringPiece& filename) {
134ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return util::StartsWith(filename, ".");
135a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
136a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
1375b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski// Walks the res directory structure, looking for resource files.
138d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
139d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  std::vector<ResourcePathData>* out_path_data) {
140ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const std::string& root_dir = options.res_dir.value();
14106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski  std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!d) {
14360d9c2fd55ef8e64ab3f39980b78389cd1317ee5Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(root_dir) << "failed to open directory: "
14406460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                     << android::base::SystemErrorCodeToString(errno));
145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
147a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
148cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  while (struct dirent* entry = readdir(d.get())) {
149ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (IsHidden(entry->d_name)) {
150cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
151a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    }
1529f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
153ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::string prefix_path = root_dir;
154ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    file::AppendPath(&prefix_path, entry->d_name);
1559f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
156ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (file::GetFileType(prefix_path) != file::FileType::kDirectory) {
157cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
1581ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
1591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
16006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir);
161ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!subdir) {
16260d9c2fd55ef8e64ab3f39980b78389cd1317ee5Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(prefix_path) << "failed to open directory: "
16306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
164cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
165393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
166393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
167ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    while (struct dirent* leaf_entry = readdir(subdir.get())) {
168ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (IsHidden(leaf_entry->d_name)) {
169cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
170cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
17183f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski
172ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string full_path = prefix_path;
173ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      file::AppendPath(&full_path, leaf_entry->d_name);
1749ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
175ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string err_str;
17606460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      Maybe<ResourcePathData> path_data = ExtractResourcePathData(full_path, &err_str);
177ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!path_data) {
17860d9c2fd55ef8e64ab3f39980b78389cd1317ee5Adam Lesinski        context->GetDiagnostics()->Error(DiagMessage(full_path) << err_str);
1791ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
180cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1811ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
182ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_path_data->push_back(std::move(path_data.value()));
18359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
184cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
185cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
186cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
18759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
188ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileTable(IAaptContext* context, const CompileOptions& options,
189d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                         const ResourcePathData& path_data, IArchiveWriter* writer,
190ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         const std::string& output_path) {
191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceTable table;
192cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
1935b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski    FileInputStream fin(path_data.source.path);
1945b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski    if (fin.HadError()) {
195ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
1965b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski                                       << "failed to open file: " << fin.GetError());
197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
198cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
199cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
200cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Parse the values file from XML.
2015b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski    xml::XmlPullParser xml_parser(&fin);
202cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
203ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceParserOptions parser_options;
204ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    parser_options.error_on_positional_arguments = !options.legacy_mode;
205cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
2065b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski    // If the filename includes donottranslate, then the default translatable is false.
207d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos;
208cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
209d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
210ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                              parser_options);
211ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!res_parser.Parse(&xml_parser)) {
212cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
213cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
214cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
215cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
216cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (options.pseudolocalize) {
217cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Generate pseudo-localized strings (en-XA and ar-XB).
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // These are created as weak symbols, and are only generated from default
219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // configuration
220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // strings and plurals.
221ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    PseudolocaleGenerator pseudolocale_generator;
222ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pseudolocale_generator.Consume(context, &table)) {
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
225cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Ensure we have the compilation package at least.
228ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  table.CreatePackage(context->GetCompilationPackage());
229cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Assign an ID to any package that has resources.
231cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (auto& pkg : table.packages) {
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!pkg->id) {
2335b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski      // If no package ID was set while parsing (public identifiers), auto assign an ID.
234ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      pkg->id = context->GetPackageId();
235cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create the file/zip entry.
239ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
24006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open");
241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
242cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
243cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
245ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
246cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
24806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    // ZeroCopyOutputStream interface.
249ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
251ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
252ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pb_table->SerializeToZeroCopyStream(&copying_adaptor)) {
25306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
254cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
257cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
258ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
25906460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish entry");
260cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
261cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
262cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
26359e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
2641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
265d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, const ResourceFile& file,
266d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                         const BigBuffer& buffer, IArchiveWriter* writer,
26759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                         IDiagnostics* diag) {
268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
269ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
270ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to open file");
271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
272cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
273cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
274cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
275ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
27806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    // ZeroCopyOutputStream interface.
279ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
280ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
283ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1);
284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
285b791721cd1a6154e5582d824f5d20b2c8b8d5ac5Adam Lesinski    std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
286ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteCompiledFile(compiled_file.get());
287ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteData(&buffer);
288cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
289ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (output_stream.HadError()) {
290ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag->Error(DiagMessage(output_path) << "failed to write data");
291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
292cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
294cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
295ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
296ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to finish writing data");
297cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
30059e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
3011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
302d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const ResourceFile& file,
303d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                       const android::FileMap& map, IArchiveWriter* writer,
30459e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                       IDiagnostics* diag) {
305cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
306ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
307ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to open file");
308cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
309cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
310cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
311cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
312ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
315ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    // ZeroCopyOutputStream interface.
316ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
317ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
320ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1);
321cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
322b791721cd1a6154e5582d824f5d20b2c8b8d5ac5Adam Lesinski    std::unique_ptr<pb::internal::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
323ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteCompiledFile(compiled_file.get());
324ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteData(map.getDataPtr(), map.getDataLength());
325cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
326ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (output_stream.HadError()) {
327ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag->Error(DiagMessage(output_path) << "failed to write data");
328cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
329cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
330cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
331cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
332ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
333ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to finish writing data");
334cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
335cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
336cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
3371ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
339d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& output_path,
340d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  xml::XmlResource* xmlres, CompiledFileOutputStream* out) {
341cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(1024);
342ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  XmlFlattenerOptions xml_flattener_options;
343ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xml_flattener_options.keep_raw_values = true;
344ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  XmlFlattener flattener(&buffer, xml_flattener_options);
345ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!flattener.Consume(context, xmlres)) {
346cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
347cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
348cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
349b791721cd1a6154e5582d824f5d20b2c8b8d5ac5Adam Lesinski  std::unique_ptr<pb::internal::CompiledFile> pb_compiled_file =
350b791721cd1a6154e5582d824f5d20b2c8b8d5ac5Adam Lesinski      SerializeCompiledFileToPb(xmlres->file);
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out->WriteCompiledFile(pb_compiled_file.get());
352cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  out->WriteData(&buffer);
353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (out->HadError()) {
35506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write data");
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
357cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
358cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
3595eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski}
3605eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski
3615b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinskistatic bool IsValidFile(IAaptContext* context, const std::string& input_path) {
362776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  const file::FileType file_type = file::GetFileType(input_path);
363776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  if (file_type != file::FileType::kRegular && file_type != file::FileType::kSymlink) {
364776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    if (file_type == file::FileType::kDirectory) {
365776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path)
366776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                       << "resource file cannot be a directory");
367cc73e990e5381adfa605ccacad431231d9269893Adam Lesinski    } else if (file_type == file::FileType::kNonexistant) {
368cc73e990e5381adfa605ccacad431231d9269893Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path) << "file not found");
369776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    } else {
370776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path)
371776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                       << "not a valid resource file");
372776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    }
373776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    return false;
374776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  }
375776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  return true;
376776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski}
377776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski
378ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileXml(IAaptContext* context, const CompileOptions& options,
379d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const ResourcePathData& path_data, IArchiveWriter* writer,
380d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const std::string& output_path) {
381ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
38206460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling XML");
383cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
384cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
385ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<xml::XmlResource> xmlres;
386cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
3875b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski    FileInputStream fin(path_data.source.path);
3885b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski    if (fin.HadError()) {
389ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
3905b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski                                       << "failed to open file: " << fin.GetError());
391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
392cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
394ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
395cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
396cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
397ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!xmlres) {
398cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
399cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
401d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  xmlres->file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
402ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xmlres->file.config = path_data.config;
403ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xmlres->file.source = path_data.source;
404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Collect IDs that are defined here.
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  XmlIdCollector collector;
407ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!collector.Consume(context, xmlres.get())) {
408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
411cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Look for and process any <aapt:attr> tags and create sub-documents.
412ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  InlineXmlFormatParser inline_xml_format_parser;
413ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!inline_xml_format_parser.Consume(context, xmlres.get())) {
414cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
417cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
418ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
419d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open file");
420cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
421cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
422cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
4235b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
424cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
4255b54ca2d72c410aa34363b0f3bb0fe1666954aeaAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
426ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
427ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
429ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
430ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        inline_xml_format_parser.GetExtractedInlineXmlDocuments();
431cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
432cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
433ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1 + inline_documents.size());
434cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
435d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    if (!FlattenXmlToOutStream(context, output_path, xmlres.get(), &output_stream)) {
436cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
437cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
438cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
439ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (auto& inline_xml_doc : inline_documents) {
440d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(), &output_stream)) {
4411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
442cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
4431ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
444cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
4451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
446ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
447d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish writing data");
448cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
449cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
450cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
4511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
4521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
453ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompilePng(IAaptContext* context, const CompileOptions& options,
454d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const ResourcePathData& path_data, IArchiveWriter* writer,
455d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const std::string& output_path) {
456ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
457d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling PNG");
458cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
459cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
460cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(4096);
461ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceFile res_file;
462d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
463ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.config = path_data.config;
464ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.source = path_data.source;
465cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
466cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
467cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::string content;
4682354b568379fe31ba4e774f7a92d4c685a60a2abAdam Lesinski    if (!android::base::ReadFileToString(path_data.source.path, &content,
4692354b568379fe31ba4e774f7a92d4c685a60a2abAdam Lesinski                                         true /*follow_symlinks*/)) {
470d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
47160d9c2fd55ef8e64ab3f39980b78389cd1317ee5Adam Lesinski                                       << "failed to open file: "
472d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
473cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
474cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
475cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
476ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    BigBuffer crunched_png_buffer(4096);
47706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    io::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
478cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
479cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Ensure that we only keep the chunks we care about if we end up
480cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // using the original PNG instead of the crunched one.
481ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    PngChunkFilter png_chunk_filter(content);
482cc73e990e5381adfa605ccacad431231d9269893Adam Lesinski    std::unique_ptr<Image> image = ReadPng(context, path_data.source, &png_chunk_filter);
483cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!image) {
484cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
485cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
486cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
487ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::unique_ptr<NinePatch> nine_patch;
488ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (path_data.extension == "9.png") {
489cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::string err;
49006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      nine_patch = NinePatch::Create(image->rows.get(), image->width, image->height, &err);
491ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!nine_patch) {
492ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        context->GetDiagnostics()->Error(DiagMessage() << err);
4931ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
494cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
495cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
496cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Remove the 1px border around the NinePatch.
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Basically the row array is shifted up by 1, and the length is treated
498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // as height - 2.
499cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // For each row, shift the array to the left by 1, and treat the length as
500cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // width - 2.
501cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      image->width -= 2;
502cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      image->height -= 2;
50306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
504cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (int32_t h = 0; h < image->height; h++) {
505cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
506cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
507cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
508ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (context->IsVerbose()) {
509d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "9-patch: "
510d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                                                      << *nine_patch);
511cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
512cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
513cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
514cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Write the crunched PNG.
51506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    if (!WritePng(context, image.get(), nine_patch.get(), &crunched_png_buffer_out, {})) {
516cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
517cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
518cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
519ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (nine_patch != nullptr ||
520ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        crunched_png_buffer_out.ByteCount() <= png_chunk_filter.ByteCount()) {
521cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // No matter what, we must use the re-encoded PNG, even if it is larger.
522cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // 9-patch images must be re-encoded since their borders are stripped.
523ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      buffer.AppendBuffer(std::move(crunched_png_buffer));
524cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // The re-encoded PNG is larger than the original, and there is
526cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // no mandatory transformation. Use the original.
527ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (context->IsVerbose()) {
52806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski        context->GetDiagnostics()->Note(DiagMessage(path_data.source)
52906460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                        << "original PNG is smaller than crunched PNG"
53006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                        << ", using original");
531cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
532cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
53306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      png_chunk_filter.Rewind();
534ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      BigBuffer filtered_png_buffer(4096);
53506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      io::BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
53606460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      io::Copy(&filtered_png_buffer_out, &png_chunk_filter);
537ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      buffer.AppendBuffer(std::move(filtered_png_buffer));
5381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
5391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
540ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (context->IsVerbose()) {
54106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
54206460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      // This will help catch exotic cases where the new code may generate larger PNGs.
543ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream legacy_stream(content);
544ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      BigBuffer legacy_buffer(4096);
545ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Png png(context->GetDiagnostics());
546ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
5471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
548cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
549cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
550ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Note(DiagMessage(path_data.source)
551ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                      << "legacy=" << legacy_buffer.size()
552cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                      << " new=" << buffer.size());
5531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
554cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
5551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
556ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!WriteHeaderAndBufferToWriter(output_path, res_file, buffer, writer,
557ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                    context->GetDiagnostics())) {
558cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
559cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
561cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
562cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
563ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileFile(IAaptContext* context, const CompileOptions& options,
564d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                        const ResourcePathData& path_data, IArchiveWriter* writer,
565ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                        const std::string& output_path) {
566ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
567d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling file");
568cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
569cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
570cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(256);
571ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceFile res_file;
572d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
573ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.config = path_data.config;
574ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.source = path_data.source;
575ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
576ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string error_str;
577ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str);
578cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!f) {
579776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(path_data.source) << "failed to mmap file: "
580776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                     << error_str);
581cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
582cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
583cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
584ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!WriteHeaderAndMmapToWriter(output_path, res_file, f.value(), writer,
585ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                  context->GetDiagnostics())) {
586cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
587cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
588cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
5891ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
5901ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5911ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskiclass CompileContext : public IAaptContext {
592cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
593820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
594820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  }
595820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington
596b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  PackageType GetPackageType() override {
597b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    // Every compilation unit starts as an app and then gets linked as potentially something else.
598b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    return PackageType::kApp;
599b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  }
600b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski
601d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  void SetVerbose(bool val) {
602d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    verbose_ = val;
603d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
604355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
605d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  bool IsVerbose() override {
606d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return verbose_;
607d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
608355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
609d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  IDiagnostics* GetDiagnostics() override {
610820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington    return diagnostics_;
611d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
6121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
613ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  NameMangler* GetNameMangler() override {
614cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    abort();
615cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return nullptr;
616cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
6171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
618ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const std::string& GetCompilationPackage() override {
619cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    static std::string empty;
620cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return empty;
621cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
6221ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
623d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  uint8_t GetPackageId() override {
624d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0x0;
625d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
6261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
627ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  SymbolTable* GetExternalSymbols() override {
628cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    abort();
629cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return nullptr;
630cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
631fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
632d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  int GetMinSdkVersion() override {
633d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0;
634d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
63564587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski
636cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
637820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  IDiagnostics* diagnostics_;
638ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool verbose_ = false;
6391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
6401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
6411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/**
642cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * Entry point for compilation phase. Parses arguments and dispatches to the
643cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * correct steps.
6441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
645820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warringtonint Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
646820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  CompileContext context(diagnostics);
647cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  CompileOptions options;
648cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
649cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool verbose = false;
650cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Flags flags =
651cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      Flags()
652ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .RequiredFlag("-o", "Output path", &options.output_path)
653d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
654ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .OptionalSwitch("--pseudo-localize",
655cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          "Generate resources for pseudo-locales "
656cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          "(en-XA and ar-XB)",
657cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          &options.pseudolocalize)
65828e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski          .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
659d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
660d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                          &options.legacy_mode)
661ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .OptionalSwitch("-v", "Enables verbose logging", &verbose);
662ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
664cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
665cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
666ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  context.SetVerbose(verbose);
667cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
668ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<IArchiveWriter> archive_writer;
669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
670ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::vector<ResourcePathData> input_data;
671ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (options.res_dir) {
672ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!flags.GetArgs().empty()) {
673cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Can't have both files and a resource directory.
674d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
675ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      flags.Usage("aapt2 compile", &std::cerr);
676cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 1;
677cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
678cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
679ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!LoadInputFilesFromDir(&context, options, &input_data)) {
680cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 1;
681cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
682cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
683d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path);
684cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
685cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
686ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    input_data.reserve(flags.GetArgs().size());
687cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
688cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Collect data from the path for each input file.
689ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const std::string& arg : flags.GetArgs()) {
690ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string error_str;
691d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
692ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        input_data.push_back(std::move(path_data.value()));
693cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
694d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context.GetDiagnostics()->Error(DiagMessage() << error_str << " (" << arg << ")");
6951ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return 1;
696cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
6971ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
6981ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
699d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path);
700cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
701355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
702ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!archive_writer) {
703dfaecafbe9fb7c5800191edfe3aa5d5938705204Adam Lesinski    return 1;
704cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
7051ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
706cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
707ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (ResourcePathData& path_data : input_data) {
708cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (options.verbose) {
709d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
710cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
7111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
712776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    if (!IsValidFile(&context, path_data.source.path)) {
713776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      error = true;
714776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      continue;
715776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    }
716776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski
717ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (path_data.resource_dir == "values") {
718cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Overwrite the extension.
719ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      path_data.extension = "arsc";
720a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
721ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const std::string output_filename = BuildIntermediateFilename(path_data);
722d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (!CompileTable(&context, options, path_data, archive_writer.get(), output_filename)) {
723cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
724cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
725a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
726a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    } else {
727ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const std::string output_filename = BuildIntermediateFilename(path_data);
728d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
729cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (*type != ResourceType::kRaw) {
730ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          if (path_data.extension == "xml") {
731d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompileXml(&context, options, path_data, archive_writer.get(), output_filename)) {
732cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
733a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            }
73428e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski          } else if (!options.no_png_crunch &&
73528e6c0bac2f22e63bc044fc44a82ec5282d2709cAdam Lesinski                     (path_data.extension == "png" || path_data.extension == "9.png")) {
736d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompilePng(&context, options, path_data, archive_writer.get(), output_filename)) {
737cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
7381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
739cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          } else {
740d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
741cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
7421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
743cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
744cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
745d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
746cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            error = true;
747cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
7481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
749cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
750d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context.GetDiagnostics()->Error(DiagMessage() << "invalid file path '" << path_data.source
751d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                                      << "'");
752cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
753cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
754cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
755cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
756cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
757cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
758cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
759cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
760cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return 0;
7611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
7621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
763cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
764