blob: 8027f4289169677c56c78f4d8b2d912c91b581af [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinskice5e56e2016-10-21 17:56:45 -070017#include <dirent.h>
18
19#include <fstream>
20#include <string>
21
Adam Lesinskid5083f62017-01-16 15:07:21 -080022#include "android-base/errors.h"
23#include "android-base/file.h"
24#include "androidfw/StringPiece.h"
25#include "google/protobuf/io/coded_stream.h"
26#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
27
Adam Lesinski1ab598f2015-08-14 14:26:04 -070028#include "ConfigDescription.h"
29#include "Diagnostics.h"
30#include "Flags.h"
31#include "ResourceParser.h"
32#include "ResourceTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070033#include "compile/IdAssigner.h"
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070034#include "compile/InlineXmlFormatParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070035#include "compile/Png.h"
Adam Lesinski393b5f02015-12-17 13:03:11 -080036#include "compile/PseudolocaleGenerator.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070037#include "compile/XmlIdCollector.h"
Adam Lesinskia40e9722015-11-24 19:11:46 -080038#include "flatten/Archive.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070039#include "flatten/XmlFlattener.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080040#include "proto/ProtoSerialize.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070041#include "util/Files.h"
42#include "util/Maybe.h"
43#include "util/Util.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080044#include "xml/XmlDom.h"
45#include "xml/XmlPullParser.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046
Adam Lesinskid5083f62017-01-16 15:07:21 -080047using android::StringPiece;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -070048using google::protobuf::io::CopyingOutputStreamAdaptor;
49using google::protobuf::io::ZeroCopyOutputStream;
50
Adam Lesinski1ab598f2015-08-14 14:26:04 -070051namespace aapt {
52
53struct ResourcePathData {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070054 Source source;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070055 std::string resource_dir;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070056 std::string name;
57 std::string extension;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070058
Adam Lesinskicacb28f2016-10-19 12:18:14 -070059 // Original config str. We keep this because when we parse the config, we may
60 // add on
61 // version qualifiers. We want to preserve the original input so the output is
62 // easily
63 // computed before hand.
Adam Lesinskice5e56e2016-10-21 17:56:45 -070064 std::string config_str;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070065 ConfigDescription config;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070066};
67
68/**
69 * Resource file paths are expected to look like:
70 * [--/res/]type[-config]/name
71 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -070072static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
73 std::string* out_error) {
74 std::vector<std::string> parts = util::Split(path, file::sDirSep);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070075 if (parts.size() < 2) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070076 if (out_error) *out_error = "bad resource path";
Adam Lesinskicacb28f2016-10-19 12:18:14 -070077 return {};
78 }
79
80 std::string& dir = parts[parts.size() - 2];
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081 StringPiece dir_str = dir;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070082
Adam Lesinskice5e56e2016-10-21 17:56:45 -070083 StringPiece config_str;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070084 ConfigDescription config;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070085 size_t dash_pos = dir.find('-');
86 if (dash_pos != std::string::npos) {
87 config_str = dir_str.substr(dash_pos + 1, dir.size() - (dash_pos + 1));
88 if (!ConfigDescription::Parse(config_str, &config)) {
89 if (out_error) {
90 std::stringstream err_str;
91 err_str << "invalid configuration '" << config_str << "'";
92 *out_error = err_str.str();
Adam Lesinskicacb28f2016-10-19 12:18:14 -070093 }
94 return {};
Adam Lesinski1ab598f2015-08-14 14:26:04 -070095 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070096 dir_str = dir_str.substr(0, dash_pos);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070097 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070098
Adam Lesinskicacb28f2016-10-19 12:18:14 -070099 std::string& filename = parts[parts.size() - 1];
100 StringPiece name = filename;
101 StringPiece extension;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700102 size_t dot_pos = filename.find('.');
103 if (dot_pos != std::string::npos) {
104 extension = name.substr(dot_pos + 1, filename.size() - (dot_pos + 1));
105 name = name.substr(0, dot_pos);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700106 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700107
Adam Lesinskid5083f62017-01-16 15:07:21 -0800108 return ResourcePathData{Source(path), dir_str.to_string(), name.to_string(),
109 extension.to_string(), config_str.to_string(), config};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700110}
111
112struct CompileOptions {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700113 std::string output_path;
114 Maybe<std::string> res_dir;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700115 bool pseudolocalize = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700116 bool legacy_mode = false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700117 bool verbose = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700118};
119
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700120static std::string BuildIntermediateFilename(const ResourcePathData& data) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700121 std::stringstream name;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700122 name << data.resource_dir;
123 if (!data.config_str.empty()) {
124 name << "-" << data.config_str;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700125 }
126 name << "_" << data.name;
127 if (!data.extension.empty()) {
128 name << "." << data.extension;
129 }
130 name << ".flat";
131 return name.str();
Adam Lesinskia40e9722015-11-24 19:11:46 -0800132}
133
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700134static bool IsHidden(const StringPiece& filename) {
135 return util::StartsWith(filename, ".");
Adam Lesinskia40e9722015-11-24 19:11:46 -0800136}
137
138/**
139 * Walks the res directory structure, looking for resource files.
140 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700141static bool LoadInputFilesFromDir(
142 IAaptContext* context, const CompileOptions& options,
143 std::vector<ResourcePathData>* out_path_data) {
144 const std::string& root_dir = options.res_dir.value();
145 std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()),
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700146 closedir);
147 if (!d) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700148 context->GetDiagnostics()->Error(DiagMessage() << strerror(errno));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700149 return false;
150 }
151
152 while (struct dirent* entry = readdir(d.get())) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700153 if (IsHidden(entry->d_name)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700154 continue;
155 }
156
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700157 std::string prefix_path = root_dir;
158 file::AppendPath(&prefix_path, entry->d_name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700160 if (file::GetFileType(prefix_path) != file::FileType::kDirectory) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700161 continue;
162 }
163
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700164 std::unique_ptr<DIR, decltype(closedir)*> subdir(
165 opendir(prefix_path.data()), closedir);
166 if (!subdir) {
167 context->GetDiagnostics()->Error(DiagMessage() << strerror(errno));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700168 return false;
169 }
170
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700171 while (struct dirent* leaf_entry = readdir(subdir.get())) {
172 if (IsHidden(leaf_entry->d_name)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700173 continue;
174 }
175
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700176 std::string full_path = prefix_path;
177 file::AppendPath(&full_path, leaf_entry->d_name);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700178
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700179 std::string err_str;
180 Maybe<ResourcePathData> path_data =
181 ExtractResourcePathData(full_path, &err_str);
182 if (!path_data) {
183 context->GetDiagnostics()->Error(DiagMessage() << err_str);
Adam Lesinskia40e9722015-11-24 19:11:46 -0800184 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700185 }
186
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700187 out_path_data->push_back(std::move(path_data.value()));
Adam Lesinskia40e9722015-11-24 19:11:46 -0800188 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700189 }
190 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700191}
192
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700193static bool CompileTable(IAaptContext* context, const CompileOptions& options,
194 const ResourcePathData& path_data,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700195 IArchiveWriter* writer,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700196 const std::string& output_path) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700197 ResourceTable table;
198 {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700199 std::ifstream fin(path_data.source.path, std::ifstream::binary);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700200 if (!fin) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700201 context->GetDiagnostics()->Error(DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700202 << strerror(errno));
203 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700204 }
205
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700206 // Parse the values file from XML.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700207 xml::XmlPullParser xml_parser(fin);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700208
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700209 ResourceParserOptions parser_options;
210 parser_options.error_on_positional_arguments = !options.legacy_mode;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700211
212 // If the filename includes donottranslate, then the default translatable is
213 // false.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700214 parser_options.translatable =
215 path_data.name.find("donottranslate") == std::string::npos;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700216
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700217 ResourceParser res_parser(context->GetDiagnostics(), &table,
218 path_data.source, path_data.config,
219 parser_options);
220 if (!res_parser.Parse(&xml_parser)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700221 return false;
Adam Lesinski393b5f02015-12-17 13:03:11 -0800222 }
223
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700224 fin.close();
225 }
Adam Lesinski83f22552015-11-07 11:51:23 -0800226
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700227 if (options.pseudolocalize) {
228 // Generate pseudo-localized strings (en-XA and ar-XB).
229 // These are created as weak symbols, and are only generated from default
230 // configuration
231 // strings and plurals.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700232 PseudolocaleGenerator pseudolocale_generator;
233 if (!pseudolocale_generator.Consume(context, &table)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 return false;
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700235 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700236 }
Adam Lesinski9ba47d82015-10-13 11:37:10 -0700237
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700238 // Ensure we have the compilation package at least.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700239 table.CreatePackage(context->GetCompilationPackage());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700240
241 // Assign an ID to any package that has resources.
242 for (auto& pkg : table.packages) {
243 if (!pkg->id) {
244 // If no package ID was set while parsing (public identifiers), auto
245 // assign an ID.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700246 pkg->id = context->GetPackageId();
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700247 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700248 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700249
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700250 // Create the file/zip entry.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700251 if (!writer->StartEntry(output_path, 0)) {
252 context->GetDiagnostics()->Error(DiagMessage(output_path)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700253 << "failed to open");
254 return false;
255 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800256
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700257 // Make sure CopyingOutputStreamAdaptor is deleted before we call
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700258 // writer->FinishEntry().
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700259 {
260 // Wrap our IArchiveWriter with an adaptor that implements the
261 // ZeroCopyOutputStream
262 // interface.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700263 CopyingOutputStreamAdaptor copying_adaptor(writer);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700264
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700265 std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(&table);
266 if (!pb_table->SerializeToZeroCopyStream(&copying_adaptor)) {
267 context->GetDiagnostics()->Error(DiagMessage(output_path)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700268 << "failed to write");
269 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700270 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700271 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800272
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700273 if (!writer->FinishEntry()) {
274 context->GetDiagnostics()->Error(DiagMessage(output_path)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700275 << "failed to finish entry");
276 return false;
277 }
278 return true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800279}
280
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700281static bool WriteHeaderAndBufferToWriter(const StringPiece& output_path,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700282 const ResourceFile& file,
283 const BigBuffer& buffer,
284 IArchiveWriter* writer,
Adam Lesinski59e04c62016-02-04 15:59:23 -0800285 IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700286 // Start the entry so we can write the header.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700287 if (!writer->StartEntry(output_path, 0)) {
288 diag->Error(DiagMessage(output_path) << "failed to open file");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700289 return false;
290 }
291
292 // Make sure CopyingOutputStreamAdaptor is deleted before we call
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700293 // writer->FinishEntry().
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700294 {
295 // Wrap our IArchiveWriter with an adaptor that implements the
296 // ZeroCopyOutputStream
297 // interface.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700298 CopyingOutputStreamAdaptor copying_adaptor(writer);
299 CompiledFileOutputStream output_stream(&copying_adaptor);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700300
301 // Number of CompiledFiles.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700302 output_stream.WriteLittleEndian32(1);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700303
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700304 std::unique_ptr<pb::CompiledFile> compiled_file =
305 SerializeCompiledFileToPb(file);
306 output_stream.WriteCompiledFile(compiled_file.get());
307 output_stream.WriteData(&buffer);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700308
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700309 if (output_stream.HadError()) {
310 diag->Error(DiagMessage(output_path) << "failed to write data");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700311 return false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800312 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700313 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800314
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700315 if (!writer->FinishEntry()) {
316 diag->Error(DiagMessage(output_path) << "failed to finish writing data");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700317 return false;
318 }
319 return true;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800320}
321
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700322static bool WriteHeaderAndMmapToWriter(const StringPiece& output_path,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700323 const ResourceFile& file,
324 const android::FileMap& map,
325 IArchiveWriter* writer,
Adam Lesinski59e04c62016-02-04 15:59:23 -0800326 IDiagnostics* diag) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700327 // Start the entry so we can write the header.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700328 if (!writer->StartEntry(output_path, 0)) {
329 diag->Error(DiagMessage(output_path) << "failed to open file");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700330 return false;
331 }
332
333 // Make sure CopyingOutputStreamAdaptor is deleted before we call
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700334 // writer->FinishEntry().
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700335 {
336 // Wrap our IArchiveWriter with an adaptor that implements the
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700337 // ZeroCopyOutputStream interface.
338 CopyingOutputStreamAdaptor copying_adaptor(writer);
339 CompiledFileOutputStream output_stream(&copying_adaptor);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700340
341 // Number of CompiledFiles.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700342 output_stream.WriteLittleEndian32(1);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700343
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700344 std::unique_ptr<pb::CompiledFile> compiled_file =
345 SerializeCompiledFileToPb(file);
346 output_stream.WriteCompiledFile(compiled_file.get());
347 output_stream.WriteData(map.getDataPtr(), map.getDataLength());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700348
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700349 if (output_stream.HadError()) {
350 diag->Error(DiagMessage(output_path) << "failed to write data");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700351 return false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800352 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700353 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800354
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355 if (!writer->FinishEntry()) {
356 diag->Error(DiagMessage(output_path) << "failed to finish writing data");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700357 return false;
358 }
359 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700360}
361
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700362static bool FlattenXmlToOutStream(IAaptContext* context,
363 const StringPiece& output_path,
364 xml::XmlResource* xmlres,
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700365 CompiledFileOutputStream* out) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700366 BigBuffer buffer(1024);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700367 XmlFlattenerOptions xml_flattener_options;
368 xml_flattener_options.keep_raw_values = true;
369 XmlFlattener flattener(&buffer, xml_flattener_options);
370 if (!flattener.Consume(context, xmlres)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700371 return false;
372 }
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700373
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700374 std::unique_ptr<pb::CompiledFile> pb_compiled_file =
375 SerializeCompiledFileToPb(xmlres->file);
376 out->WriteCompiledFile(pb_compiled_file.get());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700377 out->WriteData(&buffer);
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700378
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700379 if (out->HadError()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700380 context->GetDiagnostics()->Error(DiagMessage(output_path)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700381 << "failed to write data");
382 return false;
383 }
384 return true;
Adam Lesinski5eeaadd2016-08-25 12:26:56 -0700385}
386
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700387static bool CompileXml(IAaptContext* context, const CompileOptions& options,
388 const ResourcePathData& path_data,
389 IArchiveWriter* writer, const std::string& output_path) {
390 if (context->IsVerbose()) {
391 context->GetDiagnostics()->Note(DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700392 << "compiling XML");
393 }
394
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700395 std::unique_ptr<xml::XmlResource> xmlres;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700396 {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700397 std::ifstream fin(path_data.source.path, std::ifstream::binary);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700398 if (!fin) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700399 context->GetDiagnostics()->Error(DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700400 << strerror(errno));
401 return false;
Adam Lesinski21efb682016-09-14 17:35:43 -0700402 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700403
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700404 xmlres = xml::Inflate(&fin, context->GetDiagnostics(), path_data.source);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700405
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700406 fin.close();
407 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700408
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700409 if (!xmlres) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700410 return false;
411 }
412
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700413 xmlres->file.name = ResourceName(
414 {}, *ParseResourceType(path_data.resource_dir), path_data.name);
415 xmlres->file.config = path_data.config;
416 xmlres->file.source = path_data.source;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700417
418 // Collect IDs that are defined here.
419 XmlIdCollector collector;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700420 if (!collector.Consume(context, xmlres.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700421 return false;
422 }
423
424 // Look for and process any <aapt:attr> tags and create sub-documents.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700425 InlineXmlFormatParser inline_xml_format_parser;
426 if (!inline_xml_format_parser.Consume(context, xmlres.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700427 return false;
428 }
429
430 // Start the entry so we can write the header.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700431 if (!writer->StartEntry(output_path, 0)) {
432 context->GetDiagnostics()->Error(DiagMessage(output_path)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700433 << "failed to open file");
434 return false;
435 }
436
437 // Make sure CopyingOutputStreamAdaptor is deleted before we call
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700438 // writer->FinishEntry().
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700439 {
440 // Wrap our IArchiveWriter with an adaptor that implements the
441 // ZeroCopyOutputStream
442 // interface.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700443 CopyingOutputStreamAdaptor copying_adaptor(writer);
444 CompiledFileOutputStream output_stream(&copying_adaptor);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700445
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700446 std::vector<std::unique_ptr<xml::XmlResource>>& inline_documents =
447 inline_xml_format_parser.GetExtractedInlineXmlDocuments();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700448
449 // Number of CompiledFiles.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700450 output_stream.WriteLittleEndian32(1 + inline_documents.size());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700451
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700452 if (!FlattenXmlToOutStream(context, output_path, xmlres.get(),
453 &output_stream)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700454 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700455 }
456
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700457 for (auto& inline_xml_doc : inline_documents) {
458 if (!FlattenXmlToOutStream(context, output_path, inline_xml_doc.get(),
459 &output_stream)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700460 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700461 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700462 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700463 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700464
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700465 if (!writer->FinishEntry()) {
466 context->GetDiagnostics()->Error(DiagMessage(output_path)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700467 << "failed to finish writing data");
468 return false;
469 }
470 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700471}
472
Adam Lesinski21efb682016-09-14 17:35:43 -0700473class BigBufferOutputStream : public io::OutputStream {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700474 public:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700475 explicit BigBufferOutputStream(BigBuffer* buffer) : buffer_(buffer) {}
Adam Lesinski21efb682016-09-14 17:35:43 -0700476
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700477 bool Next(void** data, int* len) override {
478 size_t count;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700479 *data = buffer_->NextBlock(&count);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700480 *len = static_cast<int>(count);
481 return true;
482 }
Adam Lesinski21efb682016-09-14 17:35:43 -0700483
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700484 void BackUp(int count) override { buffer_->BackUp(count); }
Adam Lesinski21efb682016-09-14 17:35:43 -0700485
Tamas Berghammer383db5eb2016-06-22 15:21:38 +0100486 google::protobuf::int64 ByteCount() const override {
487 return buffer_->size();
488 }
Adam Lesinski21efb682016-09-14 17:35:43 -0700489
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700490 bool HadError() const override { return false; }
Adam Lesinski21efb682016-09-14 17:35:43 -0700491
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700492 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700493 BigBuffer* buffer_;
Adam Lesinski21efb682016-09-14 17:35:43 -0700494
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700495 DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
Adam Lesinski21efb682016-09-14 17:35:43 -0700496};
497
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700498static bool CompilePng(IAaptContext* context, const CompileOptions& options,
499 const ResourcePathData& path_data,
500 IArchiveWriter* writer, const std::string& output_path) {
501 if (context->IsVerbose()) {
502 context->GetDiagnostics()->Note(DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700503 << "compiling PNG");
504 }
505
506 BigBuffer buffer(4096);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700507 ResourceFile res_file;
508 res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir),
509 path_data.name);
510 res_file.config = path_data.config;
511 res_file.source = path_data.source;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700512
513 {
514 std::string content;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700515 if (!android::base::ReadFileToString(path_data.source.path, &content)) {
516 context->GetDiagnostics()->Error(
517 DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700518 << android::base::SystemErrorCodeToString(errno));
519 return false;
Adam Lesinski21efb682016-09-14 17:35:43 -0700520 }
521
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700522 BigBuffer crunched_png_buffer(4096);
523 BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700524
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700525 // Ensure that we only keep the chunks we care about if we end up
526 // using the original PNG instead of the crunched one.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700527 PngChunkFilter png_chunk_filter(content);
528 std::unique_ptr<Image> image = ReadPng(context, &png_chunk_filter);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700529 if (!image) {
530 return false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700531 }
532
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700533 std::unique_ptr<NinePatch> nine_patch;
534 if (path_data.extension == "9.png") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700535 std::string err;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700536 nine_patch = NinePatch::Create(image->rows.get(), image->width,
537 image->height, &err);
538 if (!nine_patch) {
539 context->GetDiagnostics()->Error(DiagMessage() << err);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700540 return false;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700541 }
542
543 // Remove the 1px border around the NinePatch.
544 // Basically the row array is shifted up by 1, and the length is treated
545 // as height - 2.
546 // For each row, shift the array to the left by 1, and treat the length as
547 // width - 2.
548 image->width -= 2;
549 image->height -= 2;
550 memmove(image->rows.get(), image->rows.get() + 1,
551 image->height * sizeof(uint8_t**));
552 for (int32_t h = 0; h < image->height; h++) {
553 memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
554 }
555
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700556 if (context->IsVerbose()) {
557 context->GetDiagnostics()->Note(DiagMessage(path_data.source)
558 << "9-patch: " << *nine_patch);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700559 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700560 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700561
562 // Write the crunched PNG.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700563 if (!WritePng(context, image.get(), nine_patch.get(),
564 &crunched_png_buffer_out, {})) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700565 return false;
566 }
567
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700568 if (nine_patch != nullptr ||
569 crunched_png_buffer_out.ByteCount() <= png_chunk_filter.ByteCount()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700570 // No matter what, we must use the re-encoded PNG, even if it is larger.
571 // 9-patch images must be re-encoded since their borders are stripped.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700572 buffer.AppendBuffer(std::move(crunched_png_buffer));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700573 } else {
574 // The re-encoded PNG is larger than the original, and there is
575 // no mandatory transformation. Use the original.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700576 if (context->IsVerbose()) {
577 context->GetDiagnostics()->Note(
578 DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700579 << "original PNG is smaller than crunched PNG"
580 << ", using original");
581 }
582
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700583 PngChunkFilter png_chunk_filter_again(content);
584 BigBuffer filtered_png_buffer(4096);
585 BigBufferOutputStream filtered_png_buffer_out(&filtered_png_buffer);
586 io::Copy(&filtered_png_buffer_out, &png_chunk_filter_again);
587 buffer.AppendBuffer(std::move(filtered_png_buffer));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700588 }
589
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700590 if (context->IsVerbose()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700591 // For debugging only, use the legacy PNG cruncher and compare the
592 // resulting file sizes.
593 // This will help catch exotic cases where the new code may generate
594 // larger PNGs.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700595 std::stringstream legacy_stream(content);
596 BigBuffer legacy_buffer(4096);
597 Png png(context->GetDiagnostics());
598 if (!png.process(path_data.source, &legacy_stream, &legacy_buffer, {})) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700599 return false;
600 }
601
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700602 context->GetDiagnostics()->Note(DiagMessage(path_data.source)
603 << "legacy=" << legacy_buffer.size()
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700604 << " new=" << buffer.size());
605 }
606 }
607
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700608 if (!WriteHeaderAndBufferToWriter(output_path, res_file, buffer, writer,
609 context->GetDiagnostics())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700610 return false;
611 }
612 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700613}
614
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700615static bool CompileFile(IAaptContext* context, const CompileOptions& options,
616 const ResourcePathData& path_data,
617 IArchiveWriter* writer,
618 const std::string& output_path) {
619 if (context->IsVerbose()) {
620 context->GetDiagnostics()->Note(DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700621 << "compiling file");
622 }
Adam Lesinski21efb682016-09-14 17:35:43 -0700623
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700624 BigBuffer buffer(256);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700625 ResourceFile res_file;
626 res_file.name = ResourceName({}, *ParseResourceType(path_data.resource_dir),
627 path_data.name);
628 res_file.config = path_data.config;
629 res_file.source = path_data.source;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700630
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700631 std::string error_str;
632 Maybe<android::FileMap> f = file::MmapPath(path_data.source.path, &error_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700633 if (!f) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700634 context->GetDiagnostics()->Error(DiagMessage(path_data.source)
635 << error_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700636 return false;
637 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700638
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700639 if (!WriteHeaderAndMmapToWriter(output_path, res_file, f.value(), writer,
640 context->GetDiagnostics())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700641 return false;
642 }
643 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700644}
645
646class CompileContext : public IAaptContext {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700647 public:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700648 void SetVerbose(bool val) { verbose_ = val; }
Adam Lesinski355f2852016-02-13 20:26:45 -0800649
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700650 bool IsVerbose() override { return verbose_; }
Adam Lesinski355f2852016-02-13 20:26:45 -0800651
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700652 IDiagnostics* GetDiagnostics() override { return &diagnostics_; }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700653
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700654 NameMangler* GetNameMangler() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700655 abort();
656 return nullptr;
657 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700658
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700659 const std::string& GetCompilationPackage() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700660 static std::string empty;
661 return empty;
662 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700663
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700664 uint8_t GetPackageId() override { return 0x0; }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700665
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700666 SymbolTable* GetExternalSymbols() override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700667 abort();
668 return nullptr;
669 }
Adam Lesinski64587af2016-02-18 18:33:06 -0800670
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700671 int GetMinSdkVersion() override { return 0; }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700672
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700673 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700674 StdErrDiagnostics diagnostics_;
675 bool verbose_ = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700676};
677
678/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700679 * Entry point for compilation phase. Parses arguments and dispatches to the
680 * correct steps.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700681 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700682int Compile(const std::vector<StringPiece>& args) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700683 CompileContext context;
684 CompileOptions options;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700685
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700686 bool verbose = false;
687 Flags flags =
688 Flags()
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700689 .RequiredFlag("-o", "Output path", &options.output_path)
690 .OptionalFlag("--dir", "Directory to scan for resources",
691 &options.res_dir)
692 .OptionalSwitch("--pseudo-localize",
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700693 "Generate resources for pseudo-locales "
694 "(en-XA and ar-XB)",
695 &options.pseudolocalize)
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700696 .OptionalSwitch(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700697 "--legacy",
698 "Treat errors that used to be valid in AAPT as warnings",
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700699 &options.legacy_mode)
700 .OptionalSwitch("-v", "Enables verbose logging", &verbose);
701 if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700702 return 1;
703 }
704
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700705 context.SetVerbose(verbose);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700706
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700707 std::unique_ptr<IArchiveWriter> archive_writer;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700708
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700709 std::vector<ResourcePathData> input_data;
710 if (options.res_dir) {
711 if (!flags.GetArgs().empty()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700712 // Can't have both files and a resource directory.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700713 context.GetDiagnostics()->Error(DiagMessage()
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700714 << "files given but --dir specified");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700715 flags.Usage("aapt2 compile", &std::cerr);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700716 return 1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700717 }
718
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700719 if (!LoadInputFilesFromDir(&context, options, &input_data)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700720 return 1;
721 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800722
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700723 archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(),
724 options.output_path);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700725
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700726 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700727 input_data.reserve(flags.GetArgs().size());
Adam Lesinskia40e9722015-11-24 19:11:46 -0800728
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700729 // Collect data from the path for each input file.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700730 for (const std::string& arg : flags.GetArgs()) {
731 std::string error_str;
732 if (Maybe<ResourcePathData> path_data =
733 ExtractResourcePathData(arg, &error_str)) {
734 input_data.push_back(std::move(path_data.value()));
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700735 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700736 context.GetDiagnostics()->Error(DiagMessage() << error_str << " ("
737 << arg << ")");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700738 return 1;
739 }
740 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800741
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700742 archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(),
743 options.output_path);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700744 }
745
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700746 if (!archive_writer) {
Adam Lesinskidfaecaf2016-10-20 17:08:51 -0700747 return 1;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700748 }
749
750 bool error = false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700751 for (ResourcePathData& path_data : input_data) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700752 if (options.verbose) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700753 context.GetDiagnostics()->Note(DiagMessage(path_data.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700754 << "processing");
755 }
756
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700757 if (path_data.resource_dir == "values") {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700758 // Overwrite the extension.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700759 path_data.extension = "arsc";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700760
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700761 const std::string output_filename = BuildIntermediateFilename(path_data);
762 if (!CompileTable(&context, options, path_data, archive_writer.get(),
763 output_filename)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700764 error = true;
765 }
Adam Lesinskia40e9722015-11-24 19:11:46 -0800766
767 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700768 const std::string output_filename = BuildIntermediateFilename(path_data);
769 if (const ResourceType* type =
770 ParseResourceType(path_data.resource_dir)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700771 if (*type != ResourceType::kRaw) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700772 if (path_data.extension == "xml") {
773 if (!CompileXml(&context, options, path_data, archive_writer.get(),
774 output_filename)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700775 error = true;
Adam Lesinskia40e9722015-11-24 19:11:46 -0800776 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700777 } else if (path_data.extension == "png" ||
778 path_data.extension == "9.png") {
779 if (!CompilePng(&context, options, path_data, archive_writer.get(),
780 output_filename)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700781 error = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700782 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700783 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700784 if (!CompileFile(&context, options, path_data, archive_writer.get(),
785 output_filename)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700786 error = true;
787 }
788 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700789 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700790 if (!CompileFile(&context, options, path_data, archive_writer.get(),
791 output_filename)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700792 error = true;
793 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700794 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700795 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700796 context.GetDiagnostics()->Error(
797 DiagMessage() << "invalid file path '" << path_data.source << "'");
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700798 error = true;
799 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700800 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700801 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700802
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700803 if (error) {
804 return 1;
805 }
806 return 0;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700807}
808
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700809} // namespace aapt