Compile.cpp revision 820d72adc0eccbfe4ac4238cdc89b7680ea03d9e
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;
117ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool legacy_mode = false;
118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool verbose = false;
1191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
1201ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
121ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic std::string BuildIntermediateFilename(const ResourcePathData& data) {
122cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::stringstream name;
123ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  name << data.resource_dir;
124ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!data.config_str.empty()) {
125ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    name << "-" << data.config_str;
126cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  name << "_" << data.name;
128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!data.extension.empty()) {
129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    name << "." << data.extension;
130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  name << ".flat";
132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return name.str();
133a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
134a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
135ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool IsHidden(const StringPiece& filename) {
136ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return util::StartsWith(filename, ".");
137a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski}
138a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
139a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski/**
140a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski * Walks the res directory structure, looking for resource files.
141a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski */
142d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool LoadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
143d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  std::vector<ResourcePathData>* out_path_data) {
144ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const std::string& root_dir = options.res_dir.value();
14506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski  std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!d) {
14706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage()
14806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                     << android::base::SystemErrorCodeToString(errno));
149cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
150cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
151a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
152cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  while (struct dirent* entry = readdir(d.get())) {
153ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (IsHidden(entry->d_name)) {
154cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
155a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    }
1569f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
157ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::string prefix_path = root_dir;
158ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    file::AppendPath(&prefix_path, entry->d_name);
1599f22204c3a9ddac4f92573c9ab098e6cf3ed1cb4Adam Lesinski
160ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (file::GetFileType(prefix_path) != file::FileType::kDirectory) {
161cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
1621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
1631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
16406460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir);
165ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!subdir) {
16606460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage()
16706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
168cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
169393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski    }
170393b5f0d6130d3848dd82075986a5cf40c09ce44Adam Lesinski
171ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    while (struct dirent* leaf_entry = readdir(subdir.get())) {
172ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (IsHidden(leaf_entry->d_name)) {
173cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
174cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
17583f2255f69729e0e97539e96e5e6161843e85823Adam Lesinski
176ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string full_path = prefix_path;
177ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      file::AppendPath(&full_path, leaf_entry->d_name);
1789ba47d813075fcb05c5e1532c137c93b394631cbAdam Lesinski
179ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string err_str;
18006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      Maybe<ResourcePathData> path_data = ExtractResourcePathData(full_path, &err_str);
181ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!path_data) {
182ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        context->GetDiagnostics()->Error(DiagMessage() << err_str);
1831ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
184cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1851ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
186ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_path_data->push_back(std::move(path_data.value()));
18759e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski    }
188cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
189cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
190cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
19159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
192ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileTable(IAaptContext* context, const CompileOptions& options,
193d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                         const ResourcePathData& path_data, IArchiveWriter* writer,
194ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         const std::string& output_path) {
195cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceTable table;
196cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
197ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::ifstream fin(path_data.source.path, std::ifstream::binary);
198cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!fin) {
199ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
20006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
202cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
204cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Parse the values file from XML.
205ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    xml::XmlPullParser xml_parser(fin);
206cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
207ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceParserOptions parser_options;
208ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    parser_options.error_on_positional_arguments = !options.legacy_mode;
209cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
210cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // If the filename includes donottranslate, then the default translatable is
211cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // false.
212d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos;
213cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
214d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
215ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                              parser_options);
216ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!res_parser.Parse(&xml_parser)) {
217cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    fin.close();
221cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (options.pseudolocalize) {
224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Generate pseudo-localized strings (en-XA and ar-XB).
225cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // These are created as weak symbols, and are only generated from default
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // configuration
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // strings and plurals.
228ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    PseudolocaleGenerator pseudolocale_generator;
229ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pseudolocale_generator.Consume(context, &table)) {
230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
231cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
233cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Ensure we have the compilation package at least.
235ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  table.CreatePackage(context->GetCompilationPackage());
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Assign an ID to any package that has resources.
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (auto& pkg : table.packages) {
239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!pkg->id) {
240cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // If no package ID was set while parsing (public identifiers), auto
241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // assign an ID.
242ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      pkg->id = context->GetPackageId();
243cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
246cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create the file/zip entry.
247ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
24806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open");
249cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
251cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
252cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
253ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
254cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
25606460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    // ZeroCopyOutputStream interface.
257ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
258cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
259ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
260ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!pb_table->SerializeToZeroCopyStream(&copying_adaptor)) {
26106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write");
262cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
264cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
265cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
266ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
26706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish entry");
268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
27159e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
2721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
273d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool WriteHeaderAndBufferToWriter(const StringPiece& output_path, const ResourceFile& file,
274d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                         const BigBuffer& buffer, IArchiveWriter* writer,
27559e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                         IDiagnostics* diag) {
276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
277ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
278ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to open file");
279cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
283ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
28606460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    // ZeroCopyOutputStream interface.
287ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
288ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
290cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
291ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1);
292cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
29306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
294ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteCompiledFile(compiled_file.get());
295ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteData(&buffer);
296cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
297ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (output_stream.HadError()) {
298ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag->Error(DiagMessage(output_path) << "failed to write data");
299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
302cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
303ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
304ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to finish writing data");
305cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
306cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
307cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
30859e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski}
3091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
310d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool WriteHeaderAndMmapToWriter(const StringPiece& output_path, const ResourceFile& file,
311d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                       const android::FileMap& map, IArchiveWriter* writer,
31259e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski                                       IDiagnostics* diag) {
313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
314ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
315ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to open file");
316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
317cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
320ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
321cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
322cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
323ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    // ZeroCopyOutputStream interface.
324ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
325ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
326cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
327cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
328ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1);
329cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
330d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    std::unique_ptr<pb::CompiledFile> compiled_file = SerializeCompiledFileToPb(file);
331ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteCompiledFile(compiled_file.get());
332ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteData(map.getDataPtr(), map.getDataLength());
333cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
334ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (output_stream.HadError()) {
335ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      diag->Error(DiagMessage(output_path) << "failed to write data");
336cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
337cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
340ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
341ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    diag->Error(DiagMessage(output_path) << "failed to finish writing data");
342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
344cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
3451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
347d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistatic bool FlattenXmlToOutStream(IAaptContext* context, const StringPiece& output_path,
348d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                  xml::XmlResource* xmlres, CompiledFileOutputStream* out) {
349cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(1024);
350ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  XmlFlattenerOptions xml_flattener_options;
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xml_flattener_options.keep_raw_values = true;
352ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  XmlFlattener flattener(&buffer, xml_flattener_options);
353ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!flattener.Consume(context, xmlres)) {
354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
355cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
35706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski  std::unique_ptr<pb::CompiledFile> pb_compiled_file = SerializeCompiledFileToPb(xmlres->file);
358ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out->WriteCompiledFile(pb_compiled_file.get());
359cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  out->WriteData(&buffer);
360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
361cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (out->HadError()) {
36206460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to write data");
363cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
364cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
3665eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski}
3675eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski
368776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinskistatic bool IsValidFile(IAaptContext* context, const StringPiece& input_path) {
369776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  const file::FileType file_type = file::GetFileType(input_path);
370776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  if (file_type != file::FileType::kRegular && file_type != file::FileType::kSymlink) {
371776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    if (file_type == file::FileType::kDirectory) {
372776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path)
373776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                       << "resource file cannot be a directory");
374776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    } else {
375776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(input_path)
376776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                       << "not a valid resource file");
377776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    }
378776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    return false;
379776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  }
380776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski  return true;
381776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski}
382776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski
383ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileXml(IAaptContext* context, const CompileOptions& options,
384d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const ResourcePathData& path_data, IArchiveWriter* writer,
385d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const std::string& output_path) {
386ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
38706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling XML");
388cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
389cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
390ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<xml::XmlResource> xmlres;
391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
392ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::ifstream fin(path_data.source.path, std::ifstream::binary);
393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!fin) {
394ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
39506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
396cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
397cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
398cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
399ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
401cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    fin.close();
402cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
403cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
404ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!xmlres) {
405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
407cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
408d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  xmlres->file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
409ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xmlres->file.config = path_data.config;
410ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  xmlres->file.source = path_data.source;
411cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
412cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Collect IDs that are defined here.
413cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  XmlIdCollector collector;
414ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!collector.Consume(context, xmlres.get())) {
415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
417cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Look for and process any <aapt:attr> tags and create sub-documents.
419ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  InlineXmlFormatParser inline_xml_format_parser;
420ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!inline_xml_format_parser.Consume(context, xmlres.get())) {
421cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
422cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
423cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
424cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start the entry so we can write the header.
425ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->StartEntry(output_path, 0)) {
426d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to open file");
427cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
429cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Make sure CopyingOutputStreamAdaptor is deleted before we call
431ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // writer->FinishEntry().
432cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
433cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Wrap our IArchiveWriter with an adaptor that implements the
434cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // ZeroCopyOutputStream
435cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // interface.
436ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CopyingOutputStreamAdaptor copying_adaptor(writer);
437ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CompiledFileOutputStream output_stream(&copying_adaptor);
438cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
439ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
440ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        inline_xml_format_parser.GetExtractedInlineXmlDocuments();
441cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
442cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Number of CompiledFiles.
443ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    output_stream.WriteLittleEndian32(1 + inline_documents.size());
444cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
445d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    if (!FlattenXmlToOutStream(context, output_path, xmlres.get(), &output_stream)) {
446cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
448cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
449ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (auto& inline_xml_doc : inline_documents) {
450d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(), &output_stream)) {
4511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
452cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
4531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
454cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
4551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
456ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!writer->FinishEntry()) {
457d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish writing data");
458cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
459cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
460cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
4611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
4621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
463ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompilePng(IAaptContext* context, const CompileOptions& options,
464d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const ResourcePathData& path_data, IArchiveWriter* writer,
465d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                       const std::string& output_path) {
466ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
467d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling PNG");
468cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
469cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
470cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(4096);
471ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceFile res_file;
472d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
473ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.config = path_data.config;
474ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.source = path_data.source;
475cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
476cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  {
477cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::string content;
478ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!android::base::ReadFileToString(path_data.source.path, &content)) {
479d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context->GetDiagnostics()->Error(DiagMessage(path_data.source)
480d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                       << android::base::SystemErrorCodeToString(errno));
481cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
482cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
483cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
484ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    BigBuffer crunched_png_buffer(4096);
48506460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    io::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
486cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
487cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Ensure that we only keep the chunks we care about if we end up
488cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // using the original PNG instead of the crunched one.
489ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    PngChunkFilter png_chunk_filter(content);
490ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::unique_ptr<Image> image = ReadPng(context, &png_chunk_filter);
491cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!image) {
492cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
493cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
494cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
495ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::unique_ptr<NinePatch> nine_patch;
496ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (path_data.extension == "9.png") {
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::string err;
49806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      nine_patch = NinePatch::Create(image->rows.get(), image->width, image->height, &err);
499ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!nine_patch) {
500ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        context->GetDiagnostics()->Error(DiagMessage() << err);
5011ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
502cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
503cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
504cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Remove the 1px border around the NinePatch.
505cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Basically the row array is shifted up by 1, and the length is treated
506cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // as height - 2.
507cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // For each row, shift the array to the left by 1, and treat the length as
508cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // width - 2.
509cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      image->width -= 2;
510cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      image->height -= 2;
51106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
512cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (int32_t h = 0; h < image->height; h++) {
513cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
514cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
515cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
516ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (context->IsVerbose()) {
517d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "9-patch: "
518d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                                                      << *nine_patch);
519cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
520cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
521cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
522cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Write the crunched PNG.
52306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski    if (!WritePng(context, image.get(), nine_patch.get(), &crunched_png_buffer_out, {})) {
524cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
526cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
527ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (nine_patch != nullptr ||
528ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        crunched_png_buffer_out.ByteCount() <= png_chunk_filter.ByteCount()) {
529cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // No matter what, we must use the re-encoded PNG, even if it is larger.
530cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // 9-patch images must be re-encoded since their borders are stripped.
531ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      buffer.AppendBuffer(std::move(crunched_png_buffer));
532cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
533cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // The re-encoded PNG is larger than the original, and there is
534cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // no mandatory transformation. Use the original.
535ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (context->IsVerbose()) {
53606460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski        context->GetDiagnostics()->Note(DiagMessage(path_data.source)
53706460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                        << "original PNG is smaller than crunched PNG"
53806460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski                                        << ", using original");
539cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
540cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
54106460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      png_chunk_filter.Rewind();
542ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      BigBuffer filtered_png_buffer(4096);
54306460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      io::BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
54406460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      io::Copy(&filtered_png_buffer_out, &png_chunk_filter);
545ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      buffer.AppendBuffer(std::move(filtered_png_buffer));
5461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
5471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
548ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (context->IsVerbose()) {
54906460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
55006460ef0d7072114ea3280e1650f77f55e7223f4Adam Lesinski      // This will help catch exotic cases where the new code may generate larger PNGs.
551ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream legacy_stream(content);
552ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      BigBuffer legacy_buffer(4096);
553ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      Png png(context->GetDiagnostics());
554ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
5551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return false;
556cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
557cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
558ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      context->GetDiagnostics()->Note(DiagMessage(path_data.source)
559ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                      << "legacy=" << legacy_buffer.size()
560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                      << " new=" << buffer.size());
5611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
562cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
5631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
564ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!WriteHeaderAndBufferToWriter(output_path, res_file, buffer, writer,
565ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                    context->GetDiagnostics())) {
566cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
567cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
568cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
569cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
570cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
571ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool CompileFile(IAaptContext* context, const CompileOptions& options,
572d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                        const ResourcePathData& path_data, IArchiveWriter* writer,
573ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                        const std::string& output_path) {
574ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (context->IsVerbose()) {
575d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    context->GetDiagnostics()->Note(DiagMessage(path_data.source) << "compiling file");
576cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
577cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
578cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  BigBuffer buffer(256);
579ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceFile res_file;
580d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
581ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.config = path_data.config;
582ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  res_file.source = path_data.source;
583ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
584ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string error_str;
585ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str);
586cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!f) {
587776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    context->GetDiagnostics()->Error(DiagMessage(path_data.source) << "failed to mmap file: "
588776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski                                     << error_str);
589cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
590cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
591cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
592ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!WriteHeaderAndMmapToWriter(output_path, res_file, f.value(), writer,
593ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                  context->GetDiagnostics())) {
594cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
595cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
596cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
5971ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
5981ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5991ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskiclass CompileContext : public IAaptContext {
600cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
601820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
602820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  }
603820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington
604b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  PackageType GetPackageType() override {
605b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    // Every compilation unit starts as an app and then gets linked as potentially something else.
606b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski    return PackageType::kApp;
607b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski  }
608b522f04bc2a581e2877bef4a44ac00b827f879edAdam Lesinski
609d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  void SetVerbose(bool val) {
610d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    verbose_ = val;
611d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
612355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
613d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  bool IsVerbose() override {
614d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return verbose_;
615d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
616355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
617d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  IDiagnostics* GetDiagnostics() override {
618820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington    return diagnostics_;
619d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
6201ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
621ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  NameMangler* GetNameMangler() override {
622cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    abort();
623cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return nullptr;
624cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
6251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
626ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const std::string& GetCompilationPackage() override {
627cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    static std::string empty;
628cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return empty;
629cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
6301ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
631d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  uint8_t GetPackageId() override {
632d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0x0;
633d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
6341ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
635ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  SymbolTable* GetExternalSymbols() override {
636cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    abort();
637cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return nullptr;
638cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
639fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
640d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  int GetMinSdkVersion() override {
641d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return 0;
642d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
64364587af8179affd38ee26543b748f2d63b7f67bbAdam Lesinski
644cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
645820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  IDiagnostics* diagnostics_;
646ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool verbose_ = false;
6471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski};
6481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
6491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/**
650cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * Entry point for compilation phase. Parses arguments and dispatches to the
651cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * correct steps.
6521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
653820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warringtonint Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
654820d72adc0eccbfe4ac4238cdc89b7680ea03d9eChris Warrington  CompileContext context(diagnostics);
655cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  CompileOptions options;
656cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
657cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool verbose = false;
658cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Flags flags =
659cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      Flags()
660ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .RequiredFlag("-o", "Output path", &options.output_path)
661d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
662ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .OptionalSwitch("--pseudo-localize",
663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          "Generate resources for pseudo-locales "
664cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          "(en-XA and ar-XB)",
665cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                          &options.pseudolocalize)
666d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
667d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                          &options.legacy_mode)
668ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          .OptionalSwitch("-v", "Enables verbose logging", &verbose);
669ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
670cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
671cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
672cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
673ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  context.SetVerbose(verbose);
674cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
675ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::unique_ptr<IArchiveWriter> archive_writer;
676cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
677ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::vector<ResourcePathData> input_data;
678ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (options.res_dir) {
679ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!flags.GetArgs().empty()) {
680cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Can't have both files and a resource directory.
681d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
682ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      flags.Usage("aapt2 compile", &std::cerr);
683cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 1;
684cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
685cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
686ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!LoadInputFilesFromDir(&context, options, &input_data)) {
687cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 1;
688cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
689cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
690d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path);
691cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
692cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
693ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    input_data.reserve(flags.GetArgs().size());
694cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
695cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Collect data from the path for each input file.
696ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const std::string& arg : flags.GetArgs()) {
697ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::string error_str;
698d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
699ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        input_data.push_back(std::move(path_data.value()));
700cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
701d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context.GetDiagnostics()->Error(DiagMessage() << error_str << " (" << arg << ")");
7021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        return 1;
703cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
7041ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
7051ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
706d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path);
707cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
708355f285ffd000f6cfe76680eb22d010546d124bbAdam Lesinski
709ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!archive_writer) {
710dfaecafbe9fb7c5800191edfe3aa5d5938705204Adam Lesinski    return 1;
711cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
7121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
713cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
714ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (ResourcePathData& path_data : input_data) {
715cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (options.verbose) {
716d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
717cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
7181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
719776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    if (!IsValidFile(&context, path_data.source.path)) {
720776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      error = true;
721776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski      continue;
722776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski    }
723776aa959c7122f23f3c58443ea1b673127ed01f2Adam Lesinski
724ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (path_data.resource_dir == "values") {
725cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Overwrite the extension.
726ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      path_data.extension = "arsc";
727a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
728ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const std::string output_filename = BuildIntermediateFilename(path_data);
729d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (!CompileTable(&context, options, path_data, archive_writer.get(), output_filename)) {
730cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
731cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
732a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski
733a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski    } else {
734ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const std::string output_filename = BuildIntermediateFilename(path_data);
735d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
736cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (*type != ResourceType::kRaw) {
737ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          if (path_data.extension == "xml") {
738d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompileXml(&context, options, path_data, archive_writer.get(), output_filename)) {
739cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
740a40e972fdaa68fc486ff90a319195515819068b2Adam Lesinski            }
741d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          } else if (path_data.extension == "png" || path_data.extension == "9.png") {
742d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompilePng(&context, options, path_data, archive_writer.get(), output_filename)) {
743cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
7441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
745cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          } else {
746d0f492db038c6210c1138865d816bfb134376538Adam Lesinski            if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
747cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              error = true;
7481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
749cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
750cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
751d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          if (!CompileFile(&context, options, path_data, archive_writer.get(), output_filename)) {
752cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            error = true;
753cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
7541ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
755cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      } else {
756d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        context.GetDiagnostics()->Error(DiagMessage() << "invalid file path '" << path_data.source
757d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                                      << "'");
758cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        error = true;
759cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
760cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
761cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
762cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
763cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (error) {
764cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 1;
765cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
766cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return 0;
7671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
7681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
769cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
770