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