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