1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include "Compile.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <dirent.h>
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker #include <string>
22*d57664e9SAndroid Build Coastguard Worker
23*d57664e9SAndroid Build Coastguard Worker #include "ResourceParser.h"
24*d57664e9SAndroid Build Coastguard Worker #include "ResourceTable.h"
25*d57664e9SAndroid Build Coastguard Worker #include "android-base/errors.h"
26*d57664e9SAndroid Build Coastguard Worker #include "android-base/file.h"
27*d57664e9SAndroid Build Coastguard Worker #include "android-base/utf8.h"
28*d57664e9SAndroid Build Coastguard Worker #include "androidfw/BigBufferStream.h"
29*d57664e9SAndroid Build Coastguard Worker #include "androidfw/ConfigDescription.h"
30*d57664e9SAndroid Build Coastguard Worker #include "androidfw/FileStream.h"
31*d57664e9SAndroid Build Coastguard Worker #include "androidfw/IDiagnostics.h"
32*d57664e9SAndroid Build Coastguard Worker #include "androidfw/Image.h"
33*d57664e9SAndroid Build Coastguard Worker #include "androidfw/Png.h"
34*d57664e9SAndroid Build Coastguard Worker #include "androidfw/StringPiece.h"
35*d57664e9SAndroid Build Coastguard Worker #include "cmd/Util.h"
36*d57664e9SAndroid Build Coastguard Worker #include "compile/IdAssigner.h"
37*d57664e9SAndroid Build Coastguard Worker #include "compile/InlineXmlFormatParser.h"
38*d57664e9SAndroid Build Coastguard Worker #include "compile/PseudolocaleGenerator.h"
39*d57664e9SAndroid Build Coastguard Worker #include "compile/XmlIdCollector.h"
40*d57664e9SAndroid Build Coastguard Worker #include "format/Archive.h"
41*d57664e9SAndroid Build Coastguard Worker #include "format/Container.h"
42*d57664e9SAndroid Build Coastguard Worker #include "format/proto/ProtoSerialize.h"
43*d57664e9SAndroid Build Coastguard Worker #include "google/protobuf/io/coded_stream.h"
44*d57664e9SAndroid Build Coastguard Worker #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
45*d57664e9SAndroid Build Coastguard Worker #include "io/FileSystem.h"
46*d57664e9SAndroid Build Coastguard Worker #include "io/StringStream.h"
47*d57664e9SAndroid Build Coastguard Worker #include "io/Util.h"
48*d57664e9SAndroid Build Coastguard Worker #include "io/ZipArchive.h"
49*d57664e9SAndroid Build Coastguard Worker #include "process/ProductFilter.h"
50*d57664e9SAndroid Build Coastguard Worker #include "trace/TraceBuffer.h"
51*d57664e9SAndroid Build Coastguard Worker #include "util/Files.h"
52*d57664e9SAndroid Build Coastguard Worker #include "util/Util.h"
53*d57664e9SAndroid Build Coastguard Worker #include "xml/XmlDom.h"
54*d57664e9SAndroid Build Coastguard Worker #include "xml/XmlPullParser.h"
55*d57664e9SAndroid Build Coastguard Worker
56*d57664e9SAndroid Build Coastguard Worker using ::aapt::text::Printer;
57*d57664e9SAndroid Build Coastguard Worker using ::android::ConfigDescription;
58*d57664e9SAndroid Build Coastguard Worker using ::android::FileInputStream;
59*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece;
60*d57664e9SAndroid Build Coastguard Worker using ::android::base::SystemErrorCodeToString;
61*d57664e9SAndroid Build Coastguard Worker using ::google::protobuf::io::CopyingOutputStreamAdaptor;
62*d57664e9SAndroid Build Coastguard Worker
63*d57664e9SAndroid Build Coastguard Worker namespace aapt {
64*d57664e9SAndroid Build Coastguard Worker
65*d57664e9SAndroid Build Coastguard Worker struct ResourcePathData {
66*d57664e9SAndroid Build Coastguard Worker android::Source source;
67*d57664e9SAndroid Build Coastguard Worker std::string resource_dir;
68*d57664e9SAndroid Build Coastguard Worker std::string name;
69*d57664e9SAndroid Build Coastguard Worker std::string extension;
70*d57664e9SAndroid Build Coastguard Worker std::string flag_name;
71*d57664e9SAndroid Build Coastguard Worker
72*d57664e9SAndroid Build Coastguard Worker // Original config str. We keep this because when we parse the config, we may add on
73*d57664e9SAndroid Build Coastguard Worker // version qualifiers. We want to preserve the original input so the output is easily
74*d57664e9SAndroid Build Coastguard Worker // computed before hand.
75*d57664e9SAndroid Build Coastguard Worker std::string config_str;
76*d57664e9SAndroid Build Coastguard Worker ConfigDescription config;
77*d57664e9SAndroid Build Coastguard Worker };
78*d57664e9SAndroid Build Coastguard Worker
79*d57664e9SAndroid Build Coastguard Worker // Resource file paths are expected to look like: [--/res/]type[-config]/name
ExtractResourcePathData(const std::string & path,const char dir_sep,std::string * out_error,const CompileOptions & options)80*d57664e9SAndroid Build Coastguard Worker static std::optional<ResourcePathData> ExtractResourcePathData(const std::string& path,
81*d57664e9SAndroid Build Coastguard Worker const char dir_sep,
82*d57664e9SAndroid Build Coastguard Worker std::string* out_error,
83*d57664e9SAndroid Build Coastguard Worker const CompileOptions& options) {
84*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> parts = util::Split(path, dir_sep);
85*d57664e9SAndroid Build Coastguard Worker
86*d57664e9SAndroid Build Coastguard Worker std::string flag_name;
87*d57664e9SAndroid Build Coastguard Worker // Check for a flag
88*d57664e9SAndroid Build Coastguard Worker for (auto iter = parts.begin(); iter != parts.end();) {
89*d57664e9SAndroid Build Coastguard Worker if (iter->starts_with("flag(") && iter->ends_with(")")) {
90*d57664e9SAndroid Build Coastguard Worker if (!flag_name.empty()) {
91*d57664e9SAndroid Build Coastguard Worker if (out_error) *out_error = "resource path cannot contain more than one flag directory";
92*d57664e9SAndroid Build Coastguard Worker return {};
93*d57664e9SAndroid Build Coastguard Worker }
94*d57664e9SAndroid Build Coastguard Worker flag_name = iter->substr(5, iter->size() - 6);
95*d57664e9SAndroid Build Coastguard Worker iter = parts.erase(iter);
96*d57664e9SAndroid Build Coastguard Worker } else {
97*d57664e9SAndroid Build Coastguard Worker ++iter;
98*d57664e9SAndroid Build Coastguard Worker }
99*d57664e9SAndroid Build Coastguard Worker }
100*d57664e9SAndroid Build Coastguard Worker
101*d57664e9SAndroid Build Coastguard Worker if (parts.size() < 2) {
102*d57664e9SAndroid Build Coastguard Worker if (out_error) *out_error = "bad resource path";
103*d57664e9SAndroid Build Coastguard Worker return {};
104*d57664e9SAndroid Build Coastguard Worker }
105*d57664e9SAndroid Build Coastguard Worker
106*d57664e9SAndroid Build Coastguard Worker std::string& dir = parts[parts.size() - 2];
107*d57664e9SAndroid Build Coastguard Worker StringPiece dir_str = dir;
108*d57664e9SAndroid Build Coastguard Worker
109*d57664e9SAndroid Build Coastguard Worker StringPiece config_str;
110*d57664e9SAndroid Build Coastguard Worker ConfigDescription config;
111*d57664e9SAndroid Build Coastguard Worker size_t dash_pos = dir.find('-');
112*d57664e9SAndroid Build Coastguard Worker if (dash_pos != std::string::npos) {
113*d57664e9SAndroid Build Coastguard Worker config_str = dir_str.substr(dash_pos + 1, dir.size() - (dash_pos + 1));
114*d57664e9SAndroid Build Coastguard Worker if (!ConfigDescription::Parse(config_str, &config)) {
115*d57664e9SAndroid Build Coastguard Worker if (out_error) {
116*d57664e9SAndroid Build Coastguard Worker std::stringstream err_str;
117*d57664e9SAndroid Build Coastguard Worker err_str << "invalid configuration '" << config_str << "'";
118*d57664e9SAndroid Build Coastguard Worker *out_error = err_str.str();
119*d57664e9SAndroid Build Coastguard Worker }
120*d57664e9SAndroid Build Coastguard Worker return {};
121*d57664e9SAndroid Build Coastguard Worker }
122*d57664e9SAndroid Build Coastguard Worker dir_str = dir_str.substr(0, dash_pos);
123*d57664e9SAndroid Build Coastguard Worker }
124*d57664e9SAndroid Build Coastguard Worker
125*d57664e9SAndroid Build Coastguard Worker std::string& filename = parts[parts.size() - 1];
126*d57664e9SAndroid Build Coastguard Worker StringPiece name = filename;
127*d57664e9SAndroid Build Coastguard Worker StringPiece extension;
128*d57664e9SAndroid Build Coastguard Worker
129*d57664e9SAndroid Build Coastguard Worker const std::string kNinePng = ".9.png";
130*d57664e9SAndroid Build Coastguard Worker if (filename.size() > kNinePng.size()
131*d57664e9SAndroid Build Coastguard Worker && std::equal(kNinePng.rbegin(), kNinePng.rend(), filename.rbegin())) {
132*d57664e9SAndroid Build Coastguard Worker // Split on .9.png if this extension is present at the end of the file path
133*d57664e9SAndroid Build Coastguard Worker name = name.substr(0, filename.size() - kNinePng.size());
134*d57664e9SAndroid Build Coastguard Worker extension = "9.png";
135*d57664e9SAndroid Build Coastguard Worker } else {
136*d57664e9SAndroid Build Coastguard Worker // Split on the last period occurrence
137*d57664e9SAndroid Build Coastguard Worker size_t dot_pos = filename.rfind('.');
138*d57664e9SAndroid Build Coastguard Worker if (dot_pos != std::string::npos) {
139*d57664e9SAndroid Build Coastguard Worker extension = name.substr(dot_pos + 1, filename.size() - (dot_pos + 1));
140*d57664e9SAndroid Build Coastguard Worker name = name.substr(0, dot_pos);
141*d57664e9SAndroid Build Coastguard Worker }
142*d57664e9SAndroid Build Coastguard Worker }
143*d57664e9SAndroid Build Coastguard Worker
144*d57664e9SAndroid Build Coastguard Worker const android::Source res_path =
145*d57664e9SAndroid Build Coastguard Worker options.source_path ? StringPiece(options.source_path.value()) : StringPiece(path);
146*d57664e9SAndroid Build Coastguard Worker
147*d57664e9SAndroid Build Coastguard Worker return ResourcePathData{res_path,
148*d57664e9SAndroid Build Coastguard Worker std::string(dir_str),
149*d57664e9SAndroid Build Coastguard Worker std::string(name),
150*d57664e9SAndroid Build Coastguard Worker std::string(extension),
151*d57664e9SAndroid Build Coastguard Worker std::move(flag_name),
152*d57664e9SAndroid Build Coastguard Worker std::string(config_str),
153*d57664e9SAndroid Build Coastguard Worker config};
154*d57664e9SAndroid Build Coastguard Worker }
155*d57664e9SAndroid Build Coastguard Worker
BuildIntermediateContainerFilename(const ResourcePathData & data)156*d57664e9SAndroid Build Coastguard Worker static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) {
157*d57664e9SAndroid Build Coastguard Worker std::stringstream name;
158*d57664e9SAndroid Build Coastguard Worker name << data.resource_dir;
159*d57664e9SAndroid Build Coastguard Worker if (!data.config_str.empty()) {
160*d57664e9SAndroid Build Coastguard Worker name << "-" << data.config_str;
161*d57664e9SAndroid Build Coastguard Worker }
162*d57664e9SAndroid Build Coastguard Worker name << "_" << data.name;
163*d57664e9SAndroid Build Coastguard Worker if (!data.flag_name.empty()) {
164*d57664e9SAndroid Build Coastguard Worker name << ".(" << data.flag_name << ")";
165*d57664e9SAndroid Build Coastguard Worker }
166*d57664e9SAndroid Build Coastguard Worker if (!data.extension.empty()) {
167*d57664e9SAndroid Build Coastguard Worker name << "." << data.extension;
168*d57664e9SAndroid Build Coastguard Worker }
169*d57664e9SAndroid Build Coastguard Worker name << ".flat";
170*d57664e9SAndroid Build Coastguard Worker return name.str();
171*d57664e9SAndroid Build Coastguard Worker }
172*d57664e9SAndroid Build Coastguard Worker
CompileTable(IAaptContext * context,const CompileOptions & options,const ResourcePathData & path_data,io::IFile * file,IArchiveWriter * writer,const std::string & output_path)173*d57664e9SAndroid Build Coastguard Worker static bool CompileTable(IAaptContext* context, const CompileOptions& options,
174*d57664e9SAndroid Build Coastguard Worker const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
175*d57664e9SAndroid Build Coastguard Worker const std::string& output_path) {
176*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
177*d57664e9SAndroid Build Coastguard Worker // Filenames starting with "donottranslate" are not localizable
178*d57664e9SAndroid Build Coastguard Worker bool translatable_file = path_data.name.find("donottranslate") != 0;
179*d57664e9SAndroid Build Coastguard Worker ResourceTable table;
180*d57664e9SAndroid Build Coastguard Worker {
181*d57664e9SAndroid Build Coastguard Worker auto fin = file->OpenInputStream();
182*d57664e9SAndroid Build Coastguard Worker if (fin->HadError()) {
183*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source)
184*d57664e9SAndroid Build Coastguard Worker << "failed to open file: " << fin->GetError());
185*d57664e9SAndroid Build Coastguard Worker return false;
186*d57664e9SAndroid Build Coastguard Worker }
187*d57664e9SAndroid Build Coastguard Worker // Parse the values file from XML.
188*d57664e9SAndroid Build Coastguard Worker xml::XmlPullParser xml_parser(fin.get());
189*d57664e9SAndroid Build Coastguard Worker
190*d57664e9SAndroid Build Coastguard Worker ResourceParserOptions parser_options;
191*d57664e9SAndroid Build Coastguard Worker parser_options.error_on_positional_arguments = !options.legacy_mode;
192*d57664e9SAndroid Build Coastguard Worker parser_options.preserve_visibility_of_styleables = options.preserve_visibility_of_styleables;
193*d57664e9SAndroid Build Coastguard Worker parser_options.translatable = translatable_file;
194*d57664e9SAndroid Build Coastguard Worker parser_options.feature_flag_values = options.feature_flag_values;
195*d57664e9SAndroid Build Coastguard Worker
196*d57664e9SAndroid Build Coastguard Worker // If visibility was forced, we need to use it when creating a new resource and also error if
197*d57664e9SAndroid Build Coastguard Worker // we try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
198*d57664e9SAndroid Build Coastguard Worker parser_options.visibility = options.visibility;
199*d57664e9SAndroid Build Coastguard Worker parser_options.flag = ParseFlag(path_data.flag_name);
200*d57664e9SAndroid Build Coastguard Worker
201*d57664e9SAndroid Build Coastguard Worker if (parser_options.flag) {
202*d57664e9SAndroid Build Coastguard Worker std::string error;
203*d57664e9SAndroid Build Coastguard Worker auto flag_status = GetFlagStatus(parser_options.flag, options.feature_flag_values, &error);
204*d57664e9SAndroid Build Coastguard Worker if (flag_status) {
205*d57664e9SAndroid Build Coastguard Worker parser_options.flag_status = std::move(flag_status.value());
206*d57664e9SAndroid Build Coastguard Worker } else {
207*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source) << error);
208*d57664e9SAndroid Build Coastguard Worker return false;
209*d57664e9SAndroid Build Coastguard Worker }
210*d57664e9SAndroid Build Coastguard Worker }
211*d57664e9SAndroid Build Coastguard Worker
212*d57664e9SAndroid Build Coastguard Worker ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
213*d57664e9SAndroid Build Coastguard Worker parser_options);
214*d57664e9SAndroid Build Coastguard Worker if (!res_parser.Parse(&xml_parser)) {
215*d57664e9SAndroid Build Coastguard Worker return false;
216*d57664e9SAndroid Build Coastguard Worker }
217*d57664e9SAndroid Build Coastguard Worker
218*d57664e9SAndroid Build Coastguard Worker if (options.product_.has_value()) {
219*d57664e9SAndroid Build Coastguard Worker if (!ProductFilter({*options.product_}, /* remove_default_config_values = */ true)
220*d57664e9SAndroid Build Coastguard Worker .Consume(context, &table)) {
221*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source)
222*d57664e9SAndroid Build Coastguard Worker << "failed to filter product");
223*d57664e9SAndroid Build Coastguard Worker return false;
224*d57664e9SAndroid Build Coastguard Worker }
225*d57664e9SAndroid Build Coastguard Worker }
226*d57664e9SAndroid Build Coastguard Worker }
227*d57664e9SAndroid Build Coastguard Worker
228*d57664e9SAndroid Build Coastguard Worker if (options.pseudolocalize && translatable_file) {
229*d57664e9SAndroid Build Coastguard Worker // Generate pseudo-localized strings (en-XA and ar-XB).
230*d57664e9SAndroid Build Coastguard Worker // These are created as weak symbols, and are only generated from default
231*d57664e9SAndroid Build Coastguard Worker // configuration
232*d57664e9SAndroid Build Coastguard Worker // strings and plurals.
233*d57664e9SAndroid Build Coastguard Worker std::string grammatical_gender_values;
234*d57664e9SAndroid Build Coastguard Worker std::string grammatical_gender_ratio;
235*d57664e9SAndroid Build Coastguard Worker if (options.pseudo_localize_gender_values) {
236*d57664e9SAndroid Build Coastguard Worker grammatical_gender_values = options.pseudo_localize_gender_values.value();
237*d57664e9SAndroid Build Coastguard Worker } else {
238*d57664e9SAndroid Build Coastguard Worker grammatical_gender_values = "f,m,n";
239*d57664e9SAndroid Build Coastguard Worker }
240*d57664e9SAndroid Build Coastguard Worker if (options.pseudo_localize_gender_ratio) {
241*d57664e9SAndroid Build Coastguard Worker grammatical_gender_ratio = options.pseudo_localize_gender_ratio.value();
242*d57664e9SAndroid Build Coastguard Worker } else {
243*d57664e9SAndroid Build Coastguard Worker grammatical_gender_ratio = "1.0";
244*d57664e9SAndroid Build Coastguard Worker }
245*d57664e9SAndroid Build Coastguard Worker PseudolocaleGenerator pseudolocale_generator(grammatical_gender_values,
246*d57664e9SAndroid Build Coastguard Worker grammatical_gender_ratio);
247*d57664e9SAndroid Build Coastguard Worker if (!pseudolocale_generator.Consume(context, &table)) {
248*d57664e9SAndroid Build Coastguard Worker return false;
249*d57664e9SAndroid Build Coastguard Worker }
250*d57664e9SAndroid Build Coastguard Worker }
251*d57664e9SAndroid Build Coastguard Worker
252*d57664e9SAndroid Build Coastguard Worker // Create the file/zip entry.
253*d57664e9SAndroid Build Coastguard Worker if (!writer->StartEntry(output_path, 0)) {
254*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(output_path) << "failed to open");
255*d57664e9SAndroid Build Coastguard Worker return false;
256*d57664e9SAndroid Build Coastguard Worker }
257*d57664e9SAndroid Build Coastguard Worker
258*d57664e9SAndroid Build Coastguard Worker // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
259*d57664e9SAndroid Build Coastguard Worker {
260*d57664e9SAndroid Build Coastguard Worker // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
261*d57664e9SAndroid Build Coastguard Worker CopyingOutputStreamAdaptor copying_adaptor(writer);
262*d57664e9SAndroid Build Coastguard Worker ContainerWriter container_writer(©ing_adaptor, 1u);
263*d57664e9SAndroid Build Coastguard Worker
264*d57664e9SAndroid Build Coastguard Worker pb::ResourceTable pb_table;
265*d57664e9SAndroid Build Coastguard Worker SerializeTableToPb(table, &pb_table, context->GetDiagnostics());
266*d57664e9SAndroid Build Coastguard Worker if (!container_writer.AddResTableEntry(pb_table)) {
267*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(output_path) << "failed to write");
268*d57664e9SAndroid Build Coastguard Worker return false;
269*d57664e9SAndroid Build Coastguard Worker }
270*d57664e9SAndroid Build Coastguard Worker }
271*d57664e9SAndroid Build Coastguard Worker
272*d57664e9SAndroid Build Coastguard Worker if (!writer->FinishEntry()) {
273*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(output_path) << "failed to finish entry");
274*d57664e9SAndroid Build Coastguard Worker return false;
275*d57664e9SAndroid Build Coastguard Worker }
276*d57664e9SAndroid Build Coastguard Worker
277*d57664e9SAndroid Build Coastguard Worker if (options.generate_text_symbols_path) {
278*d57664e9SAndroid Build Coastguard Worker android::FileOutputStream fout_text(options.generate_text_symbols_path.value());
279*d57664e9SAndroid Build Coastguard Worker
280*d57664e9SAndroid Build Coastguard Worker if (fout_text.HadError()) {
281*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage()
282*d57664e9SAndroid Build Coastguard Worker << "failed writing to'"
283*d57664e9SAndroid Build Coastguard Worker << options.generate_text_symbols_path.value()
284*d57664e9SAndroid Build Coastguard Worker << "': " << fout_text.GetError());
285*d57664e9SAndroid Build Coastguard Worker return false;
286*d57664e9SAndroid Build Coastguard Worker }
287*d57664e9SAndroid Build Coastguard Worker
288*d57664e9SAndroid Build Coastguard Worker Printer r_txt_printer(&fout_text);
289*d57664e9SAndroid Build Coastguard Worker for (const auto& package : table.packages) {
290*d57664e9SAndroid Build Coastguard Worker // Only print resources defined locally, e.g. don't write android attributes.
291*d57664e9SAndroid Build Coastguard Worker if (package->name.empty()) {
292*d57664e9SAndroid Build Coastguard Worker for (const auto& type : package->types) {
293*d57664e9SAndroid Build Coastguard Worker for (const auto& entry : type->entries) {
294*d57664e9SAndroid Build Coastguard Worker // Check access modifiers.
295*d57664e9SAndroid Build Coastguard Worker switch (entry->visibility.level) {
296*d57664e9SAndroid Build Coastguard Worker case Visibility::Level::kUndefined :
297*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("default ");
298*d57664e9SAndroid Build Coastguard Worker break;
299*d57664e9SAndroid Build Coastguard Worker case Visibility::Level::kPublic :
300*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("public ");
301*d57664e9SAndroid Build Coastguard Worker break;
302*d57664e9SAndroid Build Coastguard Worker case Visibility::Level::kPrivate :
303*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("private ");
304*d57664e9SAndroid Build Coastguard Worker }
305*d57664e9SAndroid Build Coastguard Worker
306*d57664e9SAndroid Build Coastguard Worker if (type->named_type.type != ResourceType::kStyleable) {
307*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("int ");
308*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print(type->named_type.to_string());
309*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print(" ");
310*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Println(entry->name);
311*d57664e9SAndroid Build Coastguard Worker } else {
312*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("int[] styleable ");
313*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Println(entry->name);
314*d57664e9SAndroid Build Coastguard Worker
315*d57664e9SAndroid Build Coastguard Worker if (!entry->values.empty()) {
316*d57664e9SAndroid Build Coastguard Worker auto styleable =
317*d57664e9SAndroid Build Coastguard Worker static_cast<const Styleable*>(entry->values.front()->value.get());
318*d57664e9SAndroid Build Coastguard Worker for (const auto& attr : styleable->entries) {
319*d57664e9SAndroid Build Coastguard Worker // The visibility of the children under the styleable does not matter as they are
320*d57664e9SAndroid Build Coastguard Worker // nested under their parent and use its visibility.
321*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("default int styleable ");
322*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print(entry->name);
323*d57664e9SAndroid Build Coastguard Worker // If the package name is present, also include it in the mangled name (e.g.
324*d57664e9SAndroid Build Coastguard Worker // "android")
325*d57664e9SAndroid Build Coastguard Worker if (!attr.name.value().package.empty()) {
326*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("_");
327*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print(MakePackageSafeName(attr.name.value().package));
328*d57664e9SAndroid Build Coastguard Worker }
329*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("_");
330*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Println(attr.name.value().entry);
331*d57664e9SAndroid Build Coastguard Worker }
332*d57664e9SAndroid Build Coastguard Worker }
333*d57664e9SAndroid Build Coastguard Worker }
334*d57664e9SAndroid Build Coastguard Worker }
335*d57664e9SAndroid Build Coastguard Worker }
336*d57664e9SAndroid Build Coastguard Worker }
337*d57664e9SAndroid Build Coastguard Worker }
338*d57664e9SAndroid Build Coastguard Worker }
339*d57664e9SAndroid Build Coastguard Worker
340*d57664e9SAndroid Build Coastguard Worker return true;
341*d57664e9SAndroid Build Coastguard Worker }
342*d57664e9SAndroid Build Coastguard Worker
WriteHeaderAndDataToWriter(StringPiece output_path,const ResourceFile & file,android::KnownSizeInputStream * in,IArchiveWriter * writer,android::IDiagnostics * diag)343*d57664e9SAndroid Build Coastguard Worker static bool WriteHeaderAndDataToWriter(StringPiece output_path, const ResourceFile& file,
344*d57664e9SAndroid Build Coastguard Worker android::KnownSizeInputStream* in, IArchiveWriter* writer,
345*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diag) {
346*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
347*d57664e9SAndroid Build Coastguard Worker // Start the entry so we can write the header.
348*d57664e9SAndroid Build Coastguard Worker if (!writer->StartEntry(output_path, 0)) {
349*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(output_path) << "failed to open file");
350*d57664e9SAndroid Build Coastguard Worker return false;
351*d57664e9SAndroid Build Coastguard Worker }
352*d57664e9SAndroid Build Coastguard Worker
353*d57664e9SAndroid Build Coastguard Worker // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
354*d57664e9SAndroid Build Coastguard Worker {
355*d57664e9SAndroid Build Coastguard Worker // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
356*d57664e9SAndroid Build Coastguard Worker CopyingOutputStreamAdaptor copying_adaptor(writer);
357*d57664e9SAndroid Build Coastguard Worker ContainerWriter container_writer(©ing_adaptor, 1u);
358*d57664e9SAndroid Build Coastguard Worker
359*d57664e9SAndroid Build Coastguard Worker pb::internal::CompiledFile pb_compiled_file;
360*d57664e9SAndroid Build Coastguard Worker SerializeCompiledFileToPb(file, &pb_compiled_file);
361*d57664e9SAndroid Build Coastguard Worker
362*d57664e9SAndroid Build Coastguard Worker if (!container_writer.AddResFileEntry(pb_compiled_file, in)) {
363*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(output_path) << "failed to write entry data");
364*d57664e9SAndroid Build Coastguard Worker return false;
365*d57664e9SAndroid Build Coastguard Worker }
366*d57664e9SAndroid Build Coastguard Worker }
367*d57664e9SAndroid Build Coastguard Worker
368*d57664e9SAndroid Build Coastguard Worker if (!writer->FinishEntry()) {
369*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(output_path) << "failed to finish writing data");
370*d57664e9SAndroid Build Coastguard Worker return false;
371*d57664e9SAndroid Build Coastguard Worker }
372*d57664e9SAndroid Build Coastguard Worker return true;
373*d57664e9SAndroid Build Coastguard Worker }
374*d57664e9SAndroid Build Coastguard Worker
FlattenXmlToOutStream(StringPiece output_path,const xml::XmlResource & xmlres,ContainerWriter * container_writer,android::IDiagnostics * diag)375*d57664e9SAndroid Build Coastguard Worker static bool FlattenXmlToOutStream(StringPiece output_path, const xml::XmlResource& xmlres,
376*d57664e9SAndroid Build Coastguard Worker ContainerWriter* container_writer, android::IDiagnostics* diag) {
377*d57664e9SAndroid Build Coastguard Worker pb::internal::CompiledFile pb_compiled_file;
378*d57664e9SAndroid Build Coastguard Worker SerializeCompiledFileToPb(xmlres.file, &pb_compiled_file);
379*d57664e9SAndroid Build Coastguard Worker
380*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_xml_node;
381*d57664e9SAndroid Build Coastguard Worker SerializeXmlToPb(*xmlres.root, &pb_xml_node);
382*d57664e9SAndroid Build Coastguard Worker
383*d57664e9SAndroid Build Coastguard Worker std::string serialized_xml = pb_xml_node.SerializeAsString();
384*d57664e9SAndroid Build Coastguard Worker io::StringInputStream serialized_in(serialized_xml);
385*d57664e9SAndroid Build Coastguard Worker
386*d57664e9SAndroid Build Coastguard Worker if (!container_writer->AddResFileEntry(pb_compiled_file, &serialized_in)) {
387*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(output_path) << "failed to write entry data");
388*d57664e9SAndroid Build Coastguard Worker return false;
389*d57664e9SAndroid Build Coastguard Worker }
390*d57664e9SAndroid Build Coastguard Worker return true;
391*d57664e9SAndroid Build Coastguard Worker }
392*d57664e9SAndroid Build Coastguard Worker
IsValidFile(IAaptContext * context,const std::string & input_path)393*d57664e9SAndroid Build Coastguard Worker static bool IsValidFile(IAaptContext* context, const std::string& input_path) {
394*d57664e9SAndroid Build Coastguard Worker const file::FileType file_type = file::GetFileType(input_path);
395*d57664e9SAndroid Build Coastguard Worker if (file_type != file::FileType::kRegular && file_type != file::FileType::kSymlink) {
396*d57664e9SAndroid Build Coastguard Worker if (file_type == file::FileType::kDirectory) {
397*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(input_path)
398*d57664e9SAndroid Build Coastguard Worker << "resource file cannot be a directory");
399*d57664e9SAndroid Build Coastguard Worker } else if (file_type == file::FileType::kNonExistant) {
400*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(input_path) << "file not found");
401*d57664e9SAndroid Build Coastguard Worker } else {
402*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(input_path)
403*d57664e9SAndroid Build Coastguard Worker << "not a valid resource file");
404*d57664e9SAndroid Build Coastguard Worker }
405*d57664e9SAndroid Build Coastguard Worker return false;
406*d57664e9SAndroid Build Coastguard Worker }
407*d57664e9SAndroid Build Coastguard Worker return true;
408*d57664e9SAndroid Build Coastguard Worker }
409*d57664e9SAndroid Build Coastguard Worker
CompileXml(IAaptContext * context,const CompileOptions & options,const ResourcePathData & path_data,io::IFile * file,IArchiveWriter * writer,const std::string & output_path)410*d57664e9SAndroid Build Coastguard Worker static bool CompileXml(IAaptContext* context, const CompileOptions& options,
411*d57664e9SAndroid Build Coastguard Worker const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
412*d57664e9SAndroid Build Coastguard Worker const std::string& output_path) {
413*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
414*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
415*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage(path_data.source) << "compiling XML");
416*d57664e9SAndroid Build Coastguard Worker }
417*d57664e9SAndroid Build Coastguard Worker
418*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> xmlres;
419*d57664e9SAndroid Build Coastguard Worker {
420*d57664e9SAndroid Build Coastguard Worker auto fin = file->OpenInputStream();
421*d57664e9SAndroid Build Coastguard Worker if (fin->HadError()) {
422*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source)
423*d57664e9SAndroid Build Coastguard Worker << "failed to open file: " << fin->GetError());
424*d57664e9SAndroid Build Coastguard Worker return false;
425*d57664e9SAndroid Build Coastguard Worker }
426*d57664e9SAndroid Build Coastguard Worker
427*d57664e9SAndroid Build Coastguard Worker xmlres = xml::Inflate(fin.get(), context->GetDiagnostics(), path_data.source);
428*d57664e9SAndroid Build Coastguard Worker if (!xmlres) {
429*d57664e9SAndroid Build Coastguard Worker return false;
430*d57664e9SAndroid Build Coastguard Worker }
431*d57664e9SAndroid Build Coastguard Worker }
432*d57664e9SAndroid Build Coastguard Worker
433*d57664e9SAndroid Build Coastguard Worker xmlres->file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
434*d57664e9SAndroid Build Coastguard Worker xmlres->file.config = path_data.config;
435*d57664e9SAndroid Build Coastguard Worker xmlres->file.source = path_data.source;
436*d57664e9SAndroid Build Coastguard Worker xmlres->file.type = ResourceFile::Type::kProtoXml;
437*d57664e9SAndroid Build Coastguard Worker xmlres->file.flag = ParseFlag(path_data.flag_name);
438*d57664e9SAndroid Build Coastguard Worker
439*d57664e9SAndroid Build Coastguard Worker if (xmlres->file.flag) {
440*d57664e9SAndroid Build Coastguard Worker std::string error;
441*d57664e9SAndroid Build Coastguard Worker auto flag_status = GetFlagStatus(xmlres->file.flag, options.feature_flag_values, &error);
442*d57664e9SAndroid Build Coastguard Worker if (flag_status) {
443*d57664e9SAndroid Build Coastguard Worker xmlres->file.flag_status = flag_status.value();
444*d57664e9SAndroid Build Coastguard Worker } else {
445*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source) << error);
446*d57664e9SAndroid Build Coastguard Worker return false;
447*d57664e9SAndroid Build Coastguard Worker }
448*d57664e9SAndroid Build Coastguard Worker }
449*d57664e9SAndroid Build Coastguard Worker
450*d57664e9SAndroid Build Coastguard Worker // Collect IDs that are defined here.
451*d57664e9SAndroid Build Coastguard Worker XmlIdCollector collector;
452*d57664e9SAndroid Build Coastguard Worker if (!collector.Consume(context, xmlres.get())) {
453*d57664e9SAndroid Build Coastguard Worker return false;
454*d57664e9SAndroid Build Coastguard Worker }
455*d57664e9SAndroid Build Coastguard Worker
456*d57664e9SAndroid Build Coastguard Worker // Look for and process any <aapt:attr> tags and create sub-documents.
457*d57664e9SAndroid Build Coastguard Worker InlineXmlFormatParser inline_xml_format_parser;
458*d57664e9SAndroid Build Coastguard Worker if (!inline_xml_format_parser.Consume(context, xmlres.get())) {
459*d57664e9SAndroid Build Coastguard Worker return false;
460*d57664e9SAndroid Build Coastguard Worker }
461*d57664e9SAndroid Build Coastguard Worker
462*d57664e9SAndroid Build Coastguard Worker // Start the entry so we can write the header.
463*d57664e9SAndroid Build Coastguard Worker if (!writer->StartEntry(output_path, 0)) {
464*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(output_path) << "failed to open file");
465*d57664e9SAndroid Build Coastguard Worker return false;
466*d57664e9SAndroid Build Coastguard Worker }
467*d57664e9SAndroid Build Coastguard Worker
468*d57664e9SAndroid Build Coastguard Worker std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
469*d57664e9SAndroid Build Coastguard Worker inline_xml_format_parser.GetExtractedInlineXmlDocuments();
470*d57664e9SAndroid Build Coastguard Worker
471*d57664e9SAndroid Build Coastguard Worker // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->FinishEntry().
472*d57664e9SAndroid Build Coastguard Worker {
473*d57664e9SAndroid Build Coastguard Worker // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
474*d57664e9SAndroid Build Coastguard Worker CopyingOutputStreamAdaptor copying_adaptor(writer);
475*d57664e9SAndroid Build Coastguard Worker ContainerWriter container_writer(©ing_adaptor, 1u + inline_documents.size());
476*d57664e9SAndroid Build Coastguard Worker
477*d57664e9SAndroid Build Coastguard Worker if (!FlattenXmlToOutStream(output_path, *xmlres, &container_writer,
478*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics())) {
479*d57664e9SAndroid Build Coastguard Worker return false;
480*d57664e9SAndroid Build Coastguard Worker }
481*d57664e9SAndroid Build Coastguard Worker
482*d57664e9SAndroid Build Coastguard Worker for (const std::unique_ptr<xml::XmlResource>& inline_xml_doc : inline_documents) {
483*d57664e9SAndroid Build Coastguard Worker if (!FlattenXmlToOutStream(output_path, *inline_xml_doc, &container_writer,
484*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics())) {
485*d57664e9SAndroid Build Coastguard Worker return false;
486*d57664e9SAndroid Build Coastguard Worker }
487*d57664e9SAndroid Build Coastguard Worker }
488*d57664e9SAndroid Build Coastguard Worker }
489*d57664e9SAndroid Build Coastguard Worker
490*d57664e9SAndroid Build Coastguard Worker if (!writer->FinishEntry()) {
491*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(output_path)
492*d57664e9SAndroid Build Coastguard Worker << "failed to finish writing data");
493*d57664e9SAndroid Build Coastguard Worker return false;
494*d57664e9SAndroid Build Coastguard Worker }
495*d57664e9SAndroid Build Coastguard Worker
496*d57664e9SAndroid Build Coastguard Worker if (options.generate_text_symbols_path) {
497*d57664e9SAndroid Build Coastguard Worker android::FileOutputStream fout_text(options.generate_text_symbols_path.value());
498*d57664e9SAndroid Build Coastguard Worker
499*d57664e9SAndroid Build Coastguard Worker if (fout_text.HadError()) {
500*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage()
501*d57664e9SAndroid Build Coastguard Worker << "failed writing to'"
502*d57664e9SAndroid Build Coastguard Worker << options.generate_text_symbols_path.value()
503*d57664e9SAndroid Build Coastguard Worker << "': " << fout_text.GetError());
504*d57664e9SAndroid Build Coastguard Worker return false;
505*d57664e9SAndroid Build Coastguard Worker }
506*d57664e9SAndroid Build Coastguard Worker
507*d57664e9SAndroid Build Coastguard Worker Printer r_txt_printer(&fout_text);
508*d57664e9SAndroid Build Coastguard Worker for (const auto& res : xmlres->file.exported_symbols) {
509*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("default int id ");
510*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Println(res.name.entry);
511*d57664e9SAndroid Build Coastguard Worker }
512*d57664e9SAndroid Build Coastguard Worker
513*d57664e9SAndroid Build Coastguard Worker // And print ourselves.
514*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print("default int ");
515*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print(path_data.resource_dir);
516*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Print(" ");
517*d57664e9SAndroid Build Coastguard Worker r_txt_printer.Println(path_data.name);
518*d57664e9SAndroid Build Coastguard Worker }
519*d57664e9SAndroid Build Coastguard Worker
520*d57664e9SAndroid Build Coastguard Worker return true;
521*d57664e9SAndroid Build Coastguard Worker }
522*d57664e9SAndroid Build Coastguard Worker
CompilePng(IAaptContext * context,const CompileOptions & options,const ResourcePathData & path_data,io::IFile * file,IArchiveWriter * writer,const std::string & output_path)523*d57664e9SAndroid Build Coastguard Worker static bool CompilePng(IAaptContext* context, const CompileOptions& options,
524*d57664e9SAndroid Build Coastguard Worker const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
525*d57664e9SAndroid Build Coastguard Worker const std::string& output_path) {
526*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
527*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
528*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage(path_data.source) << "compiling PNG");
529*d57664e9SAndroid Build Coastguard Worker }
530*d57664e9SAndroid Build Coastguard Worker
531*d57664e9SAndroid Build Coastguard Worker android::BigBuffer buffer(4096);
532*d57664e9SAndroid Build Coastguard Worker ResourceFile res_file;
533*d57664e9SAndroid Build Coastguard Worker res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
534*d57664e9SAndroid Build Coastguard Worker res_file.config = path_data.config;
535*d57664e9SAndroid Build Coastguard Worker res_file.source = path_data.source;
536*d57664e9SAndroid Build Coastguard Worker res_file.type = ResourceFile::Type::kPng;
537*d57664e9SAndroid Build Coastguard Worker
538*d57664e9SAndroid Build Coastguard Worker if (!path_data.flag_name.empty()) {
539*d57664e9SAndroid Build Coastguard Worker FeatureFlagAttribute flag;
540*d57664e9SAndroid Build Coastguard Worker auto name = path_data.flag_name;
541*d57664e9SAndroid Build Coastguard Worker if (name.starts_with('!')) {
542*d57664e9SAndroid Build Coastguard Worker flag.negated = true;
543*d57664e9SAndroid Build Coastguard Worker flag.name = name.substr(1);
544*d57664e9SAndroid Build Coastguard Worker } else {
545*d57664e9SAndroid Build Coastguard Worker flag.name = name;
546*d57664e9SAndroid Build Coastguard Worker }
547*d57664e9SAndroid Build Coastguard Worker res_file.flag = flag;
548*d57664e9SAndroid Build Coastguard Worker
549*d57664e9SAndroid Build Coastguard Worker std::string error;
550*d57664e9SAndroid Build Coastguard Worker auto flag_status = GetFlagStatus(flag, options.feature_flag_values, &error);
551*d57664e9SAndroid Build Coastguard Worker if (flag_status) {
552*d57664e9SAndroid Build Coastguard Worker res_file.flag_status = flag_status.value();
553*d57664e9SAndroid Build Coastguard Worker } else {
554*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source) << error);
555*d57664e9SAndroid Build Coastguard Worker return false;
556*d57664e9SAndroid Build Coastguard Worker }
557*d57664e9SAndroid Build Coastguard Worker }
558*d57664e9SAndroid Build Coastguard Worker
559*d57664e9SAndroid Build Coastguard Worker {
560*d57664e9SAndroid Build Coastguard Worker auto data = file->OpenAsData();
561*d57664e9SAndroid Build Coastguard Worker if (!data) {
562*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source)
563*d57664e9SAndroid Build Coastguard Worker << "failed to open file ");
564*d57664e9SAndroid Build Coastguard Worker return false;
565*d57664e9SAndroid Build Coastguard Worker }
566*d57664e9SAndroid Build Coastguard Worker
567*d57664e9SAndroid Build Coastguard Worker android::BigBuffer crunched_png_buffer(4096);
568*d57664e9SAndroid Build Coastguard Worker android::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
569*d57664e9SAndroid Build Coastguard Worker
570*d57664e9SAndroid Build Coastguard Worker // Ensure that we only keep the chunks we care about if we end up
571*d57664e9SAndroid Build Coastguard Worker // using the original PNG instead of the crunched one.
572*d57664e9SAndroid Build Coastguard Worker const StringPiece content(reinterpret_cast<const char*>(data->data()), data->size());
573*d57664e9SAndroid Build Coastguard Worker android::PngChunkFilter png_chunk_filter(content);
574*d57664e9SAndroid Build Coastguard Worker android::SourcePathDiagnostics source_diag(path_data.source, context->GetDiagnostics());
575*d57664e9SAndroid Build Coastguard Worker auto image = android::ReadPng(&png_chunk_filter, &source_diag);
576*d57664e9SAndroid Build Coastguard Worker if (!image) {
577*d57664e9SAndroid Build Coastguard Worker return false;
578*d57664e9SAndroid Build Coastguard Worker }
579*d57664e9SAndroid Build Coastguard Worker
580*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::NinePatch> nine_patch;
581*d57664e9SAndroid Build Coastguard Worker if (path_data.extension == "9.png") {
582*d57664e9SAndroid Build Coastguard Worker std::string err;
583*d57664e9SAndroid Build Coastguard Worker nine_patch = android::NinePatch::Create(image->rows.get(), image->width, image->height, &err);
584*d57664e9SAndroid Build Coastguard Worker if (!nine_patch) {
585*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage() << err);
586*d57664e9SAndroid Build Coastguard Worker return false;
587*d57664e9SAndroid Build Coastguard Worker }
588*d57664e9SAndroid Build Coastguard Worker
589*d57664e9SAndroid Build Coastguard Worker // Remove the 1px border around the NinePatch.
590*d57664e9SAndroid Build Coastguard Worker // Basically the row array is shifted up by 1, and the length is treated
591*d57664e9SAndroid Build Coastguard Worker // as height - 2.
592*d57664e9SAndroid Build Coastguard Worker // For each row, shift the array to the left by 1, and treat the length as
593*d57664e9SAndroid Build Coastguard Worker // width - 2.
594*d57664e9SAndroid Build Coastguard Worker image->width -= 2;
595*d57664e9SAndroid Build Coastguard Worker image->height -= 2;
596*d57664e9SAndroid Build Coastguard Worker memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
597*d57664e9SAndroid Build Coastguard Worker for (int32_t h = 0; h < image->height; h++) {
598*d57664e9SAndroid Build Coastguard Worker memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
599*d57664e9SAndroid Build Coastguard Worker }
600*d57664e9SAndroid Build Coastguard Worker
601*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
602*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage(path_data.source)
603*d57664e9SAndroid Build Coastguard Worker << "9-patch: " << *nine_patch);
604*d57664e9SAndroid Build Coastguard Worker }
605*d57664e9SAndroid Build Coastguard Worker }
606*d57664e9SAndroid Build Coastguard Worker
607*d57664e9SAndroid Build Coastguard Worker // Write the crunched PNG.
608*d57664e9SAndroid Build Coastguard Worker if (!android::WritePng(image.get(), nine_patch.get(), &crunched_png_buffer_out,
609*d57664e9SAndroid Build Coastguard Worker {.compression_level = options.png_compression_level_int}, &source_diag,
610*d57664e9SAndroid Build Coastguard Worker context->IsVerbose())) {
611*d57664e9SAndroid Build Coastguard Worker return false;
612*d57664e9SAndroid Build Coastguard Worker }
613*d57664e9SAndroid Build Coastguard Worker
614*d57664e9SAndroid Build Coastguard Worker if (nine_patch != nullptr ||
615*d57664e9SAndroid Build Coastguard Worker crunched_png_buffer_out.ByteCount() <= png_chunk_filter.ByteCount()) {
616*d57664e9SAndroid Build Coastguard Worker // No matter what, we must use the re-encoded PNG, even if it is larger.
617*d57664e9SAndroid Build Coastguard Worker // 9-patch images must be re-encoded since their borders are stripped.
618*d57664e9SAndroid Build Coastguard Worker buffer.AppendBuffer(std::move(crunched_png_buffer));
619*d57664e9SAndroid Build Coastguard Worker } else {
620*d57664e9SAndroid Build Coastguard Worker // The re-encoded PNG is larger than the original, and there is
621*d57664e9SAndroid Build Coastguard Worker // no mandatory transformation. Use the original.
622*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
623*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage(path_data.source)
624*d57664e9SAndroid Build Coastguard Worker << "original PNG is smaller than crunched PNG"
625*d57664e9SAndroid Build Coastguard Worker << ", using original");
626*d57664e9SAndroid Build Coastguard Worker }
627*d57664e9SAndroid Build Coastguard Worker
628*d57664e9SAndroid Build Coastguard Worker png_chunk_filter.Rewind();
629*d57664e9SAndroid Build Coastguard Worker android::BigBuffer filtered_png_buffer(4096);
630*d57664e9SAndroid Build Coastguard Worker android::BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
631*d57664e9SAndroid Build Coastguard Worker io::Copy(&filtered_png_buffer_out, &png_chunk_filter);
632*d57664e9SAndroid Build Coastguard Worker buffer.AppendBuffer(std::move(filtered_png_buffer));
633*d57664e9SAndroid Build Coastguard Worker }
634*d57664e9SAndroid Build Coastguard Worker
635*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
636*d57664e9SAndroid Build Coastguard Worker // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
637*d57664e9SAndroid Build Coastguard Worker // This will help catch exotic cases where the new code may generate larger PNGs.
638*d57664e9SAndroid Build Coastguard Worker std::stringstream legacy_stream{std::string(content)};
639*d57664e9SAndroid Build Coastguard Worker android::BigBuffer legacy_buffer(4096);
640*d57664e9SAndroid Build Coastguard Worker android::Png png(context->GetDiagnostics());
641*d57664e9SAndroid Build Coastguard Worker if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
642*d57664e9SAndroid Build Coastguard Worker return false;
643*d57664e9SAndroid Build Coastguard Worker }
644*d57664e9SAndroid Build Coastguard Worker
645*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage(path_data.source)
646*d57664e9SAndroid Build Coastguard Worker << "legacy=" << legacy_buffer.size()
647*d57664e9SAndroid Build Coastguard Worker << " new=" << buffer.size());
648*d57664e9SAndroid Build Coastguard Worker }
649*d57664e9SAndroid Build Coastguard Worker }
650*d57664e9SAndroid Build Coastguard Worker
651*d57664e9SAndroid Build Coastguard Worker android::BigBufferInputStream buffer_in(&buffer);
652*d57664e9SAndroid Build Coastguard Worker return WriteHeaderAndDataToWriter(output_path, res_file, &buffer_in, writer,
653*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics());
654*d57664e9SAndroid Build Coastguard Worker }
655*d57664e9SAndroid Build Coastguard Worker
CompileFile(IAaptContext * context,const CompileOptions & options,const ResourcePathData & path_data,io::IFile * file,IArchiveWriter * writer,const std::string & output_path)656*d57664e9SAndroid Build Coastguard Worker static bool CompileFile(IAaptContext* context, const CompileOptions& options,
657*d57664e9SAndroid Build Coastguard Worker const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
658*d57664e9SAndroid Build Coastguard Worker const std::string& output_path) {
659*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
660*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
661*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage(path_data.source) << "compiling file");
662*d57664e9SAndroid Build Coastguard Worker }
663*d57664e9SAndroid Build Coastguard Worker
664*d57664e9SAndroid Build Coastguard Worker ResourceFile res_file;
665*d57664e9SAndroid Build Coastguard Worker res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir), path_data.name);
666*d57664e9SAndroid Build Coastguard Worker res_file.config = path_data.config;
667*d57664e9SAndroid Build Coastguard Worker res_file.source = path_data.source;
668*d57664e9SAndroid Build Coastguard Worker res_file.type = ResourceFile::Type::kUnknown;
669*d57664e9SAndroid Build Coastguard Worker
670*d57664e9SAndroid Build Coastguard Worker auto data = file->OpenAsData();
671*d57664e9SAndroid Build Coastguard Worker if (!data) {
672*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path_data.source)
673*d57664e9SAndroid Build Coastguard Worker << "failed to open file ");
674*d57664e9SAndroid Build Coastguard Worker return false;
675*d57664e9SAndroid Build Coastguard Worker }
676*d57664e9SAndroid Build Coastguard Worker
677*d57664e9SAndroid Build Coastguard Worker return WriteHeaderAndDataToWriter(output_path, res_file, data.get(), writer,
678*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics());
679*d57664e9SAndroid Build Coastguard Worker }
680*d57664e9SAndroid Build Coastguard Worker
681*d57664e9SAndroid Build Coastguard Worker class CompileContext : public IAaptContext {
682*d57664e9SAndroid Build Coastguard Worker public:
CompileContext(android::IDiagnostics * diagnostics)683*d57664e9SAndroid Build Coastguard Worker explicit CompileContext(android::IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
684*d57664e9SAndroid Build Coastguard Worker }
685*d57664e9SAndroid Build Coastguard Worker
GetPackageType()686*d57664e9SAndroid Build Coastguard Worker PackageType GetPackageType() override {
687*d57664e9SAndroid Build Coastguard Worker // Every compilation unit starts as an app and then gets linked as potentially something else.
688*d57664e9SAndroid Build Coastguard Worker return PackageType::kApp;
689*d57664e9SAndroid Build Coastguard Worker }
690*d57664e9SAndroid Build Coastguard Worker
SetVerbose(bool val)691*d57664e9SAndroid Build Coastguard Worker void SetVerbose(bool val) {
692*d57664e9SAndroid Build Coastguard Worker verbose_ = val;
693*d57664e9SAndroid Build Coastguard Worker diagnostics_->SetVerbose(val);
694*d57664e9SAndroid Build Coastguard Worker }
695*d57664e9SAndroid Build Coastguard Worker
IsVerbose()696*d57664e9SAndroid Build Coastguard Worker bool IsVerbose() override {
697*d57664e9SAndroid Build Coastguard Worker return verbose_;
698*d57664e9SAndroid Build Coastguard Worker }
699*d57664e9SAndroid Build Coastguard Worker
GetDiagnostics()700*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* GetDiagnostics() override {
701*d57664e9SAndroid Build Coastguard Worker return diagnostics_;
702*d57664e9SAndroid Build Coastguard Worker }
703*d57664e9SAndroid Build Coastguard Worker
GetNameMangler()704*d57664e9SAndroid Build Coastguard Worker NameMangler* GetNameMangler() override {
705*d57664e9SAndroid Build Coastguard Worker UNIMPLEMENTED(FATAL) << "No name mangling should be needed in compile phase";
706*d57664e9SAndroid Build Coastguard Worker return nullptr;
707*d57664e9SAndroid Build Coastguard Worker }
708*d57664e9SAndroid Build Coastguard Worker
GetCompilationPackage()709*d57664e9SAndroid Build Coastguard Worker const std::string& GetCompilationPackage() override {
710*d57664e9SAndroid Build Coastguard Worker static std::string empty;
711*d57664e9SAndroid Build Coastguard Worker return empty;
712*d57664e9SAndroid Build Coastguard Worker }
713*d57664e9SAndroid Build Coastguard Worker
GetPackageId()714*d57664e9SAndroid Build Coastguard Worker uint8_t GetPackageId() override {
715*d57664e9SAndroid Build Coastguard Worker return 0x0;
716*d57664e9SAndroid Build Coastguard Worker }
717*d57664e9SAndroid Build Coastguard Worker
GetExternalSymbols()718*d57664e9SAndroid Build Coastguard Worker SymbolTable* GetExternalSymbols() override {
719*d57664e9SAndroid Build Coastguard Worker UNIMPLEMENTED(FATAL) << "No symbols should be needed in compile phase";
720*d57664e9SAndroid Build Coastguard Worker return nullptr;
721*d57664e9SAndroid Build Coastguard Worker }
722*d57664e9SAndroid Build Coastguard Worker
GetMinSdkVersion()723*d57664e9SAndroid Build Coastguard Worker int GetMinSdkVersion() override {
724*d57664e9SAndroid Build Coastguard Worker return 0;
725*d57664e9SAndroid Build Coastguard Worker }
726*d57664e9SAndroid Build Coastguard Worker
GetSplitNameDependencies()727*d57664e9SAndroid Build Coastguard Worker const std::set<std::string>& GetSplitNameDependencies() override {
728*d57664e9SAndroid Build Coastguard Worker UNIMPLEMENTED(FATAL) << "No Split Name Dependencies be needed in compile phase";
729*d57664e9SAndroid Build Coastguard Worker static std::set<std::string> empty;
730*d57664e9SAndroid Build Coastguard Worker return empty;
731*d57664e9SAndroid Build Coastguard Worker }
732*d57664e9SAndroid Build Coastguard Worker
733*d57664e9SAndroid Build Coastguard Worker private:
734*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(CompileContext);
735*d57664e9SAndroid Build Coastguard Worker
736*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diagnostics_;
737*d57664e9SAndroid Build Coastguard Worker bool verbose_ = false;
738*d57664e9SAndroid Build Coastguard Worker };
739*d57664e9SAndroid Build Coastguard Worker
Compile(IAaptContext * context,io::IFileCollection * inputs,IArchiveWriter * output_writer,CompileOptions & options)740*d57664e9SAndroid Build Coastguard Worker int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
741*d57664e9SAndroid Build Coastguard Worker CompileOptions& options) {
742*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
743*d57664e9SAndroid Build Coastguard Worker bool error = false;
744*d57664e9SAndroid Build Coastguard Worker
745*d57664e9SAndroid Build Coastguard Worker // Iterate over the input files in a stable, platform-independent manner
746*d57664e9SAndroid Build Coastguard Worker auto file_iterator = inputs->Iterator();
747*d57664e9SAndroid Build Coastguard Worker while (file_iterator->HasNext()) {
748*d57664e9SAndroid Build Coastguard Worker auto file = file_iterator->Next();
749*d57664e9SAndroid Build Coastguard Worker std::string path = file->GetSource().path;
750*d57664e9SAndroid Build Coastguard Worker
751*d57664e9SAndroid Build Coastguard Worker // Skip hidden input files
752*d57664e9SAndroid Build Coastguard Worker if (file::IsHidden(path)) {
753*d57664e9SAndroid Build Coastguard Worker continue;
754*d57664e9SAndroid Build Coastguard Worker }
755*d57664e9SAndroid Build Coastguard Worker
756*d57664e9SAndroid Build Coastguard Worker if (!options.res_zip && !IsValidFile(context, path)) {
757*d57664e9SAndroid Build Coastguard Worker error = true;
758*d57664e9SAndroid Build Coastguard Worker continue;
759*d57664e9SAndroid Build Coastguard Worker }
760*d57664e9SAndroid Build Coastguard Worker
761*d57664e9SAndroid Build Coastguard Worker // Extract resource type information from the full path
762*d57664e9SAndroid Build Coastguard Worker std::string err_str;
763*d57664e9SAndroid Build Coastguard Worker ResourcePathData path_data;
764*d57664e9SAndroid Build Coastguard Worker if (auto maybe_path_data = ExtractResourcePathData(
765*d57664e9SAndroid Build Coastguard Worker path, inputs->GetDirSeparator(), &err_str, options)) {
766*d57664e9SAndroid Build Coastguard Worker path_data = maybe_path_data.value();
767*d57664e9SAndroid Build Coastguard Worker } else {
768*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(file->GetSource()) << err_str);
769*d57664e9SAndroid Build Coastguard Worker error = true;
770*d57664e9SAndroid Build Coastguard Worker continue;
771*d57664e9SAndroid Build Coastguard Worker }
772*d57664e9SAndroid Build Coastguard Worker
773*d57664e9SAndroid Build Coastguard Worker // Determine how to compile the file based on its type.
774*d57664e9SAndroid Build Coastguard Worker auto compile_func = &CompileFile;
775*d57664e9SAndroid Build Coastguard Worker if (path_data.resource_dir == "values" && path_data.extension == "xml") {
776*d57664e9SAndroid Build Coastguard Worker compile_func = &CompileTable;
777*d57664e9SAndroid Build Coastguard Worker // We use a different extension (not necessary anymore, but avoids altering the existing
778*d57664e9SAndroid Build Coastguard Worker // build system logic).
779*d57664e9SAndroid Build Coastguard Worker path_data.extension = "arsc";
780*d57664e9SAndroid Build Coastguard Worker
781*d57664e9SAndroid Build Coastguard Worker } else if (const ResourceType* type = ParseResourceType(path_data.resource_dir)) {
782*d57664e9SAndroid Build Coastguard Worker if (*type != ResourceType::kRaw) {
783*d57664e9SAndroid Build Coastguard Worker if (*type == ResourceType::kXml || path_data.extension == "xml") {
784*d57664e9SAndroid Build Coastguard Worker compile_func = &CompileXml;
785*d57664e9SAndroid Build Coastguard Worker } else if ((!options.no_png_crunch && path_data.extension == "png")
786*d57664e9SAndroid Build Coastguard Worker || path_data.extension == "9.png") {
787*d57664e9SAndroid Build Coastguard Worker compile_func = &CompilePng;
788*d57664e9SAndroid Build Coastguard Worker }
789*d57664e9SAndroid Build Coastguard Worker }
790*d57664e9SAndroid Build Coastguard Worker } else {
791*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage()
792*d57664e9SAndroid Build Coastguard Worker << "invalid file path '" << path_data.source << "'");
793*d57664e9SAndroid Build Coastguard Worker error = true;
794*d57664e9SAndroid Build Coastguard Worker continue;
795*d57664e9SAndroid Build Coastguard Worker }
796*d57664e9SAndroid Build Coastguard Worker
797*d57664e9SAndroid Build Coastguard Worker // Treat periods as a reserved character that should not be present in a file name
798*d57664e9SAndroid Build Coastguard Worker // Legacy support for AAPT which did not reserve periods
799*d57664e9SAndroid Build Coastguard Worker if (compile_func != &CompileFile && !options.legacy_mode
800*d57664e9SAndroid Build Coastguard Worker && std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) {
801*d57664e9SAndroid Build Coastguard Worker error = true;
802*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
803*d57664e9SAndroid Build Coastguard Worker << "file name cannot contain '.' other than for"
804*d57664e9SAndroid Build Coastguard Worker << " specifying the extension");
805*d57664e9SAndroid Build Coastguard Worker continue;
806*d57664e9SAndroid Build Coastguard Worker }
807*d57664e9SAndroid Build Coastguard Worker
808*d57664e9SAndroid Build Coastguard Worker const std::string out_path = BuildIntermediateContainerFilename(path_data);
809*d57664e9SAndroid Build Coastguard Worker if (!compile_func(context, options, path_data, file, output_writer, out_path)) {
810*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
811*d57664e9SAndroid Build Coastguard Worker << "file failed to compile");
812*d57664e9SAndroid Build Coastguard Worker error = true;
813*d57664e9SAndroid Build Coastguard Worker }
814*d57664e9SAndroid Build Coastguard Worker }
815*d57664e9SAndroid Build Coastguard Worker
816*d57664e9SAndroid Build Coastguard Worker return error ? 1 : 0;
817*d57664e9SAndroid Build Coastguard Worker }
818*d57664e9SAndroid Build Coastguard Worker
Action(const std::vector<std::string> & args)819*d57664e9SAndroid Build Coastguard Worker int CompileCommand::Action(const std::vector<std::string>& args) {
820*d57664e9SAndroid Build Coastguard Worker TRACE_FLUSH(trace_folder_? trace_folder_.value() : "", "CompileCommand::Action");
821*d57664e9SAndroid Build Coastguard Worker CompileContext context(diagnostic_);
822*d57664e9SAndroid Build Coastguard Worker context.SetVerbose(options_.verbose);
823*d57664e9SAndroid Build Coastguard Worker
824*d57664e9SAndroid Build Coastguard Worker if (visibility_) {
825*d57664e9SAndroid Build Coastguard Worker if (visibility_.value() == "public") {
826*d57664e9SAndroid Build Coastguard Worker options_.visibility = Visibility::Level::kPublic;
827*d57664e9SAndroid Build Coastguard Worker } else if (visibility_.value() == "private") {
828*d57664e9SAndroid Build Coastguard Worker options_.visibility = Visibility::Level::kPrivate;
829*d57664e9SAndroid Build Coastguard Worker } else if (visibility_.value() == "default") {
830*d57664e9SAndroid Build Coastguard Worker options_.visibility = Visibility::Level::kUndefined;
831*d57664e9SAndroid Build Coastguard Worker } else {
832*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage()
833*d57664e9SAndroid Build Coastguard Worker << "Unrecognized visibility level passes to --visibility: '"
834*d57664e9SAndroid Build Coastguard Worker << visibility_.value()
835*d57664e9SAndroid Build Coastguard Worker << "'. Accepted levels: public, private, default");
836*d57664e9SAndroid Build Coastguard Worker return 1;
837*d57664e9SAndroid Build Coastguard Worker }
838*d57664e9SAndroid Build Coastguard Worker }
839*d57664e9SAndroid Build Coastguard Worker
840*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IFileCollection> file_collection;
841*d57664e9SAndroid Build Coastguard Worker
842*d57664e9SAndroid Build Coastguard Worker // Collect the resources files to compile
843*d57664e9SAndroid Build Coastguard Worker if (options_.res_dir && options_.res_zip) {
844*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage()
845*d57664e9SAndroid Build Coastguard Worker << "only one of --dir and --zip can be specified");
846*d57664e9SAndroid Build Coastguard Worker return 1;
847*d57664e9SAndroid Build Coastguard Worker } else if ((options_.res_dir || options_.res_zip) &&
848*d57664e9SAndroid Build Coastguard Worker options_.source_path && args.size() > 1) {
849*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(kPath)
850*d57664e9SAndroid Build Coastguard Worker << "Cannot use an overriding source path with multiple files.");
851*d57664e9SAndroid Build Coastguard Worker return 1;
852*d57664e9SAndroid Build Coastguard Worker } else if (options_.res_dir) {
853*d57664e9SAndroid Build Coastguard Worker if (!args.empty()) {
854*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage() << "files given but --dir specified");
855*d57664e9SAndroid Build Coastguard Worker Usage(&std::cerr);
856*d57664e9SAndroid Build Coastguard Worker return 1;
857*d57664e9SAndroid Build Coastguard Worker }
858*d57664e9SAndroid Build Coastguard Worker
859*d57664e9SAndroid Build Coastguard Worker // Load the files from the res directory
860*d57664e9SAndroid Build Coastguard Worker std::string err;
861*d57664e9SAndroid Build Coastguard Worker file_collection = io::FileCollection::Create(options_.res_dir.value(), &err);
862*d57664e9SAndroid Build Coastguard Worker if (!file_collection) {
863*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(options_.res_dir.value()) << err);
864*d57664e9SAndroid Build Coastguard Worker return 1;
865*d57664e9SAndroid Build Coastguard Worker }
866*d57664e9SAndroid Build Coastguard Worker } else if (options_.res_zip) {
867*d57664e9SAndroid Build Coastguard Worker if (!args.empty()) {
868*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage() << "files given but --zip specified");
869*d57664e9SAndroid Build Coastguard Worker Usage(&std::cerr);
870*d57664e9SAndroid Build Coastguard Worker return 1;
871*d57664e9SAndroid Build Coastguard Worker }
872*d57664e9SAndroid Build Coastguard Worker
873*d57664e9SAndroid Build Coastguard Worker // Load a zip file containing a res directory
874*d57664e9SAndroid Build Coastguard Worker std::string err;
875*d57664e9SAndroid Build Coastguard Worker file_collection = io::ZipFileCollection::Create(options_.res_zip.value(), &err);
876*d57664e9SAndroid Build Coastguard Worker if (!file_collection) {
877*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(options_.res_zip.value()) << err);
878*d57664e9SAndroid Build Coastguard Worker return 1;
879*d57664e9SAndroid Build Coastguard Worker }
880*d57664e9SAndroid Build Coastguard Worker } else {
881*d57664e9SAndroid Build Coastguard Worker auto collection = util::make_unique<io::FileCollection>();
882*d57664e9SAndroid Build Coastguard Worker
883*d57664e9SAndroid Build Coastguard Worker // Collect data from the path for each input file.
884*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> sorted_args = args;
885*d57664e9SAndroid Build Coastguard Worker std::sort(sorted_args.begin(), sorted_args.end());
886*d57664e9SAndroid Build Coastguard Worker
887*d57664e9SAndroid Build Coastguard Worker for (const std::string& arg : sorted_args) {
888*d57664e9SAndroid Build Coastguard Worker collection->InsertFile(arg);
889*d57664e9SAndroid Build Coastguard Worker }
890*d57664e9SAndroid Build Coastguard Worker
891*d57664e9SAndroid Build Coastguard Worker file_collection = std::move(collection);
892*d57664e9SAndroid Build Coastguard Worker }
893*d57664e9SAndroid Build Coastguard Worker
894*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<IArchiveWriter> archive_writer;
895*d57664e9SAndroid Build Coastguard Worker file::FileType output_file_type = file::GetFileType(options_.output_path);
896*d57664e9SAndroid Build Coastguard Worker if (output_file_type == file::FileType::kDirectory) {
897*d57664e9SAndroid Build Coastguard Worker archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
898*d57664e9SAndroid Build Coastguard Worker } else {
899*d57664e9SAndroid Build Coastguard Worker archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
900*d57664e9SAndroid Build Coastguard Worker }
901*d57664e9SAndroid Build Coastguard Worker
902*d57664e9SAndroid Build Coastguard Worker if (!archive_writer) {
903*d57664e9SAndroid Build Coastguard Worker return 1;
904*d57664e9SAndroid Build Coastguard Worker }
905*d57664e9SAndroid Build Coastguard Worker
906*d57664e9SAndroid Build Coastguard Worker // Parse the feature flag values. An argument that starts with '@' points to a file to read flag
907*d57664e9SAndroid Build Coastguard Worker // values from.
908*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> all_feature_flags_args;
909*d57664e9SAndroid Build Coastguard Worker for (const std::string& arg : feature_flags_args_) {
910*d57664e9SAndroid Build Coastguard Worker if (util::StartsWith(arg, "@")) {
911*d57664e9SAndroid Build Coastguard Worker const std::string path = arg.substr(1, arg.size() - 1);
912*d57664e9SAndroid Build Coastguard Worker std::string error;
913*d57664e9SAndroid Build Coastguard Worker if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) {
914*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
915*d57664e9SAndroid Build Coastguard Worker return 1;
916*d57664e9SAndroid Build Coastguard Worker }
917*d57664e9SAndroid Build Coastguard Worker } else {
918*d57664e9SAndroid Build Coastguard Worker all_feature_flags_args.push_back(arg);
919*d57664e9SAndroid Build Coastguard Worker }
920*d57664e9SAndroid Build Coastguard Worker }
921*d57664e9SAndroid Build Coastguard Worker
922*d57664e9SAndroid Build Coastguard Worker for (const std::string& arg : all_feature_flags_args) {
923*d57664e9SAndroid Build Coastguard Worker if (!ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
924*d57664e9SAndroid Build Coastguard Worker return 1;
925*d57664e9SAndroid Build Coastguard Worker }
926*d57664e9SAndroid Build Coastguard Worker }
927*d57664e9SAndroid Build Coastguard Worker
928*d57664e9SAndroid Build Coastguard Worker if (!options_.png_compression_level) {
929*d57664e9SAndroid Build Coastguard Worker options_.png_compression_level_int = 9;
930*d57664e9SAndroid Build Coastguard Worker } else {
931*d57664e9SAndroid Build Coastguard Worker if (options_.png_compression_level->size() != 1 ||
932*d57664e9SAndroid Build Coastguard Worker options_.png_compression_level->front() < '0' ||
933*d57664e9SAndroid Build Coastguard Worker options_.png_compression_level->front() > '9') {
934*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(
935*d57664e9SAndroid Build Coastguard Worker android::DiagMessage() << "PNG compression level should be a number in [0..9] range");
936*d57664e9SAndroid Build Coastguard Worker return 1;
937*d57664e9SAndroid Build Coastguard Worker }
938*d57664e9SAndroid Build Coastguard Worker options_.png_compression_level_int = options_.png_compression_level->front() - '0';
939*d57664e9SAndroid Build Coastguard Worker }
940*d57664e9SAndroid Build Coastguard Worker
941*d57664e9SAndroid Build Coastguard Worker return Compile(&context, file_collection.get(), archive_writer.get(), options_);
942*d57664e9SAndroid Build Coastguard Worker }
943*d57664e9SAndroid Build Coastguard Worker
944*d57664e9SAndroid Build Coastguard Worker } // namespace aapt
945