blob: f413ee9602649e8757bc8d156b90ee30c3635144 [file] [log] [blame]
Shane Farmer0a5b2012017-06-22 12:24:12 -07001/*
2 * Copyright (C) 2017 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
17#include "MultiApkGenerator.h"
18
19#include <algorithm>
20#include <string>
21
22#include "androidfw/StringPiece.h"
23
24#include "LoadedApk.h"
25#include "configuration/ConfigurationParser.h"
26#include "filter/AbiFilter.h"
27#include "filter/Filter.h"
28#include "flatten/Archive.h"
29#include "process/IResourceTableConsumer.h"
30#include "split/TableSplitter.h"
31#include "util/Files.h"
32
33namespace aapt {
34
35using ::aapt::configuration::Artifact;
36using ::aapt::configuration::PostProcessingConfiguration;
37using ::android::StringPiece;
38
39MultiApkGenerator::MultiApkGenerator(LoadedApk* apk, IAaptContext* context)
40 : apk_(apk), context_(context) {
41}
42
43bool MultiApkGenerator::FromBaseApk(const std::string& out_dir,
44 const PostProcessingConfiguration& config,
45 const TableFlattenerOptions& table_flattener_options) {
46 // TODO(safarmer): Handle APK version codes for the generated APKs.
47 // TODO(safarmer): Handle explicit outputs/generating an output file list for other tools.
48
49 const std::string& apk_path = apk_->GetSource().path;
50 const StringPiece ext = file::GetExtension(apk_path);
51 const std::string base_name = apk_path.substr(0, apk_path.rfind(ext.to_string()));
52
53 // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
54 for (const Artifact& artifact : config.artifacts) {
55 FilterChain filters;
56 TableSplitterOptions splits;
57 AxisConfigFilter axis_filter;
58
59 if (!artifact.name && !config.artifact_format) {
60 context_->GetDiagnostics()->Error(
61 DiagMessage() << "Artifact does not have a name and no global name template defined");
62 return false;
63 }
64
65 Maybe<std::string> artifact_name =
66 (artifact.name)
67 ? artifact.Name(base_name, ext.substr(1), context_->GetDiagnostics())
68 : artifact.ToArtifactName(config.artifact_format.value(), context_->GetDiagnostics(),
69 base_name, ext.substr(1));
70
71 if (!artifact_name) {
72 context_->GetDiagnostics()->Error(DiagMessage()
73 << "Could not determine split APK artifact name");
74 return false;
75 }
76
77 if (artifact.abi_group) {
78 const std::string& group_name = artifact.abi_group.value();
79
80 auto group = config.abi_groups.find(group_name);
81 // TODO: Remove validation when configuration parser ensures referential integrity.
82 if (group == config.abi_groups.end()) {
83 context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced ABI group '"
84 << group_name << "'");
85 return false;
86 }
87 filters.AddFilter(AbiFilter::FromAbiList(group->second));
88 }
89
90 if (artifact.screen_density_group) {
91 const std::string& group_name = artifact.screen_density_group.value();
92
93 auto group = config.screen_density_groups.find(group_name);
94 // TODO: Remove validation when configuration parser ensures referential integrity.
95 if (group == config.screen_density_groups.end()) {
96 context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
97 << group_name << "'");
98 return false;
99 }
100
101 const std::vector<ConfigDescription>& densities = group->second;
102 std::for_each(densities.begin(), densities.end(), [&](const ConfigDescription& c) {
103 splits.preferred_densities.push_back(c.density);
104 });
105 }
106
107 if (artifact.locale_group) {
108 const std::string& group_name = artifact.locale_group.value();
109 auto group = config.locale_groups.find(group_name);
110 // TODO: Remove validation when configuration parser ensures referential integrity.
111 if (group == config.locale_groups.end()) {
112 context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
113 << group_name << "'");
114 return false;
115 }
116
117 const std::vector<ConfigDescription>& locales = group->second;
118 std::for_each(locales.begin(), locales.end(),
119 [&](const ConfigDescription& c) { axis_filter.AddConfig(c); });
120 splits.config_filter = &axis_filter;
121 }
122
123 std::unique_ptr<ResourceTable> table = apk_->GetResourceTable()->Clone();
124
125 TableSplitter splitter{{}, splits};
126 splitter.SplitTable(table.get());
127
128 std::string out = out_dir;
129 file::AppendPath(&out, artifact_name.value());
130
131 std::unique_ptr<IArchiveWriter> writer =
132 CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
133
134 if (context_->IsVerbose()) {
135 context_->GetDiagnostics()->Note(DiagMessage() << "Writing output: " << out);
136 }
137
138 if (!apk_->WriteToArchive(context_, table.get(), table_flattener_options, &filters,
139 writer.get())) {
140 return false;
141 }
142 }
143
144 return true;
145}
146
147} // namespace aapt