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 "Link.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <sys/stat.h>
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker #include <algorithm>
22*d57664e9SAndroid Build Coastguard Worker #include <cinttypes>
23*d57664e9SAndroid Build Coastguard Worker #include <queue>
24*d57664e9SAndroid Build Coastguard Worker #include <unordered_map>
25*d57664e9SAndroid Build Coastguard Worker #include <vector>
26*d57664e9SAndroid Build Coastguard Worker
27*d57664e9SAndroid Build Coastguard Worker #include "AppInfo.h"
28*d57664e9SAndroid Build Coastguard Worker #include "Debug.h"
29*d57664e9SAndroid Build Coastguard Worker #include "LoadedApk.h"
30*d57664e9SAndroid Build Coastguard Worker #include "NameMangler.h"
31*d57664e9SAndroid Build Coastguard Worker #include "ResourceUtils.h"
32*d57664e9SAndroid Build Coastguard Worker #include "ResourceValues.h"
33*d57664e9SAndroid Build Coastguard Worker #include "ValueVisitor.h"
34*d57664e9SAndroid Build Coastguard Worker #include "android-base/errors.h"
35*d57664e9SAndroid Build Coastguard Worker #include "android-base/expected.h"
36*d57664e9SAndroid Build Coastguard Worker #include "android-base/file.h"
37*d57664e9SAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
38*d57664e9SAndroid Build Coastguard Worker #include "androidfw/BigBufferStream.h"
39*d57664e9SAndroid Build Coastguard Worker #include "androidfw/FileStream.h"
40*d57664e9SAndroid Build Coastguard Worker #include "androidfw/IDiagnostics.h"
41*d57664e9SAndroid Build Coastguard Worker #include "androidfw/Locale.h"
42*d57664e9SAndroid Build Coastguard Worker #include "androidfw/StringPiece.h"
43*d57664e9SAndroid Build Coastguard Worker #include "cmd/Util.h"
44*d57664e9SAndroid Build Coastguard Worker #include "compile/IdAssigner.h"
45*d57664e9SAndroid Build Coastguard Worker #include "compile/XmlIdCollector.h"
46*d57664e9SAndroid Build Coastguard Worker #include "filter/ConfigFilter.h"
47*d57664e9SAndroid Build Coastguard Worker #include "format/Archive.h"
48*d57664e9SAndroid Build Coastguard Worker #include "format/Container.h"
49*d57664e9SAndroid Build Coastguard Worker #include "format/binary/TableFlattener.h"
50*d57664e9SAndroid Build Coastguard Worker #include "format/binary/XmlFlattener.h"
51*d57664e9SAndroid Build Coastguard Worker #include "format/proto/ProtoDeserialize.h"
52*d57664e9SAndroid Build Coastguard Worker #include "format/proto/ProtoSerialize.h"
53*d57664e9SAndroid Build Coastguard Worker #include "io/FileSystem.h"
54*d57664e9SAndroid Build Coastguard Worker #include "io/Util.h"
55*d57664e9SAndroid Build Coastguard Worker #include "io/ZipArchive.h"
56*d57664e9SAndroid Build Coastguard Worker #include "java/JavaClassGenerator.h"
57*d57664e9SAndroid Build Coastguard Worker #include "java/ManifestClassGenerator.h"
58*d57664e9SAndroid Build Coastguard Worker #include "java/ProguardRules.h"
59*d57664e9SAndroid Build Coastguard Worker #include "link/FeatureFlagsFilter.h"
60*d57664e9SAndroid Build Coastguard Worker #include "link/FlagDisabledResourceRemover.h"
61*d57664e9SAndroid Build Coastguard Worker #include "link/Linkers.h"
62*d57664e9SAndroid Build Coastguard Worker #include "link/ManifestFixer.h"
63*d57664e9SAndroid Build Coastguard Worker #include "link/NoDefaultResourceRemover.h"
64*d57664e9SAndroid Build Coastguard Worker #include "link/ReferenceLinker.h"
65*d57664e9SAndroid Build Coastguard Worker #include "link/ResourceExcluder.h"
66*d57664e9SAndroid Build Coastguard Worker #include "link/TableMerger.h"
67*d57664e9SAndroid Build Coastguard Worker #include "link/XmlCompatVersioner.h"
68*d57664e9SAndroid Build Coastguard Worker #include "optimize/ResourceDeduper.h"
69*d57664e9SAndroid Build Coastguard Worker #include "optimize/VersionCollapser.h"
70*d57664e9SAndroid Build Coastguard Worker #include "process/IResourceTableConsumer.h"
71*d57664e9SAndroid Build Coastguard Worker #include "process/ProductFilter.h"
72*d57664e9SAndroid Build Coastguard Worker #include "process/SymbolTable.h"
73*d57664e9SAndroid Build Coastguard Worker #include "split/TableSplitter.h"
74*d57664e9SAndroid Build Coastguard Worker #include "trace/TraceBuffer.h"
75*d57664e9SAndroid Build Coastguard Worker #include "util/Files.h"
76*d57664e9SAndroid Build Coastguard Worker #include "xml/XmlDom.h"
77*d57664e9SAndroid Build Coastguard Worker
78*d57664e9SAndroid Build Coastguard Worker using ::android::ConfigDescription;
79*d57664e9SAndroid Build Coastguard Worker using ::android::FileInputStream;
80*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece;
81*d57664e9SAndroid Build Coastguard Worker using ::android::base::expected;
82*d57664e9SAndroid Build Coastguard Worker using ::android::base::StringPrintf;
83*d57664e9SAndroid Build Coastguard Worker using ::android::base::unexpected;
84*d57664e9SAndroid Build Coastguard Worker
85*d57664e9SAndroid Build Coastguard Worker namespace aapt {
86*d57664e9SAndroid Build Coastguard Worker
87*d57664e9SAndroid Build Coastguard Worker namespace {
88*d57664e9SAndroid Build Coastguard Worker
GetStaticLibraryPackage(ResourceTable * table)89*d57664e9SAndroid Build Coastguard Worker expected<ResourceTablePackage*, const char*> GetStaticLibraryPackage(ResourceTable* table) {
90*d57664e9SAndroid Build Coastguard Worker // Resource tables built by aapt2 always contain one package. This is a post condition of
91*d57664e9SAndroid Build Coastguard Worker // VerifyNoExternalPackages.
92*d57664e9SAndroid Build Coastguard Worker if (table->packages.size() != 1u) {
93*d57664e9SAndroid Build Coastguard Worker return unexpected("static library contains more than one package");
94*d57664e9SAndroid Build Coastguard Worker }
95*d57664e9SAndroid Build Coastguard Worker return table->packages.back().get();
96*d57664e9SAndroid Build Coastguard Worker }
97*d57664e9SAndroid Build Coastguard Worker
98*d57664e9SAndroid Build Coastguard Worker } // namespace
99*d57664e9SAndroid Build Coastguard Worker
100*d57664e9SAndroid Build Coastguard Worker constexpr uint8_t kAndroidPackageId = 0x01;
101*d57664e9SAndroid Build Coastguard Worker
102*d57664e9SAndroid Build Coastguard Worker class LinkContext : public IAaptContext {
103*d57664e9SAndroid Build Coastguard Worker public:
LinkContext(android::IDiagnostics * diagnostics)104*d57664e9SAndroid Build Coastguard Worker explicit LinkContext(android::IDiagnostics* diagnostics)
105*d57664e9SAndroid Build Coastguard Worker : diagnostics_(diagnostics), name_mangler_({}), symbols_(&name_mangler_) {
106*d57664e9SAndroid Build Coastguard Worker }
107*d57664e9SAndroid Build Coastguard Worker
GetPackageType()108*d57664e9SAndroid Build Coastguard Worker PackageType GetPackageType() override {
109*d57664e9SAndroid Build Coastguard Worker return package_type_;
110*d57664e9SAndroid Build Coastguard Worker }
111*d57664e9SAndroid Build Coastguard Worker
SetPackageType(PackageType type)112*d57664e9SAndroid Build Coastguard Worker void SetPackageType(PackageType type) {
113*d57664e9SAndroid Build Coastguard Worker package_type_ = type;
114*d57664e9SAndroid Build Coastguard Worker }
115*d57664e9SAndroid Build Coastguard Worker
GetDiagnostics()116*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* GetDiagnostics() override {
117*d57664e9SAndroid Build Coastguard Worker return diagnostics_;
118*d57664e9SAndroid Build Coastguard Worker }
119*d57664e9SAndroid Build Coastguard Worker
GetNameMangler()120*d57664e9SAndroid Build Coastguard Worker NameMangler* GetNameMangler() override {
121*d57664e9SAndroid Build Coastguard Worker return &name_mangler_;
122*d57664e9SAndroid Build Coastguard Worker }
123*d57664e9SAndroid Build Coastguard Worker
SetNameManglerPolicy(const NameManglerPolicy & policy)124*d57664e9SAndroid Build Coastguard Worker void SetNameManglerPolicy(const NameManglerPolicy& policy) {
125*d57664e9SAndroid Build Coastguard Worker name_mangler_ = NameMangler(policy);
126*d57664e9SAndroid Build Coastguard Worker }
127*d57664e9SAndroid Build Coastguard Worker
GetCompilationPackage()128*d57664e9SAndroid Build Coastguard Worker const std::string& GetCompilationPackage() override {
129*d57664e9SAndroid Build Coastguard Worker return compilation_package_;
130*d57664e9SAndroid Build Coastguard Worker }
131*d57664e9SAndroid Build Coastguard Worker
SetCompilationPackage(StringPiece package_name)132*d57664e9SAndroid Build Coastguard Worker void SetCompilationPackage(StringPiece package_name) {
133*d57664e9SAndroid Build Coastguard Worker compilation_package_ = std::string(package_name);
134*d57664e9SAndroid Build Coastguard Worker }
135*d57664e9SAndroid Build Coastguard Worker
GetPackageId()136*d57664e9SAndroid Build Coastguard Worker uint8_t GetPackageId() override {
137*d57664e9SAndroid Build Coastguard Worker return package_id_;
138*d57664e9SAndroid Build Coastguard Worker }
139*d57664e9SAndroid Build Coastguard Worker
SetPackageId(uint8_t id)140*d57664e9SAndroid Build Coastguard Worker void SetPackageId(uint8_t id) {
141*d57664e9SAndroid Build Coastguard Worker package_id_ = id;
142*d57664e9SAndroid Build Coastguard Worker }
143*d57664e9SAndroid Build Coastguard Worker
GetExternalSymbols()144*d57664e9SAndroid Build Coastguard Worker SymbolTable* GetExternalSymbols() override {
145*d57664e9SAndroid Build Coastguard Worker return &symbols_;
146*d57664e9SAndroid Build Coastguard Worker }
147*d57664e9SAndroid Build Coastguard Worker
IsVerbose()148*d57664e9SAndroid Build Coastguard Worker bool IsVerbose() override {
149*d57664e9SAndroid Build Coastguard Worker return verbose_;
150*d57664e9SAndroid Build Coastguard Worker }
151*d57664e9SAndroid Build Coastguard Worker
SetVerbose(bool val)152*d57664e9SAndroid Build Coastguard Worker void SetVerbose(bool val) {
153*d57664e9SAndroid Build Coastguard Worker verbose_ = val;
154*d57664e9SAndroid Build Coastguard Worker diagnostics_->SetVerbose(val);
155*d57664e9SAndroid Build Coastguard Worker }
156*d57664e9SAndroid Build Coastguard Worker
GetMinSdkVersion()157*d57664e9SAndroid Build Coastguard Worker int GetMinSdkVersion() override {
158*d57664e9SAndroid Build Coastguard Worker return min_sdk_version_;
159*d57664e9SAndroid Build Coastguard Worker }
160*d57664e9SAndroid Build Coastguard Worker
SetMinSdkVersion(int minSdk)161*d57664e9SAndroid Build Coastguard Worker void SetMinSdkVersion(int minSdk) {
162*d57664e9SAndroid Build Coastguard Worker min_sdk_version_ = minSdk;
163*d57664e9SAndroid Build Coastguard Worker }
164*d57664e9SAndroid Build Coastguard Worker
GetSplitNameDependencies()165*d57664e9SAndroid Build Coastguard Worker const std::set<std::string>& GetSplitNameDependencies() override {
166*d57664e9SAndroid Build Coastguard Worker return split_name_dependencies_;
167*d57664e9SAndroid Build Coastguard Worker }
168*d57664e9SAndroid Build Coastguard Worker
SetSplitNameDependencies(const std::set<std::string> & split_name_dependencies)169*d57664e9SAndroid Build Coastguard Worker void SetSplitNameDependencies(const std::set<std::string>& split_name_dependencies) {
170*d57664e9SAndroid Build Coastguard Worker split_name_dependencies_ = split_name_dependencies;
171*d57664e9SAndroid Build Coastguard Worker }
172*d57664e9SAndroid Build Coastguard Worker
173*d57664e9SAndroid Build Coastguard Worker private:
174*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(LinkContext);
175*d57664e9SAndroid Build Coastguard Worker
176*d57664e9SAndroid Build Coastguard Worker PackageType package_type_ = PackageType::kApp;
177*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diagnostics_;
178*d57664e9SAndroid Build Coastguard Worker NameMangler name_mangler_;
179*d57664e9SAndroid Build Coastguard Worker std::string compilation_package_;
180*d57664e9SAndroid Build Coastguard Worker uint8_t package_id_ = 0x0;
181*d57664e9SAndroid Build Coastguard Worker SymbolTable symbols_;
182*d57664e9SAndroid Build Coastguard Worker bool verbose_ = false;
183*d57664e9SAndroid Build Coastguard Worker int min_sdk_version_ = 0;
184*d57664e9SAndroid Build Coastguard Worker std::set<std::string> split_name_dependencies_;
185*d57664e9SAndroid Build Coastguard Worker };
186*d57664e9SAndroid Build Coastguard Worker
187*d57664e9SAndroid Build Coastguard Worker // A custom delegate that generates compatible pre-O IDs for use with feature splits.
188*d57664e9SAndroid Build Coastguard Worker // Feature splits use package IDs > 7f, which in Java (since Java doesn't have unsigned ints)
189*d57664e9SAndroid Build Coastguard Worker // is interpreted as a negative number. Some verification was wrongly assuming negative values
190*d57664e9SAndroid Build Coastguard Worker // were invalid.
191*d57664e9SAndroid Build Coastguard Worker //
192*d57664e9SAndroid Build Coastguard Worker // This delegate will attempt to masquerade any '@id/' references with ID 0xPPTTEEEE,
193*d57664e9SAndroid Build Coastguard Worker // where PP > 7f, as 0x7fPPEEEE. Any potential overlapping is verified and an error occurs if such
194*d57664e9SAndroid Build Coastguard Worker // an overlap exists.
195*d57664e9SAndroid Build Coastguard Worker //
196*d57664e9SAndroid Build Coastguard Worker // See b/37498913.
197*d57664e9SAndroid Build Coastguard Worker class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
198*d57664e9SAndroid Build Coastguard Worker public:
FeatureSplitSymbolTableDelegate(IAaptContext * context)199*d57664e9SAndroid Build Coastguard Worker explicit FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
200*d57664e9SAndroid Build Coastguard Worker }
201*d57664e9SAndroid Build Coastguard Worker
202*d57664e9SAndroid Build Coastguard Worker virtual ~FeatureSplitSymbolTableDelegate() = default;
203*d57664e9SAndroid Build Coastguard Worker
FindByName(const ResourceName & name,const std::vector<std::unique_ptr<ISymbolSource>> & sources)204*d57664e9SAndroid Build Coastguard Worker virtual std::unique_ptr<SymbolTable::Symbol> FindByName(
205*d57664e9SAndroid Build Coastguard Worker const ResourceName& name,
206*d57664e9SAndroid Build Coastguard Worker const std::vector<std::unique_ptr<ISymbolSource>>& sources) override {
207*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<SymbolTable::Symbol> symbol =
208*d57664e9SAndroid Build Coastguard Worker DefaultSymbolTableDelegate::FindByName(name, sources);
209*d57664e9SAndroid Build Coastguard Worker if (symbol == nullptr) {
210*d57664e9SAndroid Build Coastguard Worker return {};
211*d57664e9SAndroid Build Coastguard Worker }
212*d57664e9SAndroid Build Coastguard Worker
213*d57664e9SAndroid Build Coastguard Worker // Check to see if this is an 'id' with the target package.
214*d57664e9SAndroid Build Coastguard Worker if (name.type.type == ResourceType::kId && symbol->id) {
215*d57664e9SAndroid Build Coastguard Worker ResourceId* id = &symbol->id.value();
216*d57664e9SAndroid Build Coastguard Worker if (id->package_id() > kAppPackageId) {
217*d57664e9SAndroid Build Coastguard Worker // Rewrite the resource ID to be compatible pre-O.
218*d57664e9SAndroid Build Coastguard Worker ResourceId rewritten_id(kAppPackageId, id->package_id(), id->entry_id());
219*d57664e9SAndroid Build Coastguard Worker
220*d57664e9SAndroid Build Coastguard Worker // Check that this doesn't overlap another resource.
221*d57664e9SAndroid Build Coastguard Worker if (DefaultSymbolTableDelegate::FindById(rewritten_id, sources) != nullptr) {
222*d57664e9SAndroid Build Coastguard Worker // The ID overlaps, so log a message (since this is a weird failure) and fail.
223*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
224*d57664e9SAndroid Build Coastguard Worker << "Failed to rewrite " << name
225*d57664e9SAndroid Build Coastguard Worker << " for pre-O feature split support");
226*d57664e9SAndroid Build Coastguard Worker return {};
227*d57664e9SAndroid Build Coastguard Worker }
228*d57664e9SAndroid Build Coastguard Worker
229*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
230*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
231*d57664e9SAndroid Build Coastguard Worker << "rewriting " << name << " (" << *id << ") -> ("
232*d57664e9SAndroid Build Coastguard Worker << rewritten_id << ")");
233*d57664e9SAndroid Build Coastguard Worker }
234*d57664e9SAndroid Build Coastguard Worker
235*d57664e9SAndroid Build Coastguard Worker *id = rewritten_id;
236*d57664e9SAndroid Build Coastguard Worker }
237*d57664e9SAndroid Build Coastguard Worker }
238*d57664e9SAndroid Build Coastguard Worker return symbol;
239*d57664e9SAndroid Build Coastguard Worker }
240*d57664e9SAndroid Build Coastguard Worker
241*d57664e9SAndroid Build Coastguard Worker private:
242*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(FeatureSplitSymbolTableDelegate);
243*d57664e9SAndroid Build Coastguard Worker
244*d57664e9SAndroid Build Coastguard Worker IAaptContext* context_;
245*d57664e9SAndroid Build Coastguard Worker };
246*d57664e9SAndroid Build Coastguard Worker
FlattenXml(IAaptContext * context,const xml::XmlResource & xml_res,StringPiece path,bool keep_raw_values,bool utf16,OutputFormat format,IArchiveWriter * writer)247*d57664e9SAndroid Build Coastguard Worker static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml_res, StringPiece path,
248*d57664e9SAndroid Build Coastguard Worker bool keep_raw_values, bool utf16, OutputFormat format,
249*d57664e9SAndroid Build Coastguard Worker IArchiveWriter* writer) {
250*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
251*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
252*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage(path)
253*d57664e9SAndroid Build Coastguard Worker << "writing to archive (keep_raw_values="
254*d57664e9SAndroid Build Coastguard Worker << (keep_raw_values ? "true" : "false") << ")");
255*d57664e9SAndroid Build Coastguard Worker }
256*d57664e9SAndroid Build Coastguard Worker
257*d57664e9SAndroid Build Coastguard Worker switch (format) {
258*d57664e9SAndroid Build Coastguard Worker case OutputFormat::kApk: {
259*d57664e9SAndroid Build Coastguard Worker android::BigBuffer buffer(1024);
260*d57664e9SAndroid Build Coastguard Worker XmlFlattenerOptions options = {};
261*d57664e9SAndroid Build Coastguard Worker options.keep_raw_values = keep_raw_values;
262*d57664e9SAndroid Build Coastguard Worker options.use_utf16 = utf16;
263*d57664e9SAndroid Build Coastguard Worker XmlFlattener flattener(&buffer, options);
264*d57664e9SAndroid Build Coastguard Worker if (!flattener.Consume(context, &xml_res)) {
265*d57664e9SAndroid Build Coastguard Worker return false;
266*d57664e9SAndroid Build Coastguard Worker }
267*d57664e9SAndroid Build Coastguard Worker
268*d57664e9SAndroid Build Coastguard Worker android::BigBufferInputStream input_stream(&buffer);
269*d57664e9SAndroid Build Coastguard Worker return io::CopyInputStreamToArchive(context, &input_stream, path, ArchiveEntry::kCompress,
270*d57664e9SAndroid Build Coastguard Worker writer);
271*d57664e9SAndroid Build Coastguard Worker } break;
272*d57664e9SAndroid Build Coastguard Worker
273*d57664e9SAndroid Build Coastguard Worker case OutputFormat::kProto: {
274*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_node;
275*d57664e9SAndroid Build Coastguard Worker // Strip whitespace text nodes from tha AndroidManifest.xml
276*d57664e9SAndroid Build Coastguard Worker SerializeXmlOptions options;
277*d57664e9SAndroid Build Coastguard Worker options.remove_empty_text_nodes = (path == kAndroidManifestPath);
278*d57664e9SAndroid Build Coastguard Worker SerializeXmlResourceToPb(xml_res, &pb_node);
279*d57664e9SAndroid Build Coastguard Worker return io::CopyProtoToArchive(context, &pb_node, path, ArchiveEntry::kCompress, writer);
280*d57664e9SAndroid Build Coastguard Worker } break;
281*d57664e9SAndroid Build Coastguard Worker }
282*d57664e9SAndroid Build Coastguard Worker return false;
283*d57664e9SAndroid Build Coastguard Worker }
284*d57664e9SAndroid Build Coastguard Worker
285*d57664e9SAndroid Build Coastguard Worker // Inflates an XML file from the source path.
LoadXml(const std::string & path,android::IDiagnostics * diag)286*d57664e9SAndroid Build Coastguard Worker static std::unique_ptr<xml::XmlResource> LoadXml(const std::string& path,
287*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diag) {
288*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
289*d57664e9SAndroid Build Coastguard Worker android::FileInputStream fin(path);
290*d57664e9SAndroid Build Coastguard Worker if (fin.HadError()) {
291*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(path) << "failed to load XML file: " << fin.GetError());
292*d57664e9SAndroid Build Coastguard Worker return {};
293*d57664e9SAndroid Build Coastguard Worker }
294*d57664e9SAndroid Build Coastguard Worker return xml::Inflate(&fin, diag, android::Source(path));
295*d57664e9SAndroid Build Coastguard Worker }
296*d57664e9SAndroid Build Coastguard Worker
297*d57664e9SAndroid Build Coastguard Worker struct ResourceFileFlattenerOptions {
298*d57664e9SAndroid Build Coastguard Worker bool no_auto_version = false;
299*d57664e9SAndroid Build Coastguard Worker bool no_version_vectors = false;
300*d57664e9SAndroid Build Coastguard Worker bool no_version_transitions = false;
301*d57664e9SAndroid Build Coastguard Worker bool no_xml_namespaces = false;
302*d57664e9SAndroid Build Coastguard Worker bool keep_raw_values = false;
303*d57664e9SAndroid Build Coastguard Worker bool do_not_compress_anything = false;
304*d57664e9SAndroid Build Coastguard Worker bool update_proguard_spec = false;
305*d57664e9SAndroid Build Coastguard Worker bool do_not_fail_on_missing_resources = false;
306*d57664e9SAndroid Build Coastguard Worker OutputFormat output_format = OutputFormat::kApk;
307*d57664e9SAndroid Build Coastguard Worker std::unordered_set<std::string> extensions_to_not_compress;
308*d57664e9SAndroid Build Coastguard Worker std::optional<std::regex> regex_to_not_compress;
309*d57664e9SAndroid Build Coastguard Worker FeatureFlagValues feature_flag_values;
310*d57664e9SAndroid Build Coastguard Worker };
311*d57664e9SAndroid Build Coastguard Worker
312*d57664e9SAndroid Build Coastguard Worker // A sampling of public framework resource IDs.
313*d57664e9SAndroid Build Coastguard Worker struct R {
314*d57664e9SAndroid Build Coastguard Worker struct attr {
315*d57664e9SAndroid Build Coastguard Worker enum : uint32_t {
316*d57664e9SAndroid Build Coastguard Worker paddingLeft = 0x010100d6u,
317*d57664e9SAndroid Build Coastguard Worker paddingRight = 0x010100d8u,
318*d57664e9SAndroid Build Coastguard Worker paddingHorizontal = 0x0101053du,
319*d57664e9SAndroid Build Coastguard Worker
320*d57664e9SAndroid Build Coastguard Worker paddingTop = 0x010100d7u,
321*d57664e9SAndroid Build Coastguard Worker paddingBottom = 0x010100d9u,
322*d57664e9SAndroid Build Coastguard Worker paddingVertical = 0x0101053eu,
323*d57664e9SAndroid Build Coastguard Worker
324*d57664e9SAndroid Build Coastguard Worker layout_marginLeft = 0x010100f7u,
325*d57664e9SAndroid Build Coastguard Worker layout_marginRight = 0x010100f9u,
326*d57664e9SAndroid Build Coastguard Worker layout_marginHorizontal = 0x0101053bu,
327*d57664e9SAndroid Build Coastguard Worker
328*d57664e9SAndroid Build Coastguard Worker layout_marginTop = 0x010100f8u,
329*d57664e9SAndroid Build Coastguard Worker layout_marginBottom = 0x010100fau,
330*d57664e9SAndroid Build Coastguard Worker layout_marginVertical = 0x0101053cu,
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 template <typename T>
GetCompressionFlags(StringPiece str,T options)336*d57664e9SAndroid Build Coastguard Worker uint32_t GetCompressionFlags(StringPiece str, T options) {
337*d57664e9SAndroid Build Coastguard Worker if (options.do_not_compress_anything) {
338*d57664e9SAndroid Build Coastguard Worker return 0;
339*d57664e9SAndroid Build Coastguard Worker }
340*d57664e9SAndroid Build Coastguard Worker
341*d57664e9SAndroid Build Coastguard Worker if (options.regex_to_not_compress &&
342*d57664e9SAndroid Build Coastguard Worker std::regex_search(str.begin(), str.end(), options.regex_to_not_compress.value())) {
343*d57664e9SAndroid Build Coastguard Worker return 0;
344*d57664e9SAndroid Build Coastguard Worker }
345*d57664e9SAndroid Build Coastguard Worker
346*d57664e9SAndroid Build Coastguard Worker for (const std::string& extension : options.extensions_to_not_compress) {
347*d57664e9SAndroid Build Coastguard Worker if (util::EndsWith(str, extension)) {
348*d57664e9SAndroid Build Coastguard Worker return 0;
349*d57664e9SAndroid Build Coastguard Worker }
350*d57664e9SAndroid Build Coastguard Worker }
351*d57664e9SAndroid Build Coastguard Worker return ArchiveEntry::kCompress;
352*d57664e9SAndroid Build Coastguard Worker }
353*d57664e9SAndroid Build Coastguard Worker
354*d57664e9SAndroid Build Coastguard Worker class ResourceFileFlattener {
355*d57664e9SAndroid Build Coastguard Worker public:
356*d57664e9SAndroid Build Coastguard Worker ResourceFileFlattener(const ResourceFileFlattenerOptions& options, IAaptContext* context,
357*d57664e9SAndroid Build Coastguard Worker proguard::KeepSet* keep_set);
358*d57664e9SAndroid Build Coastguard Worker
359*d57664e9SAndroid Build Coastguard Worker bool Flatten(ResourceTable* table, IArchiveWriter* archive_writer);
360*d57664e9SAndroid Build Coastguard Worker
361*d57664e9SAndroid Build Coastguard Worker private:
362*d57664e9SAndroid Build Coastguard Worker struct FileOperation {
363*d57664e9SAndroid Build Coastguard Worker ConfigDescription config;
364*d57664e9SAndroid Build Coastguard Worker
365*d57664e9SAndroid Build Coastguard Worker // The entry this file came from.
366*d57664e9SAndroid Build Coastguard Worker ResourceEntry* entry;
367*d57664e9SAndroid Build Coastguard Worker
368*d57664e9SAndroid Build Coastguard Worker // The file to copy as-is.
369*d57664e9SAndroid Build Coastguard Worker io::IFile* file_to_copy;
370*d57664e9SAndroid Build Coastguard Worker
371*d57664e9SAndroid Build Coastguard Worker // The XML to process and flatten.
372*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> xml_to_flatten;
373*d57664e9SAndroid Build Coastguard Worker
374*d57664e9SAndroid Build Coastguard Worker // The destination to write this file to.
375*d57664e9SAndroid Build Coastguard Worker std::string dst_path;
376*d57664e9SAndroid Build Coastguard Worker };
377*d57664e9SAndroid Build Coastguard Worker
378*d57664e9SAndroid Build Coastguard Worker std::vector<std::unique_ptr<xml::XmlResource>> LinkAndVersionXmlFile(ResourceTable* table,
379*d57664e9SAndroid Build Coastguard Worker FileOperation* file_op);
380*d57664e9SAndroid Build Coastguard Worker
381*d57664e9SAndroid Build Coastguard Worker ResourceFileFlattenerOptions options_;
382*d57664e9SAndroid Build Coastguard Worker IAaptContext* context_;
383*d57664e9SAndroid Build Coastguard Worker proguard::KeepSet* keep_set_;
384*d57664e9SAndroid Build Coastguard Worker XmlCompatVersioner::Rules rules_;
385*d57664e9SAndroid Build Coastguard Worker };
386*d57664e9SAndroid Build Coastguard Worker
ResourceFileFlattener(const ResourceFileFlattenerOptions & options,IAaptContext * context,proguard::KeepSet * keep_set)387*d57664e9SAndroid Build Coastguard Worker ResourceFileFlattener::ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
388*d57664e9SAndroid Build Coastguard Worker IAaptContext* context, proguard::KeepSet* keep_set)
389*d57664e9SAndroid Build Coastguard Worker : options_(options), context_(context), keep_set_(keep_set) {
390*d57664e9SAndroid Build Coastguard Worker SymbolTable* symm = context_->GetExternalSymbols();
391*d57664e9SAndroid Build Coastguard Worker
392*d57664e9SAndroid Build Coastguard Worker // Build up the rules for degrading newer attributes to older ones.
393*d57664e9SAndroid Build Coastguard Worker // NOTE(adamlesinski): These rules are hardcoded right now, but they should be
394*d57664e9SAndroid Build Coastguard Worker // generated from the attribute definitions themselves (b/62028956).
395*d57664e9SAndroid Build Coastguard Worker if (symm->FindById(R::attr::paddingHorizontal)) {
396*d57664e9SAndroid Build Coastguard Worker std::vector<ReplacementAttr> replacements{
397*d57664e9SAndroid Build Coastguard Worker {"paddingLeft", R::attr::paddingLeft, Attribute(android::ResTable_map::TYPE_DIMENSION)},
398*d57664e9SAndroid Build Coastguard Worker {"paddingRight", R::attr::paddingRight, Attribute(android::ResTable_map::TYPE_DIMENSION)},
399*d57664e9SAndroid Build Coastguard Worker };
400*d57664e9SAndroid Build Coastguard Worker rules_[R::attr::paddingHorizontal] =
401*d57664e9SAndroid Build Coastguard Worker util::make_unique<DegradeToManyRule>(std::move(replacements));
402*d57664e9SAndroid Build Coastguard Worker }
403*d57664e9SAndroid Build Coastguard Worker
404*d57664e9SAndroid Build Coastguard Worker if (symm->FindById(R::attr::paddingVertical)) {
405*d57664e9SAndroid Build Coastguard Worker std::vector<ReplacementAttr> replacements{
406*d57664e9SAndroid Build Coastguard Worker {"paddingTop", R::attr::paddingTop, Attribute(android::ResTable_map::TYPE_DIMENSION)},
407*d57664e9SAndroid Build Coastguard Worker {"paddingBottom", R::attr::paddingBottom, Attribute(android::ResTable_map::TYPE_DIMENSION)},
408*d57664e9SAndroid Build Coastguard Worker };
409*d57664e9SAndroid Build Coastguard Worker rules_[R::attr::paddingVertical] =
410*d57664e9SAndroid Build Coastguard Worker util::make_unique<DegradeToManyRule>(std::move(replacements));
411*d57664e9SAndroid Build Coastguard Worker }
412*d57664e9SAndroid Build Coastguard Worker
413*d57664e9SAndroid Build Coastguard Worker if (symm->FindById(R::attr::layout_marginHorizontal)) {
414*d57664e9SAndroid Build Coastguard Worker std::vector<ReplacementAttr> replacements{
415*d57664e9SAndroid Build Coastguard Worker {"layout_marginLeft", R::attr::layout_marginLeft,
416*d57664e9SAndroid Build Coastguard Worker Attribute(android::ResTable_map::TYPE_DIMENSION)},
417*d57664e9SAndroid Build Coastguard Worker {"layout_marginRight", R::attr::layout_marginRight,
418*d57664e9SAndroid Build Coastguard Worker Attribute(android::ResTable_map::TYPE_DIMENSION)},
419*d57664e9SAndroid Build Coastguard Worker };
420*d57664e9SAndroid Build Coastguard Worker rules_[R::attr::layout_marginHorizontal] =
421*d57664e9SAndroid Build Coastguard Worker util::make_unique<DegradeToManyRule>(std::move(replacements));
422*d57664e9SAndroid Build Coastguard Worker }
423*d57664e9SAndroid Build Coastguard Worker
424*d57664e9SAndroid Build Coastguard Worker if (symm->FindById(R::attr::layout_marginVertical)) {
425*d57664e9SAndroid Build Coastguard Worker std::vector<ReplacementAttr> replacements{
426*d57664e9SAndroid Build Coastguard Worker {"layout_marginTop", R::attr::layout_marginTop,
427*d57664e9SAndroid Build Coastguard Worker Attribute(android::ResTable_map::TYPE_DIMENSION)},
428*d57664e9SAndroid Build Coastguard Worker {"layout_marginBottom", R::attr::layout_marginBottom,
429*d57664e9SAndroid Build Coastguard Worker Attribute(android::ResTable_map::TYPE_DIMENSION)},
430*d57664e9SAndroid Build Coastguard Worker };
431*d57664e9SAndroid Build Coastguard Worker rules_[R::attr::layout_marginVertical] =
432*d57664e9SAndroid Build Coastguard Worker util::make_unique<DegradeToManyRule>(std::move(replacements));
433*d57664e9SAndroid Build Coastguard Worker }
434*d57664e9SAndroid Build Coastguard Worker }
435*d57664e9SAndroid Build Coastguard Worker
IsTransitionElement(const std::string & name)436*d57664e9SAndroid Build Coastguard Worker static bool IsTransitionElement(const std::string& name) {
437*d57664e9SAndroid Build Coastguard Worker return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" ||
438*d57664e9SAndroid Build Coastguard Worker name == "changeImageTransform" || name == "changeTransform" ||
439*d57664e9SAndroid Build Coastguard Worker name == "changeClipBounds" || name == "autoTransition" || name == "recolor" ||
440*d57664e9SAndroid Build Coastguard Worker name == "changeScroll" || name == "transitionSet" || name == "transition" ||
441*d57664e9SAndroid Build Coastguard Worker name == "transitionManager";
442*d57664e9SAndroid Build Coastguard Worker }
443*d57664e9SAndroid Build Coastguard Worker
IsVectorElement(const std::string & name)444*d57664e9SAndroid Build Coastguard Worker static bool IsVectorElement(const std::string& name) {
445*d57664e9SAndroid Build Coastguard Worker return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
446*d57664e9SAndroid Build Coastguard Worker name == "objectAnimator" || name == "gradient" || name == "animated-selector" ||
447*d57664e9SAndroid Build Coastguard Worker name == "set";
448*d57664e9SAndroid Build Coastguard Worker }
449*d57664e9SAndroid Build Coastguard Worker
450*d57664e9SAndroid Build Coastguard Worker template <typename T>
make_singleton_vec(T && val)451*d57664e9SAndroid Build Coastguard Worker std::vector<T> make_singleton_vec(T&& val) {
452*d57664e9SAndroid Build Coastguard Worker std::vector<T> vec;
453*d57664e9SAndroid Build Coastguard Worker vec.emplace_back(std::forward<T>(val));
454*d57664e9SAndroid Build Coastguard Worker return vec;
455*d57664e9SAndroid Build Coastguard Worker }
456*d57664e9SAndroid Build Coastguard Worker
LinkAndVersionXmlFile(ResourceTable * table,FileOperation * file_op)457*d57664e9SAndroid Build Coastguard Worker std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVersionXmlFile(
458*d57664e9SAndroid Build Coastguard Worker ResourceTable* table, FileOperation* file_op) {
459*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
460*d57664e9SAndroid Build Coastguard Worker xml::XmlResource* doc = file_op->xml_to_flatten.get();
461*d57664e9SAndroid Build Coastguard Worker const android::Source& src = doc->file.source;
462*d57664e9SAndroid Build Coastguard Worker
463*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
464*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
465*d57664e9SAndroid Build Coastguard Worker << "linking " << src.path << " (" << doc->file.name << ")");
466*d57664e9SAndroid Build Coastguard Worker }
467*d57664e9SAndroid Build Coastguard Worker
468*d57664e9SAndroid Build Coastguard Worker // First, strip out any tools namespace attributes. AAPT stripped them out early, which means
469*d57664e9SAndroid Build Coastguard Worker // that existing projects have out-of-date references which pass compilation.
470*d57664e9SAndroid Build Coastguard Worker xml::StripAndroidStudioAttributes(doc->root.get());
471*d57664e9SAndroid Build Coastguard Worker
472*d57664e9SAndroid Build Coastguard Worker XmlReferenceLinker xml_linker(table);
473*d57664e9SAndroid Build Coastguard Worker if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
474*d57664e9SAndroid Build Coastguard Worker return {};
475*d57664e9SAndroid Build Coastguard Worker }
476*d57664e9SAndroid Build Coastguard Worker
477*d57664e9SAndroid Build Coastguard Worker if (options_.update_proguard_spec && !proguard::CollectProguardRules(context_, doc, keep_set_)) {
478*d57664e9SAndroid Build Coastguard Worker return {};
479*d57664e9SAndroid Build Coastguard Worker }
480*d57664e9SAndroid Build Coastguard Worker
481*d57664e9SAndroid Build Coastguard Worker if (options_.no_xml_namespaces) {
482*d57664e9SAndroid Build Coastguard Worker XmlNamespaceRemover namespace_remover;
483*d57664e9SAndroid Build Coastguard Worker if (!namespace_remover.Consume(context_, doc)) {
484*d57664e9SAndroid Build Coastguard Worker return {};
485*d57664e9SAndroid Build Coastguard Worker }
486*d57664e9SAndroid Build Coastguard Worker }
487*d57664e9SAndroid Build Coastguard Worker
488*d57664e9SAndroid Build Coastguard Worker if (options_.no_auto_version) {
489*d57664e9SAndroid Build Coastguard Worker return make_singleton_vec(std::move(file_op->xml_to_flatten));
490*d57664e9SAndroid Build Coastguard Worker }
491*d57664e9SAndroid Build Coastguard Worker
492*d57664e9SAndroid Build Coastguard Worker if (options_.no_version_vectors || options_.no_version_transitions) {
493*d57664e9SAndroid Build Coastguard Worker // Skip this if it is a vector or animated-vector.
494*d57664e9SAndroid Build Coastguard Worker xml::Element* el = doc->root.get();
495*d57664e9SAndroid Build Coastguard Worker if (el && el->namespace_uri.empty()) {
496*d57664e9SAndroid Build Coastguard Worker if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
497*d57664e9SAndroid Build Coastguard Worker (options_.no_version_transitions && IsTransitionElement(el->name))) {
498*d57664e9SAndroid Build Coastguard Worker return make_singleton_vec(std::move(file_op->xml_to_flatten));
499*d57664e9SAndroid Build Coastguard Worker }
500*d57664e9SAndroid Build Coastguard Worker }
501*d57664e9SAndroid Build Coastguard Worker }
502*d57664e9SAndroid Build Coastguard Worker
503*d57664e9SAndroid Build Coastguard Worker const ConfigDescription& config = file_op->config;
504*d57664e9SAndroid Build Coastguard Worker ResourceEntry* entry = file_op->entry;
505*d57664e9SAndroid Build Coastguard Worker
506*d57664e9SAndroid Build Coastguard Worker XmlCompatVersioner xml_compat_versioner(&rules_);
507*d57664e9SAndroid Build Coastguard Worker const util::Range<ApiVersion> api_range{config.sdkVersion,
508*d57664e9SAndroid Build Coastguard Worker FindNextApiVersionForConfig(entry, config)};
509*d57664e9SAndroid Build Coastguard Worker return xml_compat_versioner.Process(context_, doc, api_range);
510*d57664e9SAndroid Build Coastguard Worker }
511*d57664e9SAndroid Build Coastguard Worker
XmlFileTypeForOutputFormat(OutputFormat format)512*d57664e9SAndroid Build Coastguard Worker ResourceFile::Type XmlFileTypeForOutputFormat(OutputFormat format) {
513*d57664e9SAndroid Build Coastguard Worker switch (format) {
514*d57664e9SAndroid Build Coastguard Worker case OutputFormat::kApk:
515*d57664e9SAndroid Build Coastguard Worker return ResourceFile::Type::kBinaryXml;
516*d57664e9SAndroid Build Coastguard Worker case OutputFormat::kProto:
517*d57664e9SAndroid Build Coastguard Worker return ResourceFile::Type::kProtoXml;
518*d57664e9SAndroid Build Coastguard Worker }
519*d57664e9SAndroid Build Coastguard Worker LOG_ALWAYS_FATAL("unreachable");
520*d57664e9SAndroid Build Coastguard Worker return ResourceFile::Type::kUnknown;
521*d57664e9SAndroid Build Coastguard Worker }
522*d57664e9SAndroid Build Coastguard Worker
523*d57664e9SAndroid Build Coastguard Worker static auto kDrawableVersions = std::map<std::string, ApiVersion>{
524*d57664e9SAndroid Build Coastguard Worker { "adaptive-icon" , SDK_O },
525*d57664e9SAndroid Build Coastguard Worker };
526*d57664e9SAndroid Build Coastguard Worker
Flatten(ResourceTable * table,IArchiveWriter * archive_writer)527*d57664e9SAndroid Build Coastguard Worker bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archive_writer) {
528*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
529*d57664e9SAndroid Build Coastguard Worker bool error = false;
530*d57664e9SAndroid Build Coastguard Worker std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
531*d57664e9SAndroid Build Coastguard Worker
532*d57664e9SAndroid Build Coastguard Worker proguard::CollectResourceReferences(context_, table, keep_set_);
533*d57664e9SAndroid Build Coastguard Worker
534*d57664e9SAndroid Build Coastguard Worker for (auto& pkg : table->packages) {
535*d57664e9SAndroid Build Coastguard Worker CHECK(!pkg->name.empty()) << "Packages must have names when being linked";
536*d57664e9SAndroid Build Coastguard Worker
537*d57664e9SAndroid Build Coastguard Worker for (auto& type : pkg->types) {
538*d57664e9SAndroid Build Coastguard Worker // Sort by config and name, so that we get better locality in the zip file.
539*d57664e9SAndroid Build Coastguard Worker config_sorted_files.clear();
540*d57664e9SAndroid Build Coastguard Worker std::queue<FileOperation> file_operations;
541*d57664e9SAndroid Build Coastguard Worker
542*d57664e9SAndroid Build Coastguard Worker // Populate the queue with all files in the ResourceTable.
543*d57664e9SAndroid Build Coastguard Worker for (auto& entry : type->entries) {
544*d57664e9SAndroid Build Coastguard Worker for (auto& config_value : entry->values) {
545*d57664e9SAndroid Build Coastguard Worker // WARNING! Do not insert or remove any resources while executing in this scope. It will
546*d57664e9SAndroid Build Coastguard Worker // corrupt the iteration order.
547*d57664e9SAndroid Build Coastguard Worker
548*d57664e9SAndroid Build Coastguard Worker FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
549*d57664e9SAndroid Build Coastguard Worker if (!file_ref) {
550*d57664e9SAndroid Build Coastguard Worker continue;
551*d57664e9SAndroid Build Coastguard Worker }
552*d57664e9SAndroid Build Coastguard Worker
553*d57664e9SAndroid Build Coastguard Worker io::IFile* file = file_ref->file;
554*d57664e9SAndroid Build Coastguard Worker if (!file) {
555*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file_ref->GetSource())
556*d57664e9SAndroid Build Coastguard Worker << "file not found");
557*d57664e9SAndroid Build Coastguard Worker return false;
558*d57664e9SAndroid Build Coastguard Worker }
559*d57664e9SAndroid Build Coastguard Worker
560*d57664e9SAndroid Build Coastguard Worker FileOperation file_op;
561*d57664e9SAndroid Build Coastguard Worker file_op.entry = entry.get();
562*d57664e9SAndroid Build Coastguard Worker file_op.dst_path = *file_ref->path;
563*d57664e9SAndroid Build Coastguard Worker file_op.config = config_value->config;
564*d57664e9SAndroid Build Coastguard Worker file_op.file_to_copy = file;
565*d57664e9SAndroid Build Coastguard Worker
566*d57664e9SAndroid Build Coastguard Worker if (type->named_type.type != ResourceType::kRaw &&
567*d57664e9SAndroid Build Coastguard Worker (file_ref->type == ResourceFile::Type::kBinaryXml ||
568*d57664e9SAndroid Build Coastguard Worker file_ref->type == ResourceFile::Type::kProtoXml)) {
569*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IData> data = file->OpenAsData();
570*d57664e9SAndroid Build Coastguard Worker if (!data) {
571*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
572*d57664e9SAndroid Build Coastguard Worker << "failed to open file");
573*d57664e9SAndroid Build Coastguard Worker return false;
574*d57664e9SAndroid Build Coastguard Worker }
575*d57664e9SAndroid Build Coastguard Worker
576*d57664e9SAndroid Build Coastguard Worker if (file_ref->type == ResourceFile::Type::kProtoXml) {
577*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_xml_node;
578*d57664e9SAndroid Build Coastguard Worker if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) {
579*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
580*d57664e9SAndroid Build Coastguard Worker << "failed to parse proto XML");
581*d57664e9SAndroid Build Coastguard Worker return false;
582*d57664e9SAndroid Build Coastguard Worker }
583*d57664e9SAndroid Build Coastguard Worker
584*d57664e9SAndroid Build Coastguard Worker std::string error;
585*d57664e9SAndroid Build Coastguard Worker file_op.xml_to_flatten = DeserializeXmlResourceFromPb(pb_xml_node, &error);
586*d57664e9SAndroid Build Coastguard Worker if (file_op.xml_to_flatten == nullptr) {
587*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
588*d57664e9SAndroid Build Coastguard Worker << "failed to deserialize proto XML: " << error);
589*d57664e9SAndroid Build Coastguard Worker return false;
590*d57664e9SAndroid Build Coastguard Worker }
591*d57664e9SAndroid Build Coastguard Worker } else {
592*d57664e9SAndroid Build Coastguard Worker std::string error_str;
593*d57664e9SAndroid Build Coastguard Worker file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), &error_str);
594*d57664e9SAndroid Build Coastguard Worker if (file_op.xml_to_flatten == nullptr) {
595*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
596*d57664e9SAndroid Build Coastguard Worker << "failed to parse binary XML: " << error_str);
597*d57664e9SAndroid Build Coastguard Worker return false;
598*d57664e9SAndroid Build Coastguard Worker }
599*d57664e9SAndroid Build Coastguard Worker }
600*d57664e9SAndroid Build Coastguard Worker
601*d57664e9SAndroid Build Coastguard Worker // Update the type that this file will be written as.
602*d57664e9SAndroid Build Coastguard Worker file_ref->type = XmlFileTypeForOutputFormat(options_.output_format);
603*d57664e9SAndroid Build Coastguard Worker
604*d57664e9SAndroid Build Coastguard Worker file_op.xml_to_flatten->file.config = config_value->config;
605*d57664e9SAndroid Build Coastguard Worker file_op.xml_to_flatten->file.source = file_ref->GetSource();
606*d57664e9SAndroid Build Coastguard Worker file_op.xml_to_flatten->file.name =
607*d57664e9SAndroid Build Coastguard Worker ResourceName(pkg->name, type->named_type, entry->name);
608*d57664e9SAndroid Build Coastguard Worker }
609*d57664e9SAndroid Build Coastguard Worker
610*d57664e9SAndroid Build Coastguard Worker // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
611*d57664e9SAndroid Build Coastguard Worker // else we end up copying the string in the std::make_pair() method,
612*d57664e9SAndroid Build Coastguard Worker // then creating a StringPiece from the copy, which would cause us
613*d57664e9SAndroid Build Coastguard Worker // to end up referencing garbage in the map.
614*d57664e9SAndroid Build Coastguard Worker const StringPiece entry_name(entry->name);
615*d57664e9SAndroid Build Coastguard Worker config_sorted_files[std::make_pair(config_value->config, entry_name)] =
616*d57664e9SAndroid Build Coastguard Worker std::move(file_op);
617*d57664e9SAndroid Build Coastguard Worker }
618*d57664e9SAndroid Build Coastguard Worker }
619*d57664e9SAndroid Build Coastguard Worker
620*d57664e9SAndroid Build Coastguard Worker // Now flatten the sorted values.
621*d57664e9SAndroid Build Coastguard Worker for (auto& map_entry : config_sorted_files) {
622*d57664e9SAndroid Build Coastguard Worker const ConfigDescription& config = map_entry.first.first;
623*d57664e9SAndroid Build Coastguard Worker FileOperation& file_op = map_entry.second;
624*d57664e9SAndroid Build Coastguard Worker
625*d57664e9SAndroid Build Coastguard Worker if (file_op.xml_to_flatten) {
626*d57664e9SAndroid Build Coastguard Worker // Check minimum sdk versions supported for drawables
627*d57664e9SAndroid Build Coastguard Worker auto drawable_entry = kDrawableVersions.find(file_op.xml_to_flatten->root->name);
628*d57664e9SAndroid Build Coastguard Worker if (drawable_entry != kDrawableVersions.end()) {
629*d57664e9SAndroid Build Coastguard Worker if (drawable_entry->second > context_->GetMinSdkVersion()
630*d57664e9SAndroid Build Coastguard Worker && drawable_entry->second > config.sdkVersion) {
631*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
632*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(file_op.xml_to_flatten->file.source)
633*d57664e9SAndroid Build Coastguard Worker << "<" << drawable_entry->first << "> elements "
634*d57664e9SAndroid Build Coastguard Worker << "require a sdk version of at least " << (int16_t)drawable_entry->second);
635*d57664e9SAndroid Build Coastguard Worker error = true;
636*d57664e9SAndroid Build Coastguard Worker continue;
637*d57664e9SAndroid Build Coastguard Worker }
638*d57664e9SAndroid Build Coastguard Worker }
639*d57664e9SAndroid Build Coastguard Worker
640*d57664e9SAndroid Build Coastguard Worker std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
641*d57664e9SAndroid Build Coastguard Worker LinkAndVersionXmlFile(table, &file_op);
642*d57664e9SAndroid Build Coastguard Worker if (versioned_docs.empty()) {
643*d57664e9SAndroid Build Coastguard Worker error = true;
644*d57664e9SAndroid Build Coastguard Worker continue;
645*d57664e9SAndroid Build Coastguard Worker }
646*d57664e9SAndroid Build Coastguard Worker
647*d57664e9SAndroid Build Coastguard Worker for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) {
648*d57664e9SAndroid Build Coastguard Worker std::string dst_path = file_op.dst_path;
649*d57664e9SAndroid Build Coastguard Worker if (doc->file.config != file_op.config) {
650*d57664e9SAndroid Build Coastguard Worker // Only add the new versioned configurations.
651*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
652*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage(doc->file.source)
653*d57664e9SAndroid Build Coastguard Worker << "auto-versioning resource from config '"
654*d57664e9SAndroid Build Coastguard Worker << config << "' -> '" << doc->file.config << "'");
655*d57664e9SAndroid Build Coastguard Worker }
656*d57664e9SAndroid Build Coastguard Worker
657*d57664e9SAndroid Build Coastguard Worker const ResourceFile& file = doc->file;
658*d57664e9SAndroid Build Coastguard Worker dst_path = ResourceUtils::BuildResourceFileName(file, context_->GetNameMangler());
659*d57664e9SAndroid Build Coastguard Worker
660*d57664e9SAndroid Build Coastguard Worker auto file_ref =
661*d57664e9SAndroid Build Coastguard Worker util::make_unique<FileReference>(table->string_pool.MakeRef(dst_path));
662*d57664e9SAndroid Build Coastguard Worker file_ref->SetSource(doc->file.source);
663*d57664e9SAndroid Build Coastguard Worker
664*d57664e9SAndroid Build Coastguard Worker // Update the output format of this XML file.
665*d57664e9SAndroid Build Coastguard Worker file_ref->type = XmlFileTypeForOutputFormat(options_.output_format);
666*d57664e9SAndroid Build Coastguard Worker bool result = table->AddResource(NewResourceBuilder(file.name)
667*d57664e9SAndroid Build Coastguard Worker .SetValue(std::move(file_ref), file.config)
668*d57664e9SAndroid Build Coastguard Worker .SetAllowMangled(true)
669*d57664e9SAndroid Build Coastguard Worker .Build(),
670*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics());
671*d57664e9SAndroid Build Coastguard Worker if (!result) {
672*d57664e9SAndroid Build Coastguard Worker return false;
673*d57664e9SAndroid Build Coastguard Worker }
674*d57664e9SAndroid Build Coastguard Worker }
675*d57664e9SAndroid Build Coastguard Worker
676*d57664e9SAndroid Build Coastguard Worker FeatureFlagsFilterOptions flags_filter_options;
677*d57664e9SAndroid Build Coastguard Worker flags_filter_options.flags_must_be_readonly = true;
678*d57664e9SAndroid Build Coastguard Worker FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options);
679*d57664e9SAndroid Build Coastguard Worker if (!flags_filter.Consume(context_, doc.get())) {
680*d57664e9SAndroid Build Coastguard Worker return 1;
681*d57664e9SAndroid Build Coastguard Worker }
682*d57664e9SAndroid Build Coastguard Worker
683*d57664e9SAndroid Build Coastguard Worker error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values,
684*d57664e9SAndroid Build Coastguard Worker false /*utf16*/, options_.output_format, archive_writer);
685*d57664e9SAndroid Build Coastguard Worker }
686*d57664e9SAndroid Build Coastguard Worker } else {
687*d57664e9SAndroid Build Coastguard Worker error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
688*d57664e9SAndroid Build Coastguard Worker GetCompressionFlags(file_op.dst_path, options_),
689*d57664e9SAndroid Build Coastguard Worker archive_writer);
690*d57664e9SAndroid Build Coastguard Worker }
691*d57664e9SAndroid Build Coastguard Worker }
692*d57664e9SAndroid Build Coastguard Worker }
693*d57664e9SAndroid Build Coastguard Worker }
694*d57664e9SAndroid Build Coastguard Worker return !error;
695*d57664e9SAndroid Build Coastguard Worker }
696*d57664e9SAndroid Build Coastguard Worker
WriteStableIdMapToPath(android::IDiagnostics * diag,const std::unordered_map<ResourceName,ResourceId> & id_map,const std::string & id_map_path)697*d57664e9SAndroid Build Coastguard Worker static bool WriteStableIdMapToPath(android::IDiagnostics* diag,
698*d57664e9SAndroid Build Coastguard Worker const std::unordered_map<ResourceName, ResourceId>& id_map,
699*d57664e9SAndroid Build Coastguard Worker const std::string& id_map_path) {
700*d57664e9SAndroid Build Coastguard Worker android::FileOutputStream fout(id_map_path);
701*d57664e9SAndroid Build Coastguard Worker if (fout.HadError()) {
702*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(id_map_path) << "failed to open: " << fout.GetError());
703*d57664e9SAndroid Build Coastguard Worker return false;
704*d57664e9SAndroid Build Coastguard Worker }
705*d57664e9SAndroid Build Coastguard Worker
706*d57664e9SAndroid Build Coastguard Worker text::Printer printer(&fout);
707*d57664e9SAndroid Build Coastguard Worker for (const auto& entry : id_map) {
708*d57664e9SAndroid Build Coastguard Worker const ResourceName& name = entry.first;
709*d57664e9SAndroid Build Coastguard Worker const ResourceId& id = entry.second;
710*d57664e9SAndroid Build Coastguard Worker printer.Print(name.to_string());
711*d57664e9SAndroid Build Coastguard Worker printer.Print(" = ");
712*d57664e9SAndroid Build Coastguard Worker printer.Println(id.to_string());
713*d57664e9SAndroid Build Coastguard Worker }
714*d57664e9SAndroid Build Coastguard Worker fout.Flush();
715*d57664e9SAndroid Build Coastguard Worker
716*d57664e9SAndroid Build Coastguard Worker if (fout.HadError()) {
717*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(id_map_path) << "failed writing to file: " << fout.GetError());
718*d57664e9SAndroid Build Coastguard Worker return false;
719*d57664e9SAndroid Build Coastguard Worker }
720*d57664e9SAndroid Build Coastguard Worker return true;
721*d57664e9SAndroid Build Coastguard Worker }
722*d57664e9SAndroid Build Coastguard Worker
LoadStableIdMap(android::IDiagnostics * diag,const std::string & path,std::unordered_map<ResourceName,ResourceId> * out_id_map)723*d57664e9SAndroid Build Coastguard Worker static bool LoadStableIdMap(android::IDiagnostics* diag, const std::string& path,
724*d57664e9SAndroid Build Coastguard Worker std::unordered_map<ResourceName, ResourceId>* out_id_map) {
725*d57664e9SAndroid Build Coastguard Worker std::string content;
726*d57664e9SAndroid Build Coastguard Worker if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
727*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(path) << "failed reading stable ID file");
728*d57664e9SAndroid Build Coastguard Worker return false;
729*d57664e9SAndroid Build Coastguard Worker }
730*d57664e9SAndroid Build Coastguard Worker
731*d57664e9SAndroid Build Coastguard Worker out_id_map->clear();
732*d57664e9SAndroid Build Coastguard Worker size_t line_no = 0;
733*d57664e9SAndroid Build Coastguard Worker for (StringPiece line : util::Tokenize(content, '\n')) {
734*d57664e9SAndroid Build Coastguard Worker line_no++;
735*d57664e9SAndroid Build Coastguard Worker line = util::TrimWhitespace(line);
736*d57664e9SAndroid Build Coastguard Worker if (line.empty()) {
737*d57664e9SAndroid Build Coastguard Worker continue;
738*d57664e9SAndroid Build Coastguard Worker }
739*d57664e9SAndroid Build Coastguard Worker
740*d57664e9SAndroid Build Coastguard Worker auto iter = std::find(line.begin(), line.end(), '=');
741*d57664e9SAndroid Build Coastguard Worker if (iter == line.end()) {
742*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(android::Source(path, line_no)) << "missing '='");
743*d57664e9SAndroid Build Coastguard Worker return false;
744*d57664e9SAndroid Build Coastguard Worker }
745*d57664e9SAndroid Build Coastguard Worker
746*d57664e9SAndroid Build Coastguard Worker ResourceNameRef name;
747*d57664e9SAndroid Build Coastguard Worker StringPiece res_name_str =
748*d57664e9SAndroid Build Coastguard Worker util::TrimWhitespace(line.substr(0, std::distance(line.begin(), iter)));
749*d57664e9SAndroid Build Coastguard Worker if (!ResourceUtils::ParseResourceName(res_name_str, &name)) {
750*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(android::Source(path, line_no))
751*d57664e9SAndroid Build Coastguard Worker << "invalid resource name '" << res_name_str << "'");
752*d57664e9SAndroid Build Coastguard Worker return false;
753*d57664e9SAndroid Build Coastguard Worker }
754*d57664e9SAndroid Build Coastguard Worker
755*d57664e9SAndroid Build Coastguard Worker const size_t res_id_start_idx = std::distance(line.begin(), iter) + 1;
756*d57664e9SAndroid Build Coastguard Worker const size_t res_id_str_len = line.size() - res_id_start_idx;
757*d57664e9SAndroid Build Coastguard Worker StringPiece res_id_str = util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len));
758*d57664e9SAndroid Build Coastguard Worker
759*d57664e9SAndroid Build Coastguard Worker std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
760*d57664e9SAndroid Build Coastguard Worker if (!maybe_id) {
761*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(android::Source(path, line_no))
762*d57664e9SAndroid Build Coastguard Worker << "invalid resource ID '" << res_id_str << "'");
763*d57664e9SAndroid Build Coastguard Worker return false;
764*d57664e9SAndroid Build Coastguard Worker }
765*d57664e9SAndroid Build Coastguard Worker
766*d57664e9SAndroid Build Coastguard Worker (*out_id_map)[name.ToResourceName()] = maybe_id.value();
767*d57664e9SAndroid Build Coastguard Worker }
768*d57664e9SAndroid Build Coastguard Worker return true;
769*d57664e9SAndroid Build Coastguard Worker }
770*d57664e9SAndroid Build Coastguard Worker
771*d57664e9SAndroid Build Coastguard Worker class Linker {
772*d57664e9SAndroid Build Coastguard Worker public:
Linker(LinkContext * context,const LinkOptions & options)773*d57664e9SAndroid Build Coastguard Worker Linker(LinkContext* context, const LinkOptions& options)
774*d57664e9SAndroid Build Coastguard Worker : options_(options),
775*d57664e9SAndroid Build Coastguard Worker context_(context),
776*d57664e9SAndroid Build Coastguard Worker final_table_(),
777*d57664e9SAndroid Build Coastguard Worker file_collection_(util::make_unique<io::FileCollection>()) {
778*d57664e9SAndroid Build Coastguard Worker }
779*d57664e9SAndroid Build Coastguard Worker
ExtractCompileSdkVersions(android::AssetManager2 * assets)780*d57664e9SAndroid Build Coastguard Worker void ExtractCompileSdkVersions(android::AssetManager2* assets) {
781*d57664e9SAndroid Build Coastguard Worker using namespace android;
782*d57664e9SAndroid Build Coastguard Worker
783*d57664e9SAndroid Build Coastguard Worker // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
784*d57664e9SAndroid Build Coastguard Worker // we're looking for the first attribute resource in the system package.
785*d57664e9SAndroid Build Coastguard Worker android::ApkAssetsCookie cookie;
786*d57664e9SAndroid Build Coastguard Worker if (auto value = assets->GetResource(0x01010000, true /** may_be_bag */); value.has_value()) {
787*d57664e9SAndroid Build Coastguard Worker cookie = value->cookie;
788*d57664e9SAndroid Build Coastguard Worker } else {
789*d57664e9SAndroid Build Coastguard Worker // No Framework assets loaded. Not a failure.
790*d57664e9SAndroid Build Coastguard Worker return;
791*d57664e9SAndroid Build Coastguard Worker }
792*d57664e9SAndroid Build Coastguard Worker
793*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<Asset> manifest(
794*d57664e9SAndroid Build Coastguard Worker assets->OpenNonAsset(kAndroidManifestPath, cookie, Asset::AccessMode::ACCESS_BUFFER));
795*d57664e9SAndroid Build Coastguard Worker if (manifest == nullptr) {
796*d57664e9SAndroid Build Coastguard Worker // No errors.
797*d57664e9SAndroid Build Coastguard Worker return;
798*d57664e9SAndroid Build Coastguard Worker }
799*d57664e9SAndroid Build Coastguard Worker
800*d57664e9SAndroid Build Coastguard Worker std::string error;
801*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> manifest_xml =
802*d57664e9SAndroid Build Coastguard Worker xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error);
803*d57664e9SAndroid Build Coastguard Worker if (manifest_xml == nullptr) {
804*d57664e9SAndroid Build Coastguard Worker // No errors.
805*d57664e9SAndroid Build Coastguard Worker return;
806*d57664e9SAndroid Build Coastguard Worker }
807*d57664e9SAndroid Build Coastguard Worker
808*d57664e9SAndroid Build Coastguard Worker if (!options_.manifest_fixer_options.compile_sdk_version) {
809*d57664e9SAndroid Build Coastguard Worker xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
810*d57664e9SAndroid Build Coastguard Worker if (attr != nullptr) {
811*d57664e9SAndroid Build Coastguard Worker auto& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
812*d57664e9SAndroid Build Coastguard Worker if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
813*d57664e9SAndroid Build Coastguard Worker switch (prim->value.dataType) {
814*d57664e9SAndroid Build Coastguard Worker case Res_value::TYPE_INT_DEC:
815*d57664e9SAndroid Build Coastguard Worker compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data));
816*d57664e9SAndroid Build Coastguard Worker break;
817*d57664e9SAndroid Build Coastguard Worker case Res_value::TYPE_INT_HEX:
818*d57664e9SAndroid Build Coastguard Worker compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data);
819*d57664e9SAndroid Build Coastguard Worker break;
820*d57664e9SAndroid Build Coastguard Worker default:
821*d57664e9SAndroid Build Coastguard Worker break;
822*d57664e9SAndroid Build Coastguard Worker }
823*d57664e9SAndroid Build Coastguard Worker } else if (String* str = ValueCast<String>(attr->compiled_value.get())) {
824*d57664e9SAndroid Build Coastguard Worker compile_sdk_version = *str->value;
825*d57664e9SAndroid Build Coastguard Worker } else {
826*d57664e9SAndroid Build Coastguard Worker compile_sdk_version = attr->value;
827*d57664e9SAndroid Build Coastguard Worker }
828*d57664e9SAndroid Build Coastguard Worker }
829*d57664e9SAndroid Build Coastguard Worker }
830*d57664e9SAndroid Build Coastguard Worker
831*d57664e9SAndroid Build Coastguard Worker if (!options_.manifest_fixer_options.compile_sdk_version_codename) {
832*d57664e9SAndroid Build Coastguard Worker xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
833*d57664e9SAndroid Build Coastguard Worker if (attr != nullptr) {
834*d57664e9SAndroid Build Coastguard Worker std::optional<std::string>& compile_sdk_version_codename =
835*d57664e9SAndroid Build Coastguard Worker options_.manifest_fixer_options.compile_sdk_version_codename;
836*d57664e9SAndroid Build Coastguard Worker if (String* str = ValueCast<String>(attr->compiled_value.get())) {
837*d57664e9SAndroid Build Coastguard Worker compile_sdk_version_codename = *str->value;
838*d57664e9SAndroid Build Coastguard Worker } else {
839*d57664e9SAndroid Build Coastguard Worker compile_sdk_version_codename = attr->value;
840*d57664e9SAndroid Build Coastguard Worker }
841*d57664e9SAndroid Build Coastguard Worker }
842*d57664e9SAndroid Build Coastguard Worker }
843*d57664e9SAndroid Build Coastguard Worker }
844*d57664e9SAndroid Build Coastguard Worker
845*d57664e9SAndroid Build Coastguard Worker // Creates a SymbolTable that loads symbols from the various APKs.
846*d57664e9SAndroid Build Coastguard Worker // Pre-condition: context_->GetCompilationPackage() needs to be set.
LoadSymbolsFromIncludePaths()847*d57664e9SAndroid Build Coastguard Worker bool LoadSymbolsFromIncludePaths() {
848*d57664e9SAndroid Build Coastguard Worker TRACE_NAME("LoadSymbolsFromIncludePaths: #" + std::to_string(options_.include_paths.size()));
849*d57664e9SAndroid Build Coastguard Worker auto asset_source = util::make_unique<AssetManagerSymbolSource>();
850*d57664e9SAndroid Build Coastguard Worker for (const std::string& path : options_.include_paths) {
851*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
852*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage() << "including " << path);
853*d57664e9SAndroid Build Coastguard Worker }
854*d57664e9SAndroid Build Coastguard Worker
855*d57664e9SAndroid Build Coastguard Worker std::string error;
856*d57664e9SAndroid Build Coastguard Worker auto zip_collection = io::ZipFileCollection::Create(path, &error);
857*d57664e9SAndroid Build Coastguard Worker if (zip_collection == nullptr) {
858*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
859*d57664e9SAndroid Build Coastguard Worker << "failed to open APK: " << error);
860*d57664e9SAndroid Build Coastguard Worker return false;
861*d57664e9SAndroid Build Coastguard Worker }
862*d57664e9SAndroid Build Coastguard Worker
863*d57664e9SAndroid Build Coastguard Worker if (zip_collection->FindFile(kProtoResourceTablePath) != nullptr) {
864*d57664e9SAndroid Build Coastguard Worker // Load this as a static library include.
865*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<LoadedApk> static_apk = LoadedApk::LoadProtoApkFromFileCollection(
866*d57664e9SAndroid Build Coastguard Worker android::Source(path), std::move(zip_collection), context_->GetDiagnostics());
867*d57664e9SAndroid Build Coastguard Worker if (static_apk == nullptr) {
868*d57664e9SAndroid Build Coastguard Worker return false;
869*d57664e9SAndroid Build Coastguard Worker }
870*d57664e9SAndroid Build Coastguard Worker
871*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() != PackageType::kStaticLib) {
872*d57664e9SAndroid Build Coastguard Worker // Can't include static libraries when not building a static library (they have no IDs
873*d57664e9SAndroid Build Coastguard Worker // assigned).
874*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
875*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(path)
876*d57664e9SAndroid Build Coastguard Worker << "can't include static library when not building a static lib");
877*d57664e9SAndroid Build Coastguard Worker return false;
878*d57664e9SAndroid Build Coastguard Worker }
879*d57664e9SAndroid Build Coastguard Worker
880*d57664e9SAndroid Build Coastguard Worker ResourceTable* table = static_apk->GetResourceTable();
881*d57664e9SAndroid Build Coastguard Worker
882*d57664e9SAndroid Build Coastguard Worker // If we are using --no-static-lib-packages, we need to rename the package of this table to
883*d57664e9SAndroid Build Coastguard Worker // our compilation package so the symbol package name does not get mangled into the entry
884*d57664e9SAndroid Build Coastguard Worker // name.
885*d57664e9SAndroid Build Coastguard Worker if (options_.no_static_lib_packages && !table->packages.empty()) {
886*d57664e9SAndroid Build Coastguard Worker auto lib_package_result = GetStaticLibraryPackage(table);
887*d57664e9SAndroid Build Coastguard Worker if (!lib_package_result.has_value()) {
888*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(path)
889*d57664e9SAndroid Build Coastguard Worker << lib_package_result.error());
890*d57664e9SAndroid Build Coastguard Worker return false;
891*d57664e9SAndroid Build Coastguard Worker }
892*d57664e9SAndroid Build Coastguard Worker lib_package_result.value()->name = context_->GetCompilationPackage();
893*d57664e9SAndroid Build Coastguard Worker }
894*d57664e9SAndroid Build Coastguard Worker
895*d57664e9SAndroid Build Coastguard Worker context_->GetExternalSymbols()->AppendSource(
896*d57664e9SAndroid Build Coastguard Worker util::make_unique<ResourceTableSymbolSource>(table));
897*d57664e9SAndroid Build Coastguard Worker static_library_includes_.push_back(std::move(static_apk));
898*d57664e9SAndroid Build Coastguard Worker } else {
899*d57664e9SAndroid Build Coastguard Worker if (!asset_source->AddAssetPath(path)) {
900*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
901*d57664e9SAndroid Build Coastguard Worker << "failed to load include path " << path);
902*d57664e9SAndroid Build Coastguard Worker return false;
903*d57664e9SAndroid Build Coastguard Worker }
904*d57664e9SAndroid Build Coastguard Worker }
905*d57664e9SAndroid Build Coastguard Worker }
906*d57664e9SAndroid Build Coastguard Worker
907*d57664e9SAndroid Build Coastguard Worker // Capture the shared libraries so that the final resource table can be properly flattened
908*d57664e9SAndroid Build Coastguard Worker // with support for shared libraries.
909*d57664e9SAndroid Build Coastguard Worker for (auto& entry : asset_source->GetAssignedPackageIds()) {
910*d57664e9SAndroid Build Coastguard Worker if (entry.first == kAppPackageId) {
911*d57664e9SAndroid Build Coastguard Worker // Capture the included base feature package.
912*d57664e9SAndroid Build Coastguard Worker included_feature_base_ = entry.second;
913*d57664e9SAndroid Build Coastguard Worker } else if (entry.first == kFrameworkPackageId) {
914*d57664e9SAndroid Build Coastguard Worker // Try to embed which version of the framework we're compiling against.
915*d57664e9SAndroid Build Coastguard Worker // First check if we should use compileSdkVersion at all. Otherwise compilation may fail
916*d57664e9SAndroid Build Coastguard Worker // when linking our synthesized 'android:compileSdkVersion' attribute.
917*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName(
918*d57664e9SAndroid Build Coastguard Worker ResourceName("android", ResourceType::kAttr, "compileSdkVersion"));
919*d57664e9SAndroid Build Coastguard Worker if (symbol != nullptr && symbol->is_public) {
920*d57664e9SAndroid Build Coastguard Worker // The symbol is present and public, extract the android:versionName and
921*d57664e9SAndroid Build Coastguard Worker // android:versionCode from the framework AndroidManifest.xml.
922*d57664e9SAndroid Build Coastguard Worker ExtractCompileSdkVersions(asset_source->GetAssetManager());
923*d57664e9SAndroid Build Coastguard Worker }
924*d57664e9SAndroid Build Coastguard Worker } else if (asset_source->IsPackageDynamic(entry.first, entry.second)) {
925*d57664e9SAndroid Build Coastguard Worker final_table_.included_packages_[entry.first] = entry.second;
926*d57664e9SAndroid Build Coastguard Worker }
927*d57664e9SAndroid Build Coastguard Worker }
928*d57664e9SAndroid Build Coastguard Worker
929*d57664e9SAndroid Build Coastguard Worker context_->GetExternalSymbols()->AppendSource(std::move(asset_source));
930*d57664e9SAndroid Build Coastguard Worker return true;
931*d57664e9SAndroid Build Coastguard Worker }
932*d57664e9SAndroid Build Coastguard Worker
ExtractAppInfoFromManifest(xml::XmlResource * xml_res,android::IDiagnostics * diag)933*d57664e9SAndroid Build Coastguard Worker std::optional<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res,
934*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diag) {
935*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
936*d57664e9SAndroid Build Coastguard Worker // Make sure the first element is <manifest> with package attribute.
937*d57664e9SAndroid Build Coastguard Worker xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
938*d57664e9SAndroid Build Coastguard Worker if (manifest_el == nullptr) {
939*d57664e9SAndroid Build Coastguard Worker return {};
940*d57664e9SAndroid Build Coastguard Worker }
941*d57664e9SAndroid Build Coastguard Worker
942*d57664e9SAndroid Build Coastguard Worker AppInfo app_info;
943*d57664e9SAndroid Build Coastguard Worker
944*d57664e9SAndroid Build Coastguard Worker if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
945*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(xml_res->file.source) << "root tag must be <manifest>");
946*d57664e9SAndroid Build Coastguard Worker return {};
947*d57664e9SAndroid Build Coastguard Worker }
948*d57664e9SAndroid Build Coastguard Worker
949*d57664e9SAndroid Build Coastguard Worker xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
950*d57664e9SAndroid Build Coastguard Worker if (!package_attr) {
951*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(xml_res->file.source)
952*d57664e9SAndroid Build Coastguard Worker << "<manifest> must have a 'package' attribute");
953*d57664e9SAndroid Build Coastguard Worker return {};
954*d57664e9SAndroid Build Coastguard Worker }
955*d57664e9SAndroid Build Coastguard Worker app_info.package = package_attr->value;
956*d57664e9SAndroid Build Coastguard Worker
957*d57664e9SAndroid Build Coastguard Worker if (xml::Attribute* version_code_attr =
958*d57664e9SAndroid Build Coastguard Worker manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
959*d57664e9SAndroid Build Coastguard Worker std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
960*d57664e9SAndroid Build Coastguard Worker if (!maybe_code) {
961*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
962*d57664e9SAndroid Build Coastguard Worker << "invalid android:versionCode '" << version_code_attr->value << "'");
963*d57664e9SAndroid Build Coastguard Worker return {};
964*d57664e9SAndroid Build Coastguard Worker }
965*d57664e9SAndroid Build Coastguard Worker app_info.version_code = maybe_code.value();
966*d57664e9SAndroid Build Coastguard Worker }
967*d57664e9SAndroid Build Coastguard Worker
968*d57664e9SAndroid Build Coastguard Worker if (xml::Attribute* version_code_major_attr =
969*d57664e9SAndroid Build Coastguard Worker manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
970*d57664e9SAndroid Build Coastguard Worker std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
971*d57664e9SAndroid Build Coastguard Worker if (!maybe_code) {
972*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
973*d57664e9SAndroid Build Coastguard Worker << "invalid android:versionCodeMajor '" << version_code_major_attr->value
974*d57664e9SAndroid Build Coastguard Worker << "'");
975*d57664e9SAndroid Build Coastguard Worker return {};
976*d57664e9SAndroid Build Coastguard Worker }
977*d57664e9SAndroid Build Coastguard Worker app_info.version_code_major = maybe_code.value();
978*d57664e9SAndroid Build Coastguard Worker }
979*d57664e9SAndroid Build Coastguard Worker
980*d57664e9SAndroid Build Coastguard Worker if (xml::Attribute* revision_code_attr =
981*d57664e9SAndroid Build Coastguard Worker manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
982*d57664e9SAndroid Build Coastguard Worker std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
983*d57664e9SAndroid Build Coastguard Worker if (!maybe_code) {
984*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
985*d57664e9SAndroid Build Coastguard Worker << "invalid android:revisionCode '" << revision_code_attr->value << "'");
986*d57664e9SAndroid Build Coastguard Worker return {};
987*d57664e9SAndroid Build Coastguard Worker }
988*d57664e9SAndroid Build Coastguard Worker app_info.revision_code = maybe_code.value();
989*d57664e9SAndroid Build Coastguard Worker }
990*d57664e9SAndroid Build Coastguard Worker
991*d57664e9SAndroid Build Coastguard Worker if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
992*d57664e9SAndroid Build Coastguard Worker if (!split_name_attr->value.empty()) {
993*d57664e9SAndroid Build Coastguard Worker app_info.split_name = split_name_attr->value;
994*d57664e9SAndroid Build Coastguard Worker }
995*d57664e9SAndroid Build Coastguard Worker }
996*d57664e9SAndroid Build Coastguard Worker
997*d57664e9SAndroid Build Coastguard Worker if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
998*d57664e9SAndroid Build Coastguard Worker if (xml::Attribute* min_sdk =
999*d57664e9SAndroid Build Coastguard Worker uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
1000*d57664e9SAndroid Build Coastguard Worker app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value);
1001*d57664e9SAndroid Build Coastguard Worker }
1002*d57664e9SAndroid Build Coastguard Worker }
1003*d57664e9SAndroid Build Coastguard Worker
1004*d57664e9SAndroid Build Coastguard Worker for (const xml::Element* child_el : manifest_el->GetChildElements()) {
1005*d57664e9SAndroid Build Coastguard Worker if (child_el->namespace_uri.empty() && child_el->name == "uses-split") {
1006*d57664e9SAndroid Build Coastguard Worker if (const xml::Attribute* split_name =
1007*d57664e9SAndroid Build Coastguard Worker child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
1008*d57664e9SAndroid Build Coastguard Worker if (!split_name->value.empty()) {
1009*d57664e9SAndroid Build Coastguard Worker app_info.split_name_dependencies.insert(split_name->value);
1010*d57664e9SAndroid Build Coastguard Worker }
1011*d57664e9SAndroid Build Coastguard Worker }
1012*d57664e9SAndroid Build Coastguard Worker }
1013*d57664e9SAndroid Build Coastguard Worker }
1014*d57664e9SAndroid Build Coastguard Worker return app_info;
1015*d57664e9SAndroid Build Coastguard Worker }
1016*d57664e9SAndroid Build Coastguard Worker
1017*d57664e9SAndroid Build Coastguard Worker // Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
1018*d57664e9SAndroid Build Coastguard Worker // Postcondition: ResourceTable has only one package left. All others are
1019*d57664e9SAndroid Build Coastguard Worker // stripped, or there is an error and false is returned.
VerifyNoExternalPackages()1020*d57664e9SAndroid Build Coastguard Worker bool VerifyNoExternalPackages() {
1021*d57664e9SAndroid Build Coastguard Worker auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
1022*d57664e9SAndroid Build Coastguard Worker return context_->GetCompilationPackage() != pkg->name;
1023*d57664e9SAndroid Build Coastguard Worker };
1024*d57664e9SAndroid Build Coastguard Worker
1025*d57664e9SAndroid Build Coastguard Worker bool error = false;
1026*d57664e9SAndroid Build Coastguard Worker for (const auto& package : final_table_.packages) {
1027*d57664e9SAndroid Build Coastguard Worker if (is_ext_package_func(package)) {
1028*d57664e9SAndroid Build Coastguard Worker // We have a package that is not related to the one we're building!
1029*d57664e9SAndroid Build Coastguard Worker for (const auto& type : package->types) {
1030*d57664e9SAndroid Build Coastguard Worker for (const auto& entry : type->entries) {
1031*d57664e9SAndroid Build Coastguard Worker ResourceNameRef res_name(package->name, type->named_type, entry->name);
1032*d57664e9SAndroid Build Coastguard Worker
1033*d57664e9SAndroid Build Coastguard Worker for (const auto& config_value : entry->values) {
1034*d57664e9SAndroid Build Coastguard Worker // Special case the occurrence of an ID that is being generated
1035*d57664e9SAndroid Build Coastguard Worker // for the 'android' package. This is due to legacy reasons.
1036*d57664e9SAndroid Build Coastguard Worker if (ValueCast<Id>(config_value->value.get()) && package->name == "android") {
1037*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Warn(
1038*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(config_value->value->GetSource())
1039*d57664e9SAndroid Build Coastguard Worker << "generated id '" << res_name << "' for external package '" << package->name
1040*d57664e9SAndroid Build Coastguard Worker << "'");
1041*d57664e9SAndroid Build Coastguard Worker } else {
1042*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
1043*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(config_value->value->GetSource())
1044*d57664e9SAndroid Build Coastguard Worker << "defined resource '" << res_name << "' for external package '"
1045*d57664e9SAndroid Build Coastguard Worker << package->name << "'");
1046*d57664e9SAndroid Build Coastguard Worker error = true;
1047*d57664e9SAndroid Build Coastguard Worker }
1048*d57664e9SAndroid Build Coastguard Worker }
1049*d57664e9SAndroid Build Coastguard Worker }
1050*d57664e9SAndroid Build Coastguard Worker }
1051*d57664e9SAndroid Build Coastguard Worker }
1052*d57664e9SAndroid Build Coastguard Worker }
1053*d57664e9SAndroid Build Coastguard Worker
1054*d57664e9SAndroid Build Coastguard Worker auto new_end_iter = std::remove_if(final_table_.packages.begin(), final_table_.packages.end(),
1055*d57664e9SAndroid Build Coastguard Worker is_ext_package_func);
1056*d57664e9SAndroid Build Coastguard Worker final_table_.packages.erase(new_end_iter, final_table_.packages.end());
1057*d57664e9SAndroid Build Coastguard Worker return !error;
1058*d57664e9SAndroid Build Coastguard Worker }
1059*d57664e9SAndroid Build Coastguard Worker
1060*d57664e9SAndroid Build Coastguard Worker /**
1061*d57664e9SAndroid Build Coastguard Worker * Returns true if no IDs have been set, false otherwise.
1062*d57664e9SAndroid Build Coastguard Worker */
VerifyNoIdsSet()1063*d57664e9SAndroid Build Coastguard Worker bool VerifyNoIdsSet() {
1064*d57664e9SAndroid Build Coastguard Worker for (const auto& package : final_table_.packages) {
1065*d57664e9SAndroid Build Coastguard Worker for (const auto& type : package->types) {
1066*d57664e9SAndroid Build Coastguard Worker for (const auto& entry : type->entries) {
1067*d57664e9SAndroid Build Coastguard Worker if (entry->id) {
1068*d57664e9SAndroid Build Coastguard Worker ResourceNameRef res_name(package->name, type->named_type, entry->name);
1069*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
1070*d57664e9SAndroid Build Coastguard Worker << "resource " << res_name << " has ID "
1071*d57664e9SAndroid Build Coastguard Worker << entry->id.value() << " assigned");
1072*d57664e9SAndroid Build Coastguard Worker return false;
1073*d57664e9SAndroid Build Coastguard Worker }
1074*d57664e9SAndroid Build Coastguard Worker }
1075*d57664e9SAndroid Build Coastguard Worker }
1076*d57664e9SAndroid Build Coastguard Worker }
1077*d57664e9SAndroid Build Coastguard Worker return true;
1078*d57664e9SAndroid Build Coastguard Worker }
1079*d57664e9SAndroid Build Coastguard Worker
VerifyLocaleFormat(xml::XmlResource * manifest,android::IDiagnostics * diag)1080*d57664e9SAndroid Build Coastguard Worker bool VerifyLocaleFormat(xml::XmlResource* manifest, android::IDiagnostics* diag) {
1081*d57664e9SAndroid Build Coastguard Worker // Skip it if the Manifest doesn't declare the localeConfig attribute within the <application>
1082*d57664e9SAndroid Build Coastguard Worker // element.
1083*d57664e9SAndroid Build Coastguard Worker const xml::Element* application = manifest->root->FindChild("", "application");
1084*d57664e9SAndroid Build Coastguard Worker if (!application) {
1085*d57664e9SAndroid Build Coastguard Worker return true;
1086*d57664e9SAndroid Build Coastguard Worker }
1087*d57664e9SAndroid Build Coastguard Worker const xml::Attribute* localeConfig =
1088*d57664e9SAndroid Build Coastguard Worker application->FindAttribute(xml::kSchemaAndroid, "localeConfig");
1089*d57664e9SAndroid Build Coastguard Worker if (!localeConfig) {
1090*d57664e9SAndroid Build Coastguard Worker return true;
1091*d57664e9SAndroid Build Coastguard Worker }
1092*d57664e9SAndroid Build Coastguard Worker
1093*d57664e9SAndroid Build Coastguard Worker // Deserialize XML from the compiled file
1094*d57664e9SAndroid Build Coastguard Worker if (localeConfig->compiled_value) {
1095*d57664e9SAndroid Build Coastguard Worker const auto localeconfig_reference = ValueCast<Reference>(localeConfig->compiled_value.get());
1096*d57664e9SAndroid Build Coastguard Worker const auto localeconfig_entry =
1097*d57664e9SAndroid Build Coastguard Worker ResolveTableEntry(context_, &final_table_, localeconfig_reference);
1098*d57664e9SAndroid Build Coastguard Worker if (!localeconfig_entry) {
1099*d57664e9SAndroid Build Coastguard Worker // If locale config is resolved from external symbols - skip validation.
1100*d57664e9SAndroid Build Coastguard Worker if (context_->GetExternalSymbols()->FindByReference(*localeconfig_reference)) {
1101*d57664e9SAndroid Build Coastguard Worker return true;
1102*d57664e9SAndroid Build Coastguard Worker }
1103*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
1104*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(localeConfig->compiled_value->GetSource())
1105*d57664e9SAndroid Build Coastguard Worker << "no localeConfig entry");
1106*d57664e9SAndroid Build Coastguard Worker return false;
1107*d57664e9SAndroid Build Coastguard Worker }
1108*d57664e9SAndroid Build Coastguard Worker for (const auto& value : localeconfig_entry->values) {
1109*d57664e9SAndroid Build Coastguard Worker const FileReference* file_ref = ValueCast<FileReference>(value->value.get());
1110*d57664e9SAndroid Build Coastguard Worker if (!file_ref) {
1111*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
1112*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(localeConfig->compiled_value->GetSource())
1113*d57664e9SAndroid Build Coastguard Worker << "no file reference");
1114*d57664e9SAndroid Build Coastguard Worker return false;
1115*d57664e9SAndroid Build Coastguard Worker }
1116*d57664e9SAndroid Build Coastguard Worker io::IFile* file = file_ref->file;
1117*d57664e9SAndroid Build Coastguard Worker if (!file) {
1118*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file_ref->GetSource())
1119*d57664e9SAndroid Build Coastguard Worker << "file not found");
1120*d57664e9SAndroid Build Coastguard Worker return false;
1121*d57664e9SAndroid Build Coastguard Worker }
1122*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IData> data = file->OpenAsData();
1123*d57664e9SAndroid Build Coastguard Worker if (!data) {
1124*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
1125*d57664e9SAndroid Build Coastguard Worker << "failed to open file");
1126*d57664e9SAndroid Build Coastguard Worker return false;
1127*d57664e9SAndroid Build Coastguard Worker }
1128*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_xml_node;
1129*d57664e9SAndroid Build Coastguard Worker if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) {
1130*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
1131*d57664e9SAndroid Build Coastguard Worker << "failed to parse proto XML");
1132*d57664e9SAndroid Build Coastguard Worker return false;
1133*d57664e9SAndroid Build Coastguard Worker }
1134*d57664e9SAndroid Build Coastguard Worker
1135*d57664e9SAndroid Build Coastguard Worker std::string error;
1136*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> localeConfig_xml =
1137*d57664e9SAndroid Build Coastguard Worker DeserializeXmlResourceFromPb(pb_xml_node, &error);
1138*d57664e9SAndroid Build Coastguard Worker if (!localeConfig_xml) {
1139*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(file->GetSource())
1140*d57664e9SAndroid Build Coastguard Worker << "failed to deserialize proto XML: " << error);
1141*d57664e9SAndroid Build Coastguard Worker return false;
1142*d57664e9SAndroid Build Coastguard Worker }
1143*d57664e9SAndroid Build Coastguard Worker xml::Element* localeConfig_el = xml::FindRootElement(localeConfig_xml->root.get());
1144*d57664e9SAndroid Build Coastguard Worker if (!localeConfig_el) {
1145*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(file->GetSource()) << "no root tag defined");
1146*d57664e9SAndroid Build Coastguard Worker return false;
1147*d57664e9SAndroid Build Coastguard Worker }
1148*d57664e9SAndroid Build Coastguard Worker if (localeConfig_el->name != "locale-config") {
1149*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(file->GetSource())
1150*d57664e9SAndroid Build Coastguard Worker << "invalid element name: " << localeConfig_el->name
1151*d57664e9SAndroid Build Coastguard Worker << ", expected: locale-config");
1152*d57664e9SAndroid Build Coastguard Worker return false;
1153*d57664e9SAndroid Build Coastguard Worker }
1154*d57664e9SAndroid Build Coastguard Worker for (const xml::Element* child_el : localeConfig_el->GetChildElements()) {
1155*d57664e9SAndroid Build Coastguard Worker if (child_el->name == "locale") {
1156*d57664e9SAndroid Build Coastguard Worker if (const xml::Attribute* locale_name_attr =
1157*d57664e9SAndroid Build Coastguard Worker child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
1158*d57664e9SAndroid Build Coastguard Worker const std::string& locale_name = locale_name_attr->value;
1159*d57664e9SAndroid Build Coastguard Worker const std::string valid_name = ConvertToBCP47Tag(locale_name);
1160*d57664e9SAndroid Build Coastguard Worker // Start to verify the locale format
1161*d57664e9SAndroid Build Coastguard Worker ConfigDescription config;
1162*d57664e9SAndroid Build Coastguard Worker if (!ConfigDescription::Parse(valid_name, &config)) {
1163*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(file->GetSource())
1164*d57664e9SAndroid Build Coastguard Worker << "invalid configuration: " << locale_name);
1165*d57664e9SAndroid Build Coastguard Worker return false;
1166*d57664e9SAndroid Build Coastguard Worker }
1167*d57664e9SAndroid Build Coastguard Worker } else {
1168*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(file->GetSource())
1169*d57664e9SAndroid Build Coastguard Worker << "the attribute android:name is not found");
1170*d57664e9SAndroid Build Coastguard Worker return false;
1171*d57664e9SAndroid Build Coastguard Worker }
1172*d57664e9SAndroid Build Coastguard Worker } else {
1173*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(file->GetSource())
1174*d57664e9SAndroid Build Coastguard Worker << "invalid element name: " << child_el->name << ", expected: locale");
1175*d57664e9SAndroid Build Coastguard Worker return false;
1176*d57664e9SAndroid Build Coastguard Worker }
1177*d57664e9SAndroid Build Coastguard Worker }
1178*d57664e9SAndroid Build Coastguard Worker }
1179*d57664e9SAndroid Build Coastguard Worker }
1180*d57664e9SAndroid Build Coastguard Worker return true;
1181*d57664e9SAndroid Build Coastguard Worker }
1182*d57664e9SAndroid Build Coastguard Worker
ConvertToBCP47Tag(const std::string & locale)1183*d57664e9SAndroid Build Coastguard Worker std::string ConvertToBCP47Tag(const std::string& locale) {
1184*d57664e9SAndroid Build Coastguard Worker std::string bcp47tag = "b+";
1185*d57664e9SAndroid Build Coastguard Worker bcp47tag += locale;
1186*d57664e9SAndroid Build Coastguard Worker std::replace(bcp47tag.begin(), bcp47tag.end(), '-', '+');
1187*d57664e9SAndroid Build Coastguard Worker return bcp47tag;
1188*d57664e9SAndroid Build Coastguard Worker }
1189*d57664e9SAndroid Build Coastguard Worker
MakeArchiveWriter(StringPiece out)1190*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<IArchiveWriter> MakeArchiveWriter(StringPiece out) {
1191*d57664e9SAndroid Build Coastguard Worker if (options_.output_to_directory) {
1192*d57664e9SAndroid Build Coastguard Worker return CreateDirectoryArchiveWriter(context_->GetDiagnostics(), out);
1193*d57664e9SAndroid Build Coastguard Worker } else {
1194*d57664e9SAndroid Build Coastguard Worker return CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
1195*d57664e9SAndroid Build Coastguard Worker }
1196*d57664e9SAndroid Build Coastguard Worker }
1197*d57664e9SAndroid Build Coastguard Worker
FlattenTable(ResourceTable * table,OutputFormat format,IArchiveWriter * writer)1198*d57664e9SAndroid Build Coastguard Worker bool FlattenTable(ResourceTable* table, OutputFormat format, IArchiveWriter* writer) {
1199*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1200*d57664e9SAndroid Build Coastguard Worker switch (format) {
1201*d57664e9SAndroid Build Coastguard Worker case OutputFormat::kApk: {
1202*d57664e9SAndroid Build Coastguard Worker android::BigBuffer buffer(1024);
1203*d57664e9SAndroid Build Coastguard Worker TableFlattener flattener(options_.table_flattener_options, &buffer);
1204*d57664e9SAndroid Build Coastguard Worker if (!flattener.Consume(context_, table)) {
1205*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
1206*d57664e9SAndroid Build Coastguard Worker << "failed to flatten resource table");
1207*d57664e9SAndroid Build Coastguard Worker return false;
1208*d57664e9SAndroid Build Coastguard Worker }
1209*d57664e9SAndroid Build Coastguard Worker
1210*d57664e9SAndroid Build Coastguard Worker android::BigBufferInputStream input_stream(&buffer);
1211*d57664e9SAndroid Build Coastguard Worker return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
1212*d57664e9SAndroid Build Coastguard Worker ArchiveEntry::kAlign, writer);
1213*d57664e9SAndroid Build Coastguard Worker } break;
1214*d57664e9SAndroid Build Coastguard Worker
1215*d57664e9SAndroid Build Coastguard Worker case OutputFormat::kProto: {
1216*d57664e9SAndroid Build Coastguard Worker pb::ResourceTable pb_table;
1217*d57664e9SAndroid Build Coastguard Worker SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics(),
1218*d57664e9SAndroid Build Coastguard Worker options_.proto_table_flattener_options);
1219*d57664e9SAndroid Build Coastguard Worker return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
1220*d57664e9SAndroid Build Coastguard Worker ArchiveEntry::kCompress, writer);
1221*d57664e9SAndroid Build Coastguard Worker } break;
1222*d57664e9SAndroid Build Coastguard Worker }
1223*d57664e9SAndroid Build Coastguard Worker return false;
1224*d57664e9SAndroid Build Coastguard Worker }
1225*d57664e9SAndroid Build Coastguard Worker
WriteJavaFile(ResourceTable * table,StringPiece package_name_to_generate,StringPiece out_package,const JavaClassGeneratorOptions & java_options,const std::optional<std::string> & out_text_symbols_path={})1226*d57664e9SAndroid Build Coastguard Worker bool WriteJavaFile(ResourceTable* table, StringPiece package_name_to_generate,
1227*d57664e9SAndroid Build Coastguard Worker StringPiece out_package, const JavaClassGeneratorOptions& java_options,
1228*d57664e9SAndroid Build Coastguard Worker const std::optional<std::string>& out_text_symbols_path = {}) {
1229*d57664e9SAndroid Build Coastguard Worker if (!options_.generate_java_class_path && !out_text_symbols_path) {
1230*d57664e9SAndroid Build Coastguard Worker return true;
1231*d57664e9SAndroid Build Coastguard Worker }
1232*d57664e9SAndroid Build Coastguard Worker
1233*d57664e9SAndroid Build Coastguard Worker std::string out_path;
1234*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::FileOutputStream> fout;
1235*d57664e9SAndroid Build Coastguard Worker if (options_.generate_java_class_path) {
1236*d57664e9SAndroid Build Coastguard Worker out_path = options_.generate_java_class_path.value();
1237*d57664e9SAndroid Build Coastguard Worker file::AppendPath(&out_path, file::PackageToPath(out_package));
1238*d57664e9SAndroid Build Coastguard Worker if (!file::mkdirs(out_path)) {
1239*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
1240*d57664e9SAndroid Build Coastguard Worker << "failed to create directory '" << out_path << "'");
1241*d57664e9SAndroid Build Coastguard Worker return false;
1242*d57664e9SAndroid Build Coastguard Worker }
1243*d57664e9SAndroid Build Coastguard Worker
1244*d57664e9SAndroid Build Coastguard Worker file::AppendPath(&out_path, "R.java");
1245*d57664e9SAndroid Build Coastguard Worker
1246*d57664e9SAndroid Build Coastguard Worker fout = util::make_unique<android::FileOutputStream>(out_path);
1247*d57664e9SAndroid Build Coastguard Worker if (fout->HadError()) {
1248*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
1249*d57664e9SAndroid Build Coastguard Worker << "failed writing to '" << out_path
1250*d57664e9SAndroid Build Coastguard Worker << "': " << fout->GetError());
1251*d57664e9SAndroid Build Coastguard Worker return false;
1252*d57664e9SAndroid Build Coastguard Worker }
1253*d57664e9SAndroid Build Coastguard Worker }
1254*d57664e9SAndroid Build Coastguard Worker
1255*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::FileOutputStream> fout_text;
1256*d57664e9SAndroid Build Coastguard Worker if (out_text_symbols_path) {
1257*d57664e9SAndroid Build Coastguard Worker fout_text = util::make_unique<android::FileOutputStream>(out_text_symbols_path.value());
1258*d57664e9SAndroid Build Coastguard Worker if (fout_text->HadError()) {
1259*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
1260*d57664e9SAndroid Build Coastguard Worker << "failed writing to '" << out_text_symbols_path.value()
1261*d57664e9SAndroid Build Coastguard Worker << "': " << fout_text->GetError());
1262*d57664e9SAndroid Build Coastguard Worker return false;
1263*d57664e9SAndroid Build Coastguard Worker }
1264*d57664e9SAndroid Build Coastguard Worker }
1265*d57664e9SAndroid Build Coastguard Worker
1266*d57664e9SAndroid Build Coastguard Worker JavaClassGenerator generator(context_, table, java_options);
1267*d57664e9SAndroid Build Coastguard Worker if (!generator.Generate(package_name_to_generate, out_package, fout.get(), fout_text.get())) {
1268*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(out_path) << generator.GetError());
1269*d57664e9SAndroid Build Coastguard Worker return false;
1270*d57664e9SAndroid Build Coastguard Worker }
1271*d57664e9SAndroid Build Coastguard Worker
1272*d57664e9SAndroid Build Coastguard Worker return true;
1273*d57664e9SAndroid Build Coastguard Worker }
1274*d57664e9SAndroid Build Coastguard Worker
GenerateJavaClasses()1275*d57664e9SAndroid Build Coastguard Worker bool GenerateJavaClasses() {
1276*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1277*d57664e9SAndroid Build Coastguard Worker // The set of packages whose R class to call in the main classes onResourcesLoaded callback.
1278*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> packages_to_callback;
1279*d57664e9SAndroid Build Coastguard Worker
1280*d57664e9SAndroid Build Coastguard Worker JavaClassGeneratorOptions template_options;
1281*d57664e9SAndroid Build Coastguard Worker template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
1282*d57664e9SAndroid Build Coastguard Worker template_options.javadoc_annotations = options_.javadoc_annotations;
1283*d57664e9SAndroid Build Coastguard Worker
1284*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() == PackageType::kStaticLib || options_.generate_non_final_ids) {
1285*d57664e9SAndroid Build Coastguard Worker template_options.use_final = false;
1286*d57664e9SAndroid Build Coastguard Worker }
1287*d57664e9SAndroid Build Coastguard Worker
1288*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() == PackageType::kSharedLib) {
1289*d57664e9SAndroid Build Coastguard Worker template_options.use_final = false;
1290*d57664e9SAndroid Build Coastguard Worker template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
1291*d57664e9SAndroid Build Coastguard Worker }
1292*d57664e9SAndroid Build Coastguard Worker
1293*d57664e9SAndroid Build Coastguard Worker const StringPiece actual_package = context_->GetCompilationPackage();
1294*d57664e9SAndroid Build Coastguard Worker StringPiece output_package = context_->GetCompilationPackage();
1295*d57664e9SAndroid Build Coastguard Worker if (options_.custom_java_package) {
1296*d57664e9SAndroid Build Coastguard Worker // Override the output java package to the custom one.
1297*d57664e9SAndroid Build Coastguard Worker output_package = options_.custom_java_package.value();
1298*d57664e9SAndroid Build Coastguard Worker }
1299*d57664e9SAndroid Build Coastguard Worker
1300*d57664e9SAndroid Build Coastguard Worker // Generate the private symbols if required.
1301*d57664e9SAndroid Build Coastguard Worker if (options_.private_symbols) {
1302*d57664e9SAndroid Build Coastguard Worker packages_to_callback.push_back(options_.private_symbols.value());
1303*d57664e9SAndroid Build Coastguard Worker
1304*d57664e9SAndroid Build Coastguard Worker // If we defined a private symbols package, we only emit Public symbols
1305*d57664e9SAndroid Build Coastguard Worker // to the original package, and private and public symbols to the private package.
1306*d57664e9SAndroid Build Coastguard Worker JavaClassGeneratorOptions options = template_options;
1307*d57664e9SAndroid Build Coastguard Worker options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
1308*d57664e9SAndroid Build Coastguard Worker if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
1309*d57664e9SAndroid Build Coastguard Worker options)) {
1310*d57664e9SAndroid Build Coastguard Worker return false;
1311*d57664e9SAndroid Build Coastguard Worker }
1312*d57664e9SAndroid Build Coastguard Worker }
1313*d57664e9SAndroid Build Coastguard Worker
1314*d57664e9SAndroid Build Coastguard Worker // Generate copies of the original package R class but with different package names.
1315*d57664e9SAndroid Build Coastguard Worker // This is to support non-namespaced builds.
1316*d57664e9SAndroid Build Coastguard Worker for (const std::string& extra_package : options_.extra_java_packages) {
1317*d57664e9SAndroid Build Coastguard Worker packages_to_callback.push_back(extra_package);
1318*d57664e9SAndroid Build Coastguard Worker
1319*d57664e9SAndroid Build Coastguard Worker JavaClassGeneratorOptions options = template_options;
1320*d57664e9SAndroid Build Coastguard Worker options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
1321*d57664e9SAndroid Build Coastguard Worker if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
1322*d57664e9SAndroid Build Coastguard Worker return false;
1323*d57664e9SAndroid Build Coastguard Worker }
1324*d57664e9SAndroid Build Coastguard Worker }
1325*d57664e9SAndroid Build Coastguard Worker
1326*d57664e9SAndroid Build Coastguard Worker // Generate R classes for each package that was merged (static library).
1327*d57664e9SAndroid Build Coastguard Worker // Use the actual package's resources only.
1328*d57664e9SAndroid Build Coastguard Worker for (const std::string& package : table_merger_->merged_packages()) {
1329*d57664e9SAndroid Build Coastguard Worker packages_to_callback.push_back(package);
1330*d57664e9SAndroid Build Coastguard Worker
1331*d57664e9SAndroid Build Coastguard Worker JavaClassGeneratorOptions options = template_options;
1332*d57664e9SAndroid Build Coastguard Worker options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
1333*d57664e9SAndroid Build Coastguard Worker if (!WriteJavaFile(&final_table_, package, package, options)) {
1334*d57664e9SAndroid Build Coastguard Worker return false;
1335*d57664e9SAndroid Build Coastguard Worker }
1336*d57664e9SAndroid Build Coastguard Worker }
1337*d57664e9SAndroid Build Coastguard Worker
1338*d57664e9SAndroid Build Coastguard Worker // Generate the main public R class.
1339*d57664e9SAndroid Build Coastguard Worker JavaClassGeneratorOptions options = template_options;
1340*d57664e9SAndroid Build Coastguard Worker
1341*d57664e9SAndroid Build Coastguard Worker // Only generate public symbols if we have a private package.
1342*d57664e9SAndroid Build Coastguard Worker if (options_.private_symbols) {
1343*d57664e9SAndroid Build Coastguard Worker options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
1344*d57664e9SAndroid Build Coastguard Worker }
1345*d57664e9SAndroid Build Coastguard Worker
1346*d57664e9SAndroid Build Coastguard Worker if (options.rewrite_callback_options) {
1347*d57664e9SAndroid Build Coastguard Worker options.rewrite_callback_options.value().packages_to_callback =
1348*d57664e9SAndroid Build Coastguard Worker std::move(packages_to_callback);
1349*d57664e9SAndroid Build Coastguard Worker }
1350*d57664e9SAndroid Build Coastguard Worker
1351*d57664e9SAndroid Build Coastguard Worker if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
1352*d57664e9SAndroid Build Coastguard Worker options_.generate_text_symbols_path)) {
1353*d57664e9SAndroid Build Coastguard Worker return false;
1354*d57664e9SAndroid Build Coastguard Worker }
1355*d57664e9SAndroid Build Coastguard Worker
1356*d57664e9SAndroid Build Coastguard Worker return true;
1357*d57664e9SAndroid Build Coastguard Worker }
1358*d57664e9SAndroid Build Coastguard Worker
WriteManifestJavaFile(xml::XmlResource * manifest_xml)1359*d57664e9SAndroid Build Coastguard Worker bool WriteManifestJavaFile(xml::XmlResource* manifest_xml) {
1360*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1361*d57664e9SAndroid Build Coastguard Worker if (!options_.generate_java_class_path) {
1362*d57664e9SAndroid Build Coastguard Worker return true;
1363*d57664e9SAndroid Build Coastguard Worker }
1364*d57664e9SAndroid Build Coastguard Worker
1365*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<ClassDefinition> manifest_class =
1366*d57664e9SAndroid Build Coastguard Worker GenerateManifestClass(context_->GetDiagnostics(), manifest_xml);
1367*d57664e9SAndroid Build Coastguard Worker
1368*d57664e9SAndroid Build Coastguard Worker if (!manifest_class) {
1369*d57664e9SAndroid Build Coastguard Worker // Something bad happened, but we already logged it, so exit.
1370*d57664e9SAndroid Build Coastguard Worker return false;
1371*d57664e9SAndroid Build Coastguard Worker }
1372*d57664e9SAndroid Build Coastguard Worker
1373*d57664e9SAndroid Build Coastguard Worker if (manifest_class->empty()) {
1374*d57664e9SAndroid Build Coastguard Worker // Empty Manifest class, no need to generate it.
1375*d57664e9SAndroid Build Coastguard Worker return true;
1376*d57664e9SAndroid Build Coastguard Worker }
1377*d57664e9SAndroid Build Coastguard Worker
1378*d57664e9SAndroid Build Coastguard Worker // Add any JavaDoc annotations to the generated class.
1379*d57664e9SAndroid Build Coastguard Worker for (const std::string& annotation : options_.javadoc_annotations) {
1380*d57664e9SAndroid Build Coastguard Worker std::string proper_annotation = "@";
1381*d57664e9SAndroid Build Coastguard Worker proper_annotation += annotation;
1382*d57664e9SAndroid Build Coastguard Worker manifest_class->GetCommentBuilder()->AppendComment(proper_annotation);
1383*d57664e9SAndroid Build Coastguard Worker }
1384*d57664e9SAndroid Build Coastguard Worker
1385*d57664e9SAndroid Build Coastguard Worker const std::string package_utf8 =
1386*d57664e9SAndroid Build Coastguard Worker options_.custom_java_package.value_or(context_->GetCompilationPackage());
1387*d57664e9SAndroid Build Coastguard Worker
1388*d57664e9SAndroid Build Coastguard Worker std::string out_path = options_.generate_java_class_path.value();
1389*d57664e9SAndroid Build Coastguard Worker file::AppendPath(&out_path, file::PackageToPath(package_utf8));
1390*d57664e9SAndroid Build Coastguard Worker
1391*d57664e9SAndroid Build Coastguard Worker if (!file::mkdirs(out_path)) {
1392*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
1393*d57664e9SAndroid Build Coastguard Worker << "failed to create directory '" << out_path << "'");
1394*d57664e9SAndroid Build Coastguard Worker return false;
1395*d57664e9SAndroid Build Coastguard Worker }
1396*d57664e9SAndroid Build Coastguard Worker
1397*d57664e9SAndroid Build Coastguard Worker file::AppendPath(&out_path, "Manifest.java");
1398*d57664e9SAndroid Build Coastguard Worker
1399*d57664e9SAndroid Build Coastguard Worker android::FileOutputStream fout(out_path);
1400*d57664e9SAndroid Build Coastguard Worker if (fout.HadError()) {
1401*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to open '" << out_path
1402*d57664e9SAndroid Build Coastguard Worker << "': " << fout.GetError());
1403*d57664e9SAndroid Build Coastguard Worker return false;
1404*d57664e9SAndroid Build Coastguard Worker }
1405*d57664e9SAndroid Build Coastguard Worker
1406*d57664e9SAndroid Build Coastguard Worker ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true,
1407*d57664e9SAndroid Build Coastguard Worker false /* strip_api_annotations */, &fout);
1408*d57664e9SAndroid Build Coastguard Worker fout.Flush();
1409*d57664e9SAndroid Build Coastguard Worker
1410*d57664e9SAndroid Build Coastguard Worker if (fout.HadError()) {
1411*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed writing to '" << out_path
1412*d57664e9SAndroid Build Coastguard Worker << "': " << fout.GetError());
1413*d57664e9SAndroid Build Coastguard Worker return false;
1414*d57664e9SAndroid Build Coastguard Worker }
1415*d57664e9SAndroid Build Coastguard Worker return true;
1416*d57664e9SAndroid Build Coastguard Worker }
1417*d57664e9SAndroid Build Coastguard Worker
WriteProguardFile(const std::optional<std::string> & out,const proguard::KeepSet & keep_set)1418*d57664e9SAndroid Build Coastguard Worker bool WriteProguardFile(const std::optional<std::string>& out, const proguard::KeepSet& keep_set) {
1419*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1420*d57664e9SAndroid Build Coastguard Worker if (!out) {
1421*d57664e9SAndroid Build Coastguard Worker return true;
1422*d57664e9SAndroid Build Coastguard Worker }
1423*d57664e9SAndroid Build Coastguard Worker
1424*d57664e9SAndroid Build Coastguard Worker const std::string& out_path = out.value();
1425*d57664e9SAndroid Build Coastguard Worker android::FileOutputStream fout(out_path);
1426*d57664e9SAndroid Build Coastguard Worker if (fout.HadError()) {
1427*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to open '" << out_path
1428*d57664e9SAndroid Build Coastguard Worker << "': " << fout.GetError());
1429*d57664e9SAndroid Build Coastguard Worker return false;
1430*d57664e9SAndroid Build Coastguard Worker }
1431*d57664e9SAndroid Build Coastguard Worker
1432*d57664e9SAndroid Build Coastguard Worker proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules,
1433*d57664e9SAndroid Build Coastguard Worker options_.no_proguard_location_reference);
1434*d57664e9SAndroid Build Coastguard Worker fout.Flush();
1435*d57664e9SAndroid Build Coastguard Worker
1436*d57664e9SAndroid Build Coastguard Worker if (fout.HadError()) {
1437*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed writing to '" << out_path
1438*d57664e9SAndroid Build Coastguard Worker << "': " << fout.GetError());
1439*d57664e9SAndroid Build Coastguard Worker return false;
1440*d57664e9SAndroid Build Coastguard Worker }
1441*d57664e9SAndroid Build Coastguard Worker return true;
1442*d57664e9SAndroid Build Coastguard Worker }
1443*d57664e9SAndroid Build Coastguard Worker
MergeStaticLibrary(const std::string & input,bool override)1444*d57664e9SAndroid Build Coastguard Worker bool MergeStaticLibrary(const std::string& input, bool override) {
1445*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1446*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
1447*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
1448*d57664e9SAndroid Build Coastguard Worker << "merging static library " << input);
1449*d57664e9SAndroid Build Coastguard Worker }
1450*d57664e9SAndroid Build Coastguard Worker
1451*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(input, context_->GetDiagnostics());
1452*d57664e9SAndroid Build Coastguard Worker if (apk == nullptr) {
1453*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(input) << "invalid static library");
1454*d57664e9SAndroid Build Coastguard Worker return false;
1455*d57664e9SAndroid Build Coastguard Worker }
1456*d57664e9SAndroid Build Coastguard Worker
1457*d57664e9SAndroid Build Coastguard Worker ResourceTable* table = apk->GetResourceTable();
1458*d57664e9SAndroid Build Coastguard Worker if (table->packages.empty()) {
1459*d57664e9SAndroid Build Coastguard Worker return true;
1460*d57664e9SAndroid Build Coastguard Worker }
1461*d57664e9SAndroid Build Coastguard Worker
1462*d57664e9SAndroid Build Coastguard Worker auto lib_package_result = GetStaticLibraryPackage(table);
1463*d57664e9SAndroid Build Coastguard Worker if (!lib_package_result.has_value()) {
1464*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(input) << lib_package_result.error());
1465*d57664e9SAndroid Build Coastguard Worker return false;
1466*d57664e9SAndroid Build Coastguard Worker }
1467*d57664e9SAndroid Build Coastguard Worker
1468*d57664e9SAndroid Build Coastguard Worker ResourceTablePackage* pkg = lib_package_result.value();
1469*d57664e9SAndroid Build Coastguard Worker bool result;
1470*d57664e9SAndroid Build Coastguard Worker if (options_.no_static_lib_packages) {
1471*d57664e9SAndroid Build Coastguard Worker // Merge all resources as if they were in the compilation package. This is the old behavior
1472*d57664e9SAndroid Build Coastguard Worker // of aapt.
1473*d57664e9SAndroid Build Coastguard Worker
1474*d57664e9SAndroid Build Coastguard Worker // Add the package to the set of --extra-packages so we emit an R.java for each library
1475*d57664e9SAndroid Build Coastguard Worker // package.
1476*d57664e9SAndroid Build Coastguard Worker if (!pkg->name.empty()) {
1477*d57664e9SAndroid Build Coastguard Worker options_.extra_java_packages.insert(pkg->name);
1478*d57664e9SAndroid Build Coastguard Worker }
1479*d57664e9SAndroid Build Coastguard Worker
1480*d57664e9SAndroid Build Coastguard Worker // Clear the package name, so as to make the resources look like they are coming from the
1481*d57664e9SAndroid Build Coastguard Worker // local package.
1482*d57664e9SAndroid Build Coastguard Worker pkg->name = "";
1483*d57664e9SAndroid Build Coastguard Worker result = table_merger_->Merge(android::Source(input), table, override);
1484*d57664e9SAndroid Build Coastguard Worker
1485*d57664e9SAndroid Build Coastguard Worker } else {
1486*d57664e9SAndroid Build Coastguard Worker // This is the proper way to merge libraries, where the package name is
1487*d57664e9SAndroid Build Coastguard Worker // preserved and resource names are mangled.
1488*d57664e9SAndroid Build Coastguard Worker result = table_merger_->MergeAndMangle(android::Source(input), pkg->name, table);
1489*d57664e9SAndroid Build Coastguard Worker }
1490*d57664e9SAndroid Build Coastguard Worker
1491*d57664e9SAndroid Build Coastguard Worker if (!result) {
1492*d57664e9SAndroid Build Coastguard Worker return false;
1493*d57664e9SAndroid Build Coastguard Worker }
1494*d57664e9SAndroid Build Coastguard Worker
1495*d57664e9SAndroid Build Coastguard Worker // Make sure to move the collection into the set of IFileCollections.
1496*d57664e9SAndroid Build Coastguard Worker merged_apks_.push_back(std::move(apk));
1497*d57664e9SAndroid Build Coastguard Worker return true;
1498*d57664e9SAndroid Build Coastguard Worker }
1499*d57664e9SAndroid Build Coastguard Worker
MergeExportedSymbols(const android::Source & source,const std::vector<SourcedResourceName> & exported_symbols)1500*d57664e9SAndroid Build Coastguard Worker bool MergeExportedSymbols(const android::Source& source,
1501*d57664e9SAndroid Build Coastguard Worker const std::vector<SourcedResourceName>& exported_symbols) {
1502*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1503*d57664e9SAndroid Build Coastguard Worker // Add the exports of this file to the table.
1504*d57664e9SAndroid Build Coastguard Worker for (const SourcedResourceName& exported_symbol : exported_symbols) {
1505*d57664e9SAndroid Build Coastguard Worker ResourceName res_name = exported_symbol.name;
1506*d57664e9SAndroid Build Coastguard Worker if (res_name.package.empty()) {
1507*d57664e9SAndroid Build Coastguard Worker res_name.package = context_->GetCompilationPackage();
1508*d57664e9SAndroid Build Coastguard Worker }
1509*d57664e9SAndroid Build Coastguard Worker
1510*d57664e9SAndroid Build Coastguard Worker std::optional<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
1511*d57664e9SAndroid Build Coastguard Worker if (mangled_name) {
1512*d57664e9SAndroid Build Coastguard Worker res_name = mangled_name.value();
1513*d57664e9SAndroid Build Coastguard Worker }
1514*d57664e9SAndroid Build Coastguard Worker
1515*d57664e9SAndroid Build Coastguard Worker auto id = util::make_unique<Id>();
1516*d57664e9SAndroid Build Coastguard Worker id->SetSource(source.WithLine(exported_symbol.line));
1517*d57664e9SAndroid Build Coastguard Worker bool result = final_table_.AddResource(
1518*d57664e9SAndroid Build Coastguard Worker NewResourceBuilder(res_name).SetValue(std::move(id)).SetAllowMangled(true).Build(),
1519*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics());
1520*d57664e9SAndroid Build Coastguard Worker if (!result) {
1521*d57664e9SAndroid Build Coastguard Worker return false;
1522*d57664e9SAndroid Build Coastguard Worker }
1523*d57664e9SAndroid Build Coastguard Worker }
1524*d57664e9SAndroid Build Coastguard Worker return true;
1525*d57664e9SAndroid Build Coastguard Worker }
1526*d57664e9SAndroid Build Coastguard Worker
MergeCompiledFile(const ResourceFile & compiled_file,io::IFile * file,bool override)1527*d57664e9SAndroid Build Coastguard Worker bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
1528*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1529*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
1530*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
1531*d57664e9SAndroid Build Coastguard Worker << "merging '" << compiled_file.name
1532*d57664e9SAndroid Build Coastguard Worker << "' from compiled file " << compiled_file.source);
1533*d57664e9SAndroid Build Coastguard Worker }
1534*d57664e9SAndroid Build Coastguard Worker
1535*d57664e9SAndroid Build Coastguard Worker if (!table_merger_->MergeFile(compiled_file, override, file)) {
1536*d57664e9SAndroid Build Coastguard Worker return false;
1537*d57664e9SAndroid Build Coastguard Worker }
1538*d57664e9SAndroid Build Coastguard Worker return MergeExportedSymbols(compiled_file.source, compiled_file.exported_symbols);
1539*d57664e9SAndroid Build Coastguard Worker }
1540*d57664e9SAndroid Build Coastguard Worker
1541*d57664e9SAndroid Build Coastguard Worker // Takes a path to load as a ZIP file and merges the files within into the main ResourceTable.
1542*d57664e9SAndroid Build Coastguard Worker // If override is true, conflicting resources are allowed to override each other, in order of last
1543*d57664e9SAndroid Build Coastguard Worker // seen.
1544*d57664e9SAndroid Build Coastguard Worker // An io::IFileCollection is created from the ZIP file and added to the set of
1545*d57664e9SAndroid Build Coastguard Worker // io::IFileCollections that are open.
MergeArchive(const std::string & input,bool override)1546*d57664e9SAndroid Build Coastguard Worker bool MergeArchive(const std::string& input, bool override) {
1547*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1548*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
1549*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage() << "merging archive " << input);
1550*d57664e9SAndroid Build Coastguard Worker }
1551*d57664e9SAndroid Build Coastguard Worker
1552*d57664e9SAndroid Build Coastguard Worker std::string error_str;
1553*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::ZipFileCollection> collection =
1554*d57664e9SAndroid Build Coastguard Worker io::ZipFileCollection::Create(input, &error_str);
1555*d57664e9SAndroid Build Coastguard Worker if (!collection) {
1556*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(input) << error_str);
1557*d57664e9SAndroid Build Coastguard Worker return false;
1558*d57664e9SAndroid Build Coastguard Worker }
1559*d57664e9SAndroid Build Coastguard Worker
1560*d57664e9SAndroid Build Coastguard Worker bool error = false;
1561*d57664e9SAndroid Build Coastguard Worker for (auto iter = collection->Iterator(); iter->HasNext();) {
1562*d57664e9SAndroid Build Coastguard Worker if (!MergeFile(iter->Next(), override)) {
1563*d57664e9SAndroid Build Coastguard Worker error = true;
1564*d57664e9SAndroid Build Coastguard Worker }
1565*d57664e9SAndroid Build Coastguard Worker }
1566*d57664e9SAndroid Build Coastguard Worker
1567*d57664e9SAndroid Build Coastguard Worker // Make sure to move the collection into the set of IFileCollections.
1568*d57664e9SAndroid Build Coastguard Worker collections_.push_back(std::move(collection));
1569*d57664e9SAndroid Build Coastguard Worker return !error;
1570*d57664e9SAndroid Build Coastguard Worker }
1571*d57664e9SAndroid Build Coastguard Worker
1572*d57664e9SAndroid Build Coastguard Worker // Takes a path to load and merge into the main ResourceTable. If override is true,
1573*d57664e9SAndroid Build Coastguard Worker // conflicting resources are allowed to override each other, in order of last seen.
1574*d57664e9SAndroid Build Coastguard Worker // If the file path ends with .flata, .jar, .jack, or .zip the file is treated
1575*d57664e9SAndroid Build Coastguard Worker // as ZIP archive and the files within are merged individually.
1576*d57664e9SAndroid Build Coastguard Worker // Otherwise the file is processed on its own.
MergePath(std::string path,bool override)1577*d57664e9SAndroid Build Coastguard Worker bool MergePath(std::string path, bool override) {
1578*d57664e9SAndroid Build Coastguard Worker if (path.size() > 2 && util::StartsWith(path, "'") && util::EndsWith(path, "'")) {
1579*d57664e9SAndroid Build Coastguard Worker path = path.substr(1, path.size() - 2);
1580*d57664e9SAndroid Build Coastguard Worker }
1581*d57664e9SAndroid Build Coastguard Worker if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") ||
1582*d57664e9SAndroid Build Coastguard Worker util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) {
1583*d57664e9SAndroid Build Coastguard Worker return MergeArchive(path, override);
1584*d57664e9SAndroid Build Coastguard Worker } else if (util::EndsWith(path, ".apk")) {
1585*d57664e9SAndroid Build Coastguard Worker return MergeStaticLibrary(path, override);
1586*d57664e9SAndroid Build Coastguard Worker }
1587*d57664e9SAndroid Build Coastguard Worker
1588*d57664e9SAndroid Build Coastguard Worker io::IFile* file = file_collection_->InsertFile(path);
1589*d57664e9SAndroid Build Coastguard Worker return MergeFile(file, override);
1590*d57664e9SAndroid Build Coastguard Worker }
1591*d57664e9SAndroid Build Coastguard Worker
1592*d57664e9SAndroid Build Coastguard Worker // Takes an AAPT Container file (.apc/.flat) to load and merge into the main ResourceTable.
1593*d57664e9SAndroid Build Coastguard Worker // If override is true, conflicting resources are allowed to override each other, in order of last
1594*d57664e9SAndroid Build Coastguard Worker // seen.
1595*d57664e9SAndroid Build Coastguard Worker // All other file types are ignored. This is because these files could be coming from a zip,
1596*d57664e9SAndroid Build Coastguard Worker // where we could have other files like classes.dex.
MergeFile(io::IFile * file,bool override)1597*d57664e9SAndroid Build Coastguard Worker bool MergeFile(io::IFile* file, bool override) {
1598*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1599*d57664e9SAndroid Build Coastguard Worker const android::Source& src = file->GetSource();
1600*d57664e9SAndroid Build Coastguard Worker
1601*d57664e9SAndroid Build Coastguard Worker if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) {
1602*d57664e9SAndroid Build Coastguard Worker // Since AAPT compiles these file types and appends .flat to them, seeing
1603*d57664e9SAndroid Build Coastguard Worker // their raw extensions is a sign that they weren't compiled.
1604*d57664e9SAndroid Build Coastguard Worker const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
1605*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(src)
1606*d57664e9SAndroid Build Coastguard Worker << "uncompiled " << file_type
1607*d57664e9SAndroid Build Coastguard Worker << " file passed as argument. Must be "
1608*d57664e9SAndroid Build Coastguard Worker "compiled first into .flat file.");
1609*d57664e9SAndroid Build Coastguard Worker return false;
1610*d57664e9SAndroid Build Coastguard Worker } else if (!util::EndsWith(src.path, ".apc") && !util::EndsWith(src.path, ".flat")) {
1611*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
1612*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Warn(android::DiagMessage(src) << "ignoring unrecognized file");
1613*d57664e9SAndroid Build Coastguard Worker return true;
1614*d57664e9SAndroid Build Coastguard Worker }
1615*d57664e9SAndroid Build Coastguard Worker }
1616*d57664e9SAndroid Build Coastguard Worker
1617*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::InputStream> input_stream = file->OpenInputStream();
1618*d57664e9SAndroid Build Coastguard Worker if (input_stream == nullptr) {
1619*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(src) << "failed to open file");
1620*d57664e9SAndroid Build Coastguard Worker return false;
1621*d57664e9SAndroid Build Coastguard Worker }
1622*d57664e9SAndroid Build Coastguard Worker
1623*d57664e9SAndroid Build Coastguard Worker if (input_stream->HadError()) {
1624*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(src)
1625*d57664e9SAndroid Build Coastguard Worker << "failed to open file: " << input_stream->GetError());
1626*d57664e9SAndroid Build Coastguard Worker return false;
1627*d57664e9SAndroid Build Coastguard Worker }
1628*d57664e9SAndroid Build Coastguard Worker
1629*d57664e9SAndroid Build Coastguard Worker ContainerReaderEntry* entry;
1630*d57664e9SAndroid Build Coastguard Worker ContainerReader reader(input_stream.get());
1631*d57664e9SAndroid Build Coastguard Worker
1632*d57664e9SAndroid Build Coastguard Worker if (reader.HadError()) {
1633*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(src)
1634*d57664e9SAndroid Build Coastguard Worker << "failed to read file: " << reader.GetError());
1635*d57664e9SAndroid Build Coastguard Worker return false;
1636*d57664e9SAndroid Build Coastguard Worker }
1637*d57664e9SAndroid Build Coastguard Worker
1638*d57664e9SAndroid Build Coastguard Worker while ((entry = reader.Next()) != nullptr) {
1639*d57664e9SAndroid Build Coastguard Worker if (entry->Type() == ContainerEntryType::kResTable) {
1640*d57664e9SAndroid Build Coastguard Worker TRACE_NAME(std::string("Process ResTable:") + file->GetSource().path);
1641*d57664e9SAndroid Build Coastguard Worker pb::ResourceTable pb_table;
1642*d57664e9SAndroid Build Coastguard Worker if (!entry->GetResTable(&pb_table)) {
1643*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
1644*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(src) << "failed to read resource table: " << entry->GetError());
1645*d57664e9SAndroid Build Coastguard Worker return false;
1646*d57664e9SAndroid Build Coastguard Worker }
1647*d57664e9SAndroid Build Coastguard Worker
1648*d57664e9SAndroid Build Coastguard Worker ResourceTable table;
1649*d57664e9SAndroid Build Coastguard Worker std::string error;
1650*d57664e9SAndroid Build Coastguard Worker if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) {
1651*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(src)
1652*d57664e9SAndroid Build Coastguard Worker << "failed to deserialize resource table: " << error);
1653*d57664e9SAndroid Build Coastguard Worker return false;
1654*d57664e9SAndroid Build Coastguard Worker }
1655*d57664e9SAndroid Build Coastguard Worker
1656*d57664e9SAndroid Build Coastguard Worker if (!table_merger_->Merge(src, &table, override)) {
1657*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(src)
1658*d57664e9SAndroid Build Coastguard Worker << "failed to merge resource table");
1659*d57664e9SAndroid Build Coastguard Worker return false;
1660*d57664e9SAndroid Build Coastguard Worker }
1661*d57664e9SAndroid Build Coastguard Worker } else if (entry->Type() == ContainerEntryType::kResFile) {
1662*d57664e9SAndroid Build Coastguard Worker TRACE_NAME(std::string("Process ResFile") + file->GetSource().path);
1663*d57664e9SAndroid Build Coastguard Worker pb::internal::CompiledFile pb_compiled_file;
1664*d57664e9SAndroid Build Coastguard Worker off64_t offset;
1665*d57664e9SAndroid Build Coastguard Worker size_t len;
1666*d57664e9SAndroid Build Coastguard Worker if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &len)) {
1667*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
1668*d57664e9SAndroid Build Coastguard Worker android::DiagMessage(src) << "failed to get resource file: " << entry->GetError());
1669*d57664e9SAndroid Build Coastguard Worker return false;
1670*d57664e9SAndroid Build Coastguard Worker }
1671*d57664e9SAndroid Build Coastguard Worker
1672*d57664e9SAndroid Build Coastguard Worker ResourceFile resource_file;
1673*d57664e9SAndroid Build Coastguard Worker std::string error;
1674*d57664e9SAndroid Build Coastguard Worker if (!DeserializeCompiledFileFromPb(pb_compiled_file, &resource_file, &error)) {
1675*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage(src)
1676*d57664e9SAndroid Build Coastguard Worker << "failed to read compiled header: " << error);
1677*d57664e9SAndroid Build Coastguard Worker return false;
1678*d57664e9SAndroid Build Coastguard Worker }
1679*d57664e9SAndroid Build Coastguard Worker
1680*d57664e9SAndroid Build Coastguard Worker if (!MergeCompiledFile(resource_file, file->CreateFileSegment(offset, len), override)) {
1681*d57664e9SAndroid Build Coastguard Worker return false;
1682*d57664e9SAndroid Build Coastguard Worker }
1683*d57664e9SAndroid Build Coastguard Worker }
1684*d57664e9SAndroid Build Coastguard Worker }
1685*d57664e9SAndroid Build Coastguard Worker return true;
1686*d57664e9SAndroid Build Coastguard Worker }
1687*d57664e9SAndroid Build Coastguard Worker
CopyAssetsDirsToApk(IArchiveWriter * writer)1688*d57664e9SAndroid Build Coastguard Worker bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
1689*d57664e9SAndroid Build Coastguard Worker std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
1690*d57664e9SAndroid Build Coastguard Worker for (const std::string& assets_dir : options_.assets_dirs) {
1691*d57664e9SAndroid Build Coastguard Worker std::optional<std::vector<std::string>> files =
1692*d57664e9SAndroid Build Coastguard Worker file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
1693*d57664e9SAndroid Build Coastguard Worker if (!files) {
1694*d57664e9SAndroid Build Coastguard Worker return false;
1695*d57664e9SAndroid Build Coastguard Worker }
1696*d57664e9SAndroid Build Coastguard Worker
1697*d57664e9SAndroid Build Coastguard Worker for (const std::string& file : files.value()) {
1698*d57664e9SAndroid Build Coastguard Worker std::string full_key = "assets/" + file;
1699*d57664e9SAndroid Build Coastguard Worker std::string full_path = assets_dir;
1700*d57664e9SAndroid Build Coastguard Worker file::AppendPath(&full_path, file);
1701*d57664e9SAndroid Build Coastguard Worker
1702*d57664e9SAndroid Build Coastguard Worker auto iter = merged_assets.find(full_key);
1703*d57664e9SAndroid Build Coastguard Worker if (iter == merged_assets.end()) {
1704*d57664e9SAndroid Build Coastguard Worker merged_assets.emplace(std::move(full_key), util::make_unique<io::RegularFile>(
1705*d57664e9SAndroid Build Coastguard Worker android::Source(std::move(full_path))));
1706*d57664e9SAndroid Build Coastguard Worker } else if (context_->IsVerbose()) {
1707*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Warn(android::DiagMessage(iter->second->GetSource())
1708*d57664e9SAndroid Build Coastguard Worker << "asset file overrides '" << full_path << "'");
1709*d57664e9SAndroid Build Coastguard Worker }
1710*d57664e9SAndroid Build Coastguard Worker }
1711*d57664e9SAndroid Build Coastguard Worker }
1712*d57664e9SAndroid Build Coastguard Worker
1713*d57664e9SAndroid Build Coastguard Worker for (auto& entry : merged_assets) {
1714*d57664e9SAndroid Build Coastguard Worker uint32_t compression_flags = GetCompressionFlags(entry.first, options_);
1715*d57664e9SAndroid Build Coastguard Worker if (!io::CopyFileToArchive(context_, entry.second.get(), entry.first, compression_flags,
1716*d57664e9SAndroid Build Coastguard Worker writer)) {
1717*d57664e9SAndroid Build Coastguard Worker return false;
1718*d57664e9SAndroid Build Coastguard Worker }
1719*d57664e9SAndroid Build Coastguard Worker }
1720*d57664e9SAndroid Build Coastguard Worker return true;
1721*d57664e9SAndroid Build Coastguard Worker }
1722*d57664e9SAndroid Build Coastguard Worker
ResolveTableEntry(LinkContext * context,ResourceTable * table,Reference * reference)1723*d57664e9SAndroid Build Coastguard Worker ResourceEntry* ResolveTableEntry(LinkContext* context, ResourceTable* table,
1724*d57664e9SAndroid Build Coastguard Worker Reference* reference) {
1725*d57664e9SAndroid Build Coastguard Worker if (!reference || !reference->name) {
1726*d57664e9SAndroid Build Coastguard Worker return nullptr;
1727*d57664e9SAndroid Build Coastguard Worker }
1728*d57664e9SAndroid Build Coastguard Worker auto name_ref = ResourceNameRef(reference->name.value());
1729*d57664e9SAndroid Build Coastguard Worker if (name_ref.package.empty()) {
1730*d57664e9SAndroid Build Coastguard Worker name_ref.package = context->GetCompilationPackage();
1731*d57664e9SAndroid Build Coastguard Worker }
1732*d57664e9SAndroid Build Coastguard Worker const auto search_result = table->FindResource(name_ref);
1733*d57664e9SAndroid Build Coastguard Worker if (!search_result) {
1734*d57664e9SAndroid Build Coastguard Worker return nullptr;
1735*d57664e9SAndroid Build Coastguard Worker }
1736*d57664e9SAndroid Build Coastguard Worker return search_result.value().entry;
1737*d57664e9SAndroid Build Coastguard Worker }
1738*d57664e9SAndroid Build Coastguard Worker
AliasAdaptiveIcon(xml::XmlResource * manifest,ResourceTable * table)1739*d57664e9SAndroid Build Coastguard Worker void AliasAdaptiveIcon(xml::XmlResource* manifest, ResourceTable* table) {
1740*d57664e9SAndroid Build Coastguard Worker const xml::Element* application = manifest->root->FindChild("", "application");
1741*d57664e9SAndroid Build Coastguard Worker if (!application) {
1742*d57664e9SAndroid Build Coastguard Worker return;
1743*d57664e9SAndroid Build Coastguard Worker }
1744*d57664e9SAndroid Build Coastguard Worker
1745*d57664e9SAndroid Build Coastguard Worker const xml::Attribute* icon = application->FindAttribute(xml::kSchemaAndroid, "icon");
1746*d57664e9SAndroid Build Coastguard Worker const xml::Attribute* round_icon = application->FindAttribute(xml::kSchemaAndroid, "roundIcon");
1747*d57664e9SAndroid Build Coastguard Worker if (!icon || !round_icon) {
1748*d57664e9SAndroid Build Coastguard Worker return;
1749*d57664e9SAndroid Build Coastguard Worker }
1750*d57664e9SAndroid Build Coastguard Worker
1751*d57664e9SAndroid Build Coastguard Worker // Find the icon resource defined within the application.
1752*d57664e9SAndroid Build Coastguard Worker const auto icon_reference = ValueCast<Reference>(icon->compiled_value.get());
1753*d57664e9SAndroid Build Coastguard Worker const auto icon_entry = ResolveTableEntry(context_, table, icon_reference);
1754*d57664e9SAndroid Build Coastguard Worker if (!icon_entry) {
1755*d57664e9SAndroid Build Coastguard Worker return;
1756*d57664e9SAndroid Build Coastguard Worker }
1757*d57664e9SAndroid Build Coastguard Worker
1758*d57664e9SAndroid Build Coastguard Worker int icon_max_sdk = 0;
1759*d57664e9SAndroid Build Coastguard Worker for (auto& config_value : icon_entry->values) {
1760*d57664e9SAndroid Build Coastguard Worker icon_max_sdk = (icon_max_sdk < config_value->config.sdkVersion)
1761*d57664e9SAndroid Build Coastguard Worker ? config_value->config.sdkVersion : icon_max_sdk;
1762*d57664e9SAndroid Build Coastguard Worker }
1763*d57664e9SAndroid Build Coastguard Worker if (icon_max_sdk < SDK_O) {
1764*d57664e9SAndroid Build Coastguard Worker // Adaptive icons must be versioned with v26 qualifiers, so this is not an adaptive icon.
1765*d57664e9SAndroid Build Coastguard Worker return;
1766*d57664e9SAndroid Build Coastguard Worker }
1767*d57664e9SAndroid Build Coastguard Worker
1768*d57664e9SAndroid Build Coastguard Worker // Find the roundIcon resource defined within the application.
1769*d57664e9SAndroid Build Coastguard Worker const auto round_icon_reference = ValueCast<Reference>(round_icon->compiled_value.get());
1770*d57664e9SAndroid Build Coastguard Worker const auto round_icon_entry = ResolveTableEntry(context_, table, round_icon_reference);
1771*d57664e9SAndroid Build Coastguard Worker if (!round_icon_entry) {
1772*d57664e9SAndroid Build Coastguard Worker return;
1773*d57664e9SAndroid Build Coastguard Worker }
1774*d57664e9SAndroid Build Coastguard Worker
1775*d57664e9SAndroid Build Coastguard Worker int round_icon_max_sdk = 0;
1776*d57664e9SAndroid Build Coastguard Worker for (auto& config_value : round_icon_entry->values) {
1777*d57664e9SAndroid Build Coastguard Worker round_icon_max_sdk = (round_icon_max_sdk < config_value->config.sdkVersion)
1778*d57664e9SAndroid Build Coastguard Worker ? config_value->config.sdkVersion : round_icon_max_sdk;
1779*d57664e9SAndroid Build Coastguard Worker }
1780*d57664e9SAndroid Build Coastguard Worker if (round_icon_max_sdk >= SDK_O) {
1781*d57664e9SAndroid Build Coastguard Worker // The developer explicitly used a v26 compatible drawable as the roundIcon, meaning we should
1782*d57664e9SAndroid Build Coastguard Worker // not generate an alias to the icon drawable.
1783*d57664e9SAndroid Build Coastguard Worker return;
1784*d57664e9SAndroid Build Coastguard Worker }
1785*d57664e9SAndroid Build Coastguard Worker
1786*d57664e9SAndroid Build Coastguard Worker // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
1787*d57664e9SAndroid Build Coastguard Worker for (auto& config_value : icon_entry->values) {
1788*d57664e9SAndroid Build Coastguard Worker if (config_value->config.sdkVersion < SDK_O) {
1789*d57664e9SAndroid Build Coastguard Worker continue;
1790*d57664e9SAndroid Build Coastguard Worker }
1791*d57664e9SAndroid Build Coastguard Worker
1792*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
1793*d57664e9SAndroid Build Coastguard Worker << "generating " << round_icon_reference->name.value()
1794*d57664e9SAndroid Build Coastguard Worker << " with config \"" << config_value->config
1795*d57664e9SAndroid Build Coastguard Worker << "\" for round icon compatibility");
1796*d57664e9SAndroid Build Coastguard Worker
1797*d57664e9SAndroid Build Coastguard Worker CloningValueTransformer cloner(&table->string_pool);
1798*d57664e9SAndroid Build Coastguard Worker auto value = icon_reference->Transform(cloner);
1799*d57664e9SAndroid Build Coastguard Worker auto round_config_value =
1800*d57664e9SAndroid Build Coastguard Worker round_icon_entry->FindOrCreateValue(config_value->config, config_value->product);
1801*d57664e9SAndroid Build Coastguard Worker round_config_value->value = std::move(value);
1802*d57664e9SAndroid Build Coastguard Worker }
1803*d57664e9SAndroid Build Coastguard Worker }
1804*d57664e9SAndroid Build Coastguard Worker
VerifySharedUserId(xml::XmlResource * manifest,ResourceTable * table)1805*d57664e9SAndroid Build Coastguard Worker bool VerifySharedUserId(xml::XmlResource* manifest, ResourceTable* table) {
1806*d57664e9SAndroid Build Coastguard Worker const xml::Element* manifest_el = xml::FindRootElement(manifest->root.get());
1807*d57664e9SAndroid Build Coastguard Worker if (manifest_el == nullptr) {
1808*d57664e9SAndroid Build Coastguard Worker return true;
1809*d57664e9SAndroid Build Coastguard Worker }
1810*d57664e9SAndroid Build Coastguard Worker if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
1811*d57664e9SAndroid Build Coastguard Worker return true;
1812*d57664e9SAndroid Build Coastguard Worker }
1813*d57664e9SAndroid Build Coastguard Worker const xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "sharedUserId");
1814*d57664e9SAndroid Build Coastguard Worker if (!attr) {
1815*d57664e9SAndroid Build Coastguard Worker return true;
1816*d57664e9SAndroid Build Coastguard Worker }
1817*d57664e9SAndroid Build Coastguard Worker const auto validate = [&](const std::string& shared_user_id) -> bool {
1818*d57664e9SAndroid Build Coastguard Worker if (util::IsAndroidSharedUserId(context_->GetCompilationPackage(), shared_user_id)) {
1819*d57664e9SAndroid Build Coastguard Worker return true;
1820*d57664e9SAndroid Build Coastguard Worker }
1821*d57664e9SAndroid Build Coastguard Worker android::DiagMessage error_msg(manifest_el->line_number);
1822*d57664e9SAndroid Build Coastguard Worker error_msg << "attribute 'sharedUserId' in <manifest> tag is not a valid shared user id: '"
1823*d57664e9SAndroid Build Coastguard Worker << shared_user_id << "'";
1824*d57664e9SAndroid Build Coastguard Worker if (options_.manifest_fixer_options.warn_validation) {
1825*d57664e9SAndroid Build Coastguard Worker // Treat the error only as a warning.
1826*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Warn(error_msg);
1827*d57664e9SAndroid Build Coastguard Worker return true;
1828*d57664e9SAndroid Build Coastguard Worker }
1829*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(error_msg);
1830*d57664e9SAndroid Build Coastguard Worker return false;
1831*d57664e9SAndroid Build Coastguard Worker };
1832*d57664e9SAndroid Build Coastguard Worker // If attr->compiled_value is not null, check if it is a ref
1833*d57664e9SAndroid Build Coastguard Worker if (attr->compiled_value) {
1834*d57664e9SAndroid Build Coastguard Worker const auto ref = ValueCast<Reference>(attr->compiled_value.get());
1835*d57664e9SAndroid Build Coastguard Worker if (ref == nullptr) {
1836*d57664e9SAndroid Build Coastguard Worker return true;
1837*d57664e9SAndroid Build Coastguard Worker }
1838*d57664e9SAndroid Build Coastguard Worker const auto shared_user_id_entry = ResolveTableEntry(context_, table, ref);
1839*d57664e9SAndroid Build Coastguard Worker if (!shared_user_id_entry) {
1840*d57664e9SAndroid Build Coastguard Worker return true;
1841*d57664e9SAndroid Build Coastguard Worker }
1842*d57664e9SAndroid Build Coastguard Worker for (const auto& value : shared_user_id_entry->values) {
1843*d57664e9SAndroid Build Coastguard Worker const auto str_value = ValueCast<String>(value->value.get());
1844*d57664e9SAndroid Build Coastguard Worker if (str_value != nullptr && !validate(*str_value->value)) {
1845*d57664e9SAndroid Build Coastguard Worker return false;
1846*d57664e9SAndroid Build Coastguard Worker }
1847*d57664e9SAndroid Build Coastguard Worker }
1848*d57664e9SAndroid Build Coastguard Worker return true;
1849*d57664e9SAndroid Build Coastguard Worker }
1850*d57664e9SAndroid Build Coastguard Worker
1851*d57664e9SAndroid Build Coastguard Worker // Fallback to checking the raw value
1852*d57664e9SAndroid Build Coastguard Worker return validate(attr->value);
1853*d57664e9SAndroid Build Coastguard Worker }
1854*d57664e9SAndroid Build Coastguard Worker
1855*d57664e9SAndroid Build Coastguard Worker class FlagDisabledStringVisitor : public DescendingValueVisitor {
1856*d57664e9SAndroid Build Coastguard Worker public:
1857*d57664e9SAndroid Build Coastguard Worker using DescendingValueVisitor::Visit;
1858*d57664e9SAndroid Build Coastguard Worker
FlagDisabledStringVisitor(android::StringPool & string_pool)1859*d57664e9SAndroid Build Coastguard Worker explicit FlagDisabledStringVisitor(android::StringPool& string_pool)
1860*d57664e9SAndroid Build Coastguard Worker : string_pool_(string_pool) {
1861*d57664e9SAndroid Build Coastguard Worker }
1862*d57664e9SAndroid Build Coastguard Worker
Visit(RawString * value)1863*d57664e9SAndroid Build Coastguard Worker void Visit(RawString* value) override {
1864*d57664e9SAndroid Build Coastguard Worker value->value = string_pool_.MakeRef("");
1865*d57664e9SAndroid Build Coastguard Worker }
1866*d57664e9SAndroid Build Coastguard Worker
Visit(String * value)1867*d57664e9SAndroid Build Coastguard Worker void Visit(String* value) override {
1868*d57664e9SAndroid Build Coastguard Worker value->value = string_pool_.MakeRef("");
1869*d57664e9SAndroid Build Coastguard Worker }
1870*d57664e9SAndroid Build Coastguard Worker
Visit(StyledString * value)1871*d57664e9SAndroid Build Coastguard Worker void Visit(StyledString* value) override {
1872*d57664e9SAndroid Build Coastguard Worker value->value = string_pool_.MakeRef(android::StyleString{{""}, {}});
1873*d57664e9SAndroid Build Coastguard Worker }
1874*d57664e9SAndroid Build Coastguard Worker
1875*d57664e9SAndroid Build Coastguard Worker private:
1876*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(FlagDisabledStringVisitor);
1877*d57664e9SAndroid Build Coastguard Worker android::StringPool& string_pool_;
1878*d57664e9SAndroid Build Coastguard Worker };
1879*d57664e9SAndroid Build Coastguard Worker
1880*d57664e9SAndroid Build Coastguard Worker // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
1881*d57664e9SAndroid Build Coastguard Worker // to the IArchiveWriter.
WriteApk(IArchiveWriter * writer,proguard::KeepSet * keep_set,xml::XmlResource * manifest,ResourceTable * table)1882*d57664e9SAndroid Build Coastguard Worker bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
1883*d57664e9SAndroid Build Coastguard Worker ResourceTable* table) {
1884*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
1885*d57664e9SAndroid Build Coastguard Worker
1886*d57664e9SAndroid Build Coastguard Worker FlagDisabledStringVisitor visitor(table->string_pool);
1887*d57664e9SAndroid Build Coastguard Worker
1888*d57664e9SAndroid Build Coastguard Worker for (auto& package : table->packages) {
1889*d57664e9SAndroid Build Coastguard Worker for (auto& type : package->types) {
1890*d57664e9SAndroid Build Coastguard Worker for (auto& entry : type->entries) {
1891*d57664e9SAndroid Build Coastguard Worker for (auto& config_value : entry->values) {
1892*d57664e9SAndroid Build Coastguard Worker if (config_value->value->GetFlagStatus() == FlagStatus::Disabled) {
1893*d57664e9SAndroid Build Coastguard Worker config_value->value->Accept(&visitor);
1894*d57664e9SAndroid Build Coastguard Worker }
1895*d57664e9SAndroid Build Coastguard Worker }
1896*d57664e9SAndroid Build Coastguard Worker }
1897*d57664e9SAndroid Build Coastguard Worker }
1898*d57664e9SAndroid Build Coastguard Worker }
1899*d57664e9SAndroid Build Coastguard Worker
1900*d57664e9SAndroid Build Coastguard Worker if (!FlagDisabledResourceRemover{}.Consume(context_, table)) {
1901*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
1902*d57664e9SAndroid Build Coastguard Worker << "failed removing resources behind disabled flags");
1903*d57664e9SAndroid Build Coastguard Worker return 1;
1904*d57664e9SAndroid Build Coastguard Worker }
1905*d57664e9SAndroid Build Coastguard Worker
1906*d57664e9SAndroid Build Coastguard Worker const bool keep_raw_values = (context_->GetPackageType() == PackageType::kStaticLib)
1907*d57664e9SAndroid Build Coastguard Worker || options_.keep_raw_values;
1908*d57664e9SAndroid Build Coastguard Worker bool result = FlattenXml(context_, *manifest, kAndroidManifestPath, keep_raw_values,
1909*d57664e9SAndroid Build Coastguard Worker true /*utf16*/, options_.output_format, writer);
1910*d57664e9SAndroid Build Coastguard Worker if (!result) {
1911*d57664e9SAndroid Build Coastguard Worker return false;
1912*d57664e9SAndroid Build Coastguard Worker }
1913*d57664e9SAndroid Build Coastguard Worker
1914*d57664e9SAndroid Build Coastguard Worker // When a developer specifies an adaptive application icon, and a non-adaptive round application
1915*d57664e9SAndroid Build Coastguard Worker // icon, create an alias from the round icon to the regular icon for v26 APIs and up. We do this
1916*d57664e9SAndroid Build Coastguard Worker // because certain devices prefer android:roundIcon over android:icon regardless of the API
1917*d57664e9SAndroid Build Coastguard Worker // levels of the drawables set for either. This auto-aliasing behaviour allows an app to prefer
1918*d57664e9SAndroid Build Coastguard Worker // the android:roundIcon on API 25 devices, and prefer the adaptive icon on API 26 devices.
1919*d57664e9SAndroid Build Coastguard Worker // See (b/34829129)
1920*d57664e9SAndroid Build Coastguard Worker AliasAdaptiveIcon(manifest, table);
1921*d57664e9SAndroid Build Coastguard Worker
1922*d57664e9SAndroid Build Coastguard Worker // Verify the shared user id here to handle the case of reference value.
1923*d57664e9SAndroid Build Coastguard Worker if (!VerifySharedUserId(manifest, table)) {
1924*d57664e9SAndroid Build Coastguard Worker return false;
1925*d57664e9SAndroid Build Coastguard Worker }
1926*d57664e9SAndroid Build Coastguard Worker
1927*d57664e9SAndroid Build Coastguard Worker ResourceFileFlattenerOptions file_flattener_options;
1928*d57664e9SAndroid Build Coastguard Worker file_flattener_options.keep_raw_values = keep_raw_values;
1929*d57664e9SAndroid Build Coastguard Worker file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything;
1930*d57664e9SAndroid Build Coastguard Worker file_flattener_options.extensions_to_not_compress = options_.extensions_to_not_compress;
1931*d57664e9SAndroid Build Coastguard Worker file_flattener_options.regex_to_not_compress = options_.regex_to_not_compress;
1932*d57664e9SAndroid Build Coastguard Worker file_flattener_options.no_auto_version = options_.no_auto_version;
1933*d57664e9SAndroid Build Coastguard Worker file_flattener_options.no_version_vectors = options_.no_version_vectors;
1934*d57664e9SAndroid Build Coastguard Worker file_flattener_options.no_version_transitions = options_.no_version_transitions;
1935*d57664e9SAndroid Build Coastguard Worker file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces;
1936*d57664e9SAndroid Build Coastguard Worker file_flattener_options.update_proguard_spec =
1937*d57664e9SAndroid Build Coastguard Worker static_cast<bool>(options_.generate_proguard_rules_path);
1938*d57664e9SAndroid Build Coastguard Worker file_flattener_options.output_format = options_.output_format;
1939*d57664e9SAndroid Build Coastguard Worker file_flattener_options.do_not_fail_on_missing_resources = options_.merge_only;
1940*d57664e9SAndroid Build Coastguard Worker file_flattener_options.feature_flag_values = options_.feature_flag_values;
1941*d57664e9SAndroid Build Coastguard Worker
1942*d57664e9SAndroid Build Coastguard Worker ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
1943*d57664e9SAndroid Build Coastguard Worker if (!file_flattener.Flatten(table, writer)) {
1944*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed linking file resources");
1945*d57664e9SAndroid Build Coastguard Worker return false;
1946*d57664e9SAndroid Build Coastguard Worker }
1947*d57664e9SAndroid Build Coastguard Worker
1948*d57664e9SAndroid Build Coastguard Worker // Hack to fix b/68820737.
1949*d57664e9SAndroid Build Coastguard Worker // We need to modify the ResourceTable's package name, but that should NOT affect
1950*d57664e9SAndroid Build Coastguard Worker // anything else being generated, which includes the Java classes.
1951*d57664e9SAndroid Build Coastguard Worker // If required, the package name is modifed before flattening, and then modified back
1952*d57664e9SAndroid Build Coastguard Worker // to its original name.
1953*d57664e9SAndroid Build Coastguard Worker ResourceTablePackage* package_to_rewrite = nullptr;
1954*d57664e9SAndroid Build Coastguard Worker // Pre-O, the platform treats negative resource IDs [those with a package ID of 0x80
1955*d57664e9SAndroid Build Coastguard Worker // or higher] as invalid. In order to work around this limitation, we allow the use
1956*d57664e9SAndroid Build Coastguard Worker // of traditionally reserved resource IDs [those between 0x02 and 0x7E]. Allow the
1957*d57664e9SAndroid Build Coastguard Worker // definition of what a valid "split" package ID is to account for this.
1958*d57664e9SAndroid Build Coastguard Worker const bool isSplitPackage = (options_.allow_reserved_package_id &&
1959*d57664e9SAndroid Build Coastguard Worker context_->GetPackageId() != kAppPackageId &&
1960*d57664e9SAndroid Build Coastguard Worker context_->GetPackageId() != kFrameworkPackageId)
1961*d57664e9SAndroid Build Coastguard Worker || (!options_.allow_reserved_package_id && context_->GetPackageId() > kAppPackageId);
1962*d57664e9SAndroid Build Coastguard Worker if (isSplitPackage && included_feature_base_ == context_->GetCompilationPackage()) {
1963*d57664e9SAndroid Build Coastguard Worker // The base APK is included, and this is a feature split. If the base package is
1964*d57664e9SAndroid Build Coastguard Worker // the same as this package, then we are building an old style Android Instant Apps feature
1965*d57664e9SAndroid Build Coastguard Worker // split and must apply this workaround to avoid requiring namespaces support.
1966*d57664e9SAndroid Build Coastguard Worker if (!table->packages.empty() &&
1967*d57664e9SAndroid Build Coastguard Worker table->packages.back()->name == context_->GetCompilationPackage()) {
1968*d57664e9SAndroid Build Coastguard Worker package_to_rewrite = table->packages.back().get();
1969*d57664e9SAndroid Build Coastguard Worker std::string new_package_name =
1970*d57664e9SAndroid Build Coastguard Worker StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
1971*d57664e9SAndroid Build Coastguard Worker app_info_.split_name.value_or("feature").c_str());
1972*d57664e9SAndroid Build Coastguard Worker
1973*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
1974*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(
1975*d57664e9SAndroid Build Coastguard Worker android::DiagMessage() << "rewriting resource package name for feature split to '"
1976*d57664e9SAndroid Build Coastguard Worker << new_package_name << "'");
1977*d57664e9SAndroid Build Coastguard Worker }
1978*d57664e9SAndroid Build Coastguard Worker package_to_rewrite->name = new_package_name;
1979*d57664e9SAndroid Build Coastguard Worker }
1980*d57664e9SAndroid Build Coastguard Worker }
1981*d57664e9SAndroid Build Coastguard Worker
1982*d57664e9SAndroid Build Coastguard Worker bool success = FlattenTable(table, options_.output_format, writer);
1983*d57664e9SAndroid Build Coastguard Worker
1984*d57664e9SAndroid Build Coastguard Worker if (package_to_rewrite != nullptr) {
1985*d57664e9SAndroid Build Coastguard Worker // Change the name back.
1986*d57664e9SAndroid Build Coastguard Worker package_to_rewrite->name = context_->GetCompilationPackage();
1987*d57664e9SAndroid Build Coastguard Worker
1988*d57664e9SAndroid Build Coastguard Worker // TableFlattener creates an `included_packages_` mapping entry for each package with a
1989*d57664e9SAndroid Build Coastguard Worker // non-standard package id (not 0x01 or 0x7f). Since this is a feature split and not a shared
1990*d57664e9SAndroid Build Coastguard Worker // library, do not include a mapping from the feature package name to the feature package id
1991*d57664e9SAndroid Build Coastguard Worker // in the feature's dynamic reference table.
1992*d57664e9SAndroid Build Coastguard Worker table->included_packages_.erase(context_->GetPackageId());
1993*d57664e9SAndroid Build Coastguard Worker }
1994*d57664e9SAndroid Build Coastguard Worker
1995*d57664e9SAndroid Build Coastguard Worker if (!success) {
1996*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to write resource table");
1997*d57664e9SAndroid Build Coastguard Worker }
1998*d57664e9SAndroid Build Coastguard Worker return success;
1999*d57664e9SAndroid Build Coastguard Worker }
2000*d57664e9SAndroid Build Coastguard Worker
Run(const std::vector<std::string> & input_files)2001*d57664e9SAndroid Build Coastguard Worker int Run(const std::vector<std::string>& input_files) {
2002*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
2003*d57664e9SAndroid Build Coastguard Worker // Load the AndroidManifest.xml
2004*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> manifest_xml =
2005*d57664e9SAndroid Build Coastguard Worker LoadXml(options_.manifest_path, context_->GetDiagnostics());
2006*d57664e9SAndroid Build Coastguard Worker if (!manifest_xml) {
2007*d57664e9SAndroid Build Coastguard Worker return 1;
2008*d57664e9SAndroid Build Coastguard Worker }
2009*d57664e9SAndroid Build Coastguard Worker
2010*d57664e9SAndroid Build Coastguard Worker // First extract the Package name without modifying it (via --rename-manifest-package).
2011*d57664e9SAndroid Build Coastguard Worker if (std::optional<AppInfo> maybe_app_info =
2012*d57664e9SAndroid Build Coastguard Worker ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
2013*d57664e9SAndroid Build Coastguard Worker const AppInfo& app_info = maybe_app_info.value();
2014*d57664e9SAndroid Build Coastguard Worker context_->SetCompilationPackage(app_info.package);
2015*d57664e9SAndroid Build Coastguard Worker }
2016*d57664e9SAndroid Build Coastguard Worker
2017*d57664e9SAndroid Build Coastguard Worker // Determine the package name under which to merge resources.
2018*d57664e9SAndroid Build Coastguard Worker if (options_.rename_resources_package) {
2019*d57664e9SAndroid Build Coastguard Worker if (!options_.custom_java_package) {
2020*d57664e9SAndroid Build Coastguard Worker // Generate the R.java under the original package name instead of the package name specified
2021*d57664e9SAndroid Build Coastguard Worker // through --rename-resources-package.
2022*d57664e9SAndroid Build Coastguard Worker options_.custom_java_package = context_->GetCompilationPackage();
2023*d57664e9SAndroid Build Coastguard Worker }
2024*d57664e9SAndroid Build Coastguard Worker context_->SetCompilationPackage(options_.rename_resources_package.value());
2025*d57664e9SAndroid Build Coastguard Worker }
2026*d57664e9SAndroid Build Coastguard Worker
2027*d57664e9SAndroid Build Coastguard Worker // Now that the compilation package is set, load the dependencies. This will also extract
2028*d57664e9SAndroid Build Coastguard Worker // the Android framework's versionCode and versionName, if they exist.
2029*d57664e9SAndroid Build Coastguard Worker if (!LoadSymbolsFromIncludePaths()) {
2030*d57664e9SAndroid Build Coastguard Worker return 1;
2031*d57664e9SAndroid Build Coastguard Worker }
2032*d57664e9SAndroid Build Coastguard Worker
2033*d57664e9SAndroid Build Coastguard Worker ManifestFixer manifest_fixer(options_.manifest_fixer_options);
2034*d57664e9SAndroid Build Coastguard Worker if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
2035*d57664e9SAndroid Build Coastguard Worker return 1;
2036*d57664e9SAndroid Build Coastguard Worker }
2037*d57664e9SAndroid Build Coastguard Worker
2038*d57664e9SAndroid Build Coastguard Worker std::optional<AppInfo> maybe_app_info =
2039*d57664e9SAndroid Build Coastguard Worker ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics());
2040*d57664e9SAndroid Build Coastguard Worker if (!maybe_app_info) {
2041*d57664e9SAndroid Build Coastguard Worker return 1;
2042*d57664e9SAndroid Build Coastguard Worker }
2043*d57664e9SAndroid Build Coastguard Worker
2044*d57664e9SAndroid Build Coastguard Worker app_info_ = maybe_app_info.value();
2045*d57664e9SAndroid Build Coastguard Worker context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or(0));
2046*d57664e9SAndroid Build Coastguard Worker
2047*d57664e9SAndroid Build Coastguard Worker context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
2048*d57664e9SAndroid Build Coastguard Worker context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
2049*d57664e9SAndroid Build Coastguard Worker
2050*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> pre_flags_filter_manifest_xml = manifest_xml->Clone();
2051*d57664e9SAndroid Build Coastguard Worker
2052*d57664e9SAndroid Build Coastguard Worker FeatureFlagsFilterOptions flags_filter_options;
2053*d57664e9SAndroid Build Coastguard Worker if (context_->GetMinSdkVersion() > SDK_UPSIDE_DOWN_CAKE) {
2054*d57664e9SAndroid Build Coastguard Worker // For API version > U, PackageManager will dynamically read the flag values and disable
2055*d57664e9SAndroid Build Coastguard Worker // manifest elements accordingly when parsing the manifest.
2056*d57664e9SAndroid Build Coastguard Worker // For API version <= U, we remove disabled elements from the manifest with the filter.
2057*d57664e9SAndroid Build Coastguard Worker flags_filter_options.remove_disabled_elements = false;
2058*d57664e9SAndroid Build Coastguard Worker flags_filter_options.flags_must_have_value = false;
2059*d57664e9SAndroid Build Coastguard Worker }
2060*d57664e9SAndroid Build Coastguard Worker FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options);
2061*d57664e9SAndroid Build Coastguard Worker if (!flags_filter.Consume(context_, manifest_xml.get())) {
2062*d57664e9SAndroid Build Coastguard Worker return 1;
2063*d57664e9SAndroid Build Coastguard Worker }
2064*d57664e9SAndroid Build Coastguard Worker
2065*d57664e9SAndroid Build Coastguard Worker // Override the package ID when it is "android".
2066*d57664e9SAndroid Build Coastguard Worker if (context_->GetCompilationPackage() == "android") {
2067*d57664e9SAndroid Build Coastguard Worker context_->SetPackageId(kAndroidPackageId);
2068*d57664e9SAndroid Build Coastguard Worker
2069*d57664e9SAndroid Build Coastguard Worker // Verify we're building a regular app.
2070*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() != PackageType::kApp) {
2071*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
2072*d57664e9SAndroid Build Coastguard Worker android::DiagMessage() << "package 'android' can only be built as a regular app");
2073*d57664e9SAndroid Build Coastguard Worker return 1;
2074*d57664e9SAndroid Build Coastguard Worker }
2075*d57664e9SAndroid Build Coastguard Worker }
2076*d57664e9SAndroid Build Coastguard Worker
2077*d57664e9SAndroid Build Coastguard Worker TableMergerOptions table_merger_options;
2078*d57664e9SAndroid Build Coastguard Worker table_merger_options.auto_add_overlay = options_.auto_add_overlay;
2079*d57664e9SAndroid Build Coastguard Worker table_merger_options.override_styles_instead_of_overlaying =
2080*d57664e9SAndroid Build Coastguard Worker options_.override_styles_instead_of_overlaying;
2081*d57664e9SAndroid Build Coastguard Worker table_merger_options.strict_visibility = options_.strict_visibility;
2082*d57664e9SAndroid Build Coastguard Worker table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);
2083*d57664e9SAndroid Build Coastguard Worker
2084*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
2085*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
2086*d57664e9SAndroid Build Coastguard Worker << StringPrintf("linking package '%s' using package ID %02x",
2087*d57664e9SAndroid Build Coastguard Worker context_->GetCompilationPackage().data(),
2088*d57664e9SAndroid Build Coastguard Worker context_->GetPackageId()));
2089*d57664e9SAndroid Build Coastguard Worker }
2090*d57664e9SAndroid Build Coastguard Worker
2091*d57664e9SAndroid Build Coastguard Worker // Extract symbols from AndroidManifest.xml, since this isn't merged like the other XML files
2092*d57664e9SAndroid Build Coastguard Worker // in res/**/*.
2093*d57664e9SAndroid Build Coastguard Worker {
2094*d57664e9SAndroid Build Coastguard Worker XmlIdCollector collector;
2095*d57664e9SAndroid Build Coastguard Worker if (!collector.Consume(context_, manifest_xml.get())) {
2096*d57664e9SAndroid Build Coastguard Worker return false;
2097*d57664e9SAndroid Build Coastguard Worker }
2098*d57664e9SAndroid Build Coastguard Worker
2099*d57664e9SAndroid Build Coastguard Worker if (!MergeExportedSymbols(manifest_xml->file.source, manifest_xml->file.exported_symbols)) {
2100*d57664e9SAndroid Build Coastguard Worker return false;
2101*d57664e9SAndroid Build Coastguard Worker }
2102*d57664e9SAndroid Build Coastguard Worker }
2103*d57664e9SAndroid Build Coastguard Worker
2104*d57664e9SAndroid Build Coastguard Worker for (const std::string& input : input_files) {
2105*d57664e9SAndroid Build Coastguard Worker if (!MergePath(input, false)) {
2106*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed parsing input");
2107*d57664e9SAndroid Build Coastguard Worker return 1;
2108*d57664e9SAndroid Build Coastguard Worker }
2109*d57664e9SAndroid Build Coastguard Worker }
2110*d57664e9SAndroid Build Coastguard Worker
2111*d57664e9SAndroid Build Coastguard Worker for (const std::string& input : options_.overlay_files) {
2112*d57664e9SAndroid Build Coastguard Worker if (!MergePath(input, true)) {
2113*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed parsing overlays");
2114*d57664e9SAndroid Build Coastguard Worker return 1;
2115*d57664e9SAndroid Build Coastguard Worker }
2116*d57664e9SAndroid Build Coastguard Worker }
2117*d57664e9SAndroid Build Coastguard Worker
2118*d57664e9SAndroid Build Coastguard Worker if (!VerifyNoExternalPackages()) {
2119*d57664e9SAndroid Build Coastguard Worker return 1;
2120*d57664e9SAndroid Build Coastguard Worker }
2121*d57664e9SAndroid Build Coastguard Worker
2122*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() != PackageType::kStaticLib) {
2123*d57664e9SAndroid Build Coastguard Worker PrivateAttributeMover mover;
2124*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageId() == kAndroidPackageId &&
2125*d57664e9SAndroid Build Coastguard Worker !mover.Consume(context_, &final_table_)) {
2126*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
2127*d57664e9SAndroid Build Coastguard Worker << "failed moving private attributes");
2128*d57664e9SAndroid Build Coastguard Worker return 1;
2129*d57664e9SAndroid Build Coastguard Worker }
2130*d57664e9SAndroid Build Coastguard Worker
2131*d57664e9SAndroid Build Coastguard Worker // Assign IDs if we are building a regular app.
2132*d57664e9SAndroid Build Coastguard Worker IdAssigner id_assigner(&options_.stable_id_map);
2133*d57664e9SAndroid Build Coastguard Worker if (!id_assigner.Consume(context_, &final_table_)) {
2134*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed assigning IDs");
2135*d57664e9SAndroid Build Coastguard Worker return 1;
2136*d57664e9SAndroid Build Coastguard Worker }
2137*d57664e9SAndroid Build Coastguard Worker
2138*d57664e9SAndroid Build Coastguard Worker // Now grab each ID and emit it as a file.
2139*d57664e9SAndroid Build Coastguard Worker if (options_.resource_id_map_path) {
2140*d57664e9SAndroid Build Coastguard Worker for (auto& package : final_table_.packages) {
2141*d57664e9SAndroid Build Coastguard Worker for (auto& type : package->types) {
2142*d57664e9SAndroid Build Coastguard Worker for (auto& entry : type->entries) {
2143*d57664e9SAndroid Build Coastguard Worker ResourceName name(package->name, type->named_type, entry->name);
2144*d57664e9SAndroid Build Coastguard Worker // The IDs are guaranteed to exist.
2145*d57664e9SAndroid Build Coastguard Worker options_.stable_id_map[std::move(name)] = entry->id.value();
2146*d57664e9SAndroid Build Coastguard Worker }
2147*d57664e9SAndroid Build Coastguard Worker }
2148*d57664e9SAndroid Build Coastguard Worker }
2149*d57664e9SAndroid Build Coastguard Worker
2150*d57664e9SAndroid Build Coastguard Worker if (!WriteStableIdMapToPath(context_->GetDiagnostics(), options_.stable_id_map,
2151*d57664e9SAndroid Build Coastguard Worker options_.resource_id_map_path.value())) {
2152*d57664e9SAndroid Build Coastguard Worker return 1;
2153*d57664e9SAndroid Build Coastguard Worker }
2154*d57664e9SAndroid Build Coastguard Worker }
2155*d57664e9SAndroid Build Coastguard Worker } else {
2156*d57664e9SAndroid Build Coastguard Worker // Static libs are merged with other apps, and ID collisions are bad, so
2157*d57664e9SAndroid Build Coastguard Worker // verify that
2158*d57664e9SAndroid Build Coastguard Worker // no IDs have been set.
2159*d57664e9SAndroid Build Coastguard Worker if (!VerifyNoIdsSet()) {
2160*d57664e9SAndroid Build Coastguard Worker return 1;
2161*d57664e9SAndroid Build Coastguard Worker }
2162*d57664e9SAndroid Build Coastguard Worker }
2163*d57664e9SAndroid Build Coastguard Worker
2164*d57664e9SAndroid Build Coastguard Worker // Add the names to mangle based on our source merge earlier.
2165*d57664e9SAndroid Build Coastguard Worker context_->SetNameManglerPolicy(
2166*d57664e9SAndroid Build Coastguard Worker NameManglerPolicy{context_->GetCompilationPackage(), table_merger_->merged_packages()});
2167*d57664e9SAndroid Build Coastguard Worker
2168*d57664e9SAndroid Build Coastguard Worker // Add our table to the symbol table.
2169*d57664e9SAndroid Build Coastguard Worker context_->GetExternalSymbols()->PrependSource(
2170*d57664e9SAndroid Build Coastguard Worker util::make_unique<ResourceTableSymbolSource>(&final_table_));
2171*d57664e9SAndroid Build Coastguard Worker
2172*d57664e9SAndroid Build Coastguard Worker // Workaround for pre-O runtime that would treat negative resource IDs
2173*d57664e9SAndroid Build Coastguard Worker // (any ID with a package ID > 7f) as invalid. Intercept any ID (PPTTEEEE) with PP > 0x7f
2174*d57664e9SAndroid Build Coastguard Worker // and type == 'id', and return the ID 0x7fPPEEEE. IDs don't need to be real resources, they
2175*d57664e9SAndroid Build Coastguard Worker // are just identifiers.
2176*d57664e9SAndroid Build Coastguard Worker if (context_->GetMinSdkVersion() < SDK_O && context_->GetPackageType() == PackageType::kApp) {
2177*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
2178*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
2179*d57664e9SAndroid Build Coastguard Worker << "enabling pre-O feature split ID rewriting");
2180*d57664e9SAndroid Build Coastguard Worker }
2181*d57664e9SAndroid Build Coastguard Worker context_->GetExternalSymbols()->SetDelegate(
2182*d57664e9SAndroid Build Coastguard Worker util::make_unique<FeatureSplitSymbolTableDelegate>(context_));
2183*d57664e9SAndroid Build Coastguard Worker }
2184*d57664e9SAndroid Build Coastguard Worker
2185*d57664e9SAndroid Build Coastguard Worker // Before we process anything, remove the resources whose default values don't exist.
2186*d57664e9SAndroid Build Coastguard Worker // We want to force any references to these to fail the build.
2187*d57664e9SAndroid Build Coastguard Worker if (!options_.no_resource_removal) {
2188*d57664e9SAndroid Build Coastguard Worker if (!NoDefaultResourceRemover{}.Consume(context_, &final_table_)) {
2189*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
2190*d57664e9SAndroid Build Coastguard Worker << "failed removing resources with no defaults");
2191*d57664e9SAndroid Build Coastguard Worker return 1;
2192*d57664e9SAndroid Build Coastguard Worker }
2193*d57664e9SAndroid Build Coastguard Worker }
2194*d57664e9SAndroid Build Coastguard Worker
2195*d57664e9SAndroid Build Coastguard Worker ReferenceLinker linker;
2196*d57664e9SAndroid Build Coastguard Worker if (!options_.merge_only && !linker.Consume(context_, &final_table_)) {
2197*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed linking references");
2198*d57664e9SAndroid Build Coastguard Worker return 1;
2199*d57664e9SAndroid Build Coastguard Worker }
2200*d57664e9SAndroid Build Coastguard Worker
2201*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() == PackageType::kStaticLib) {
2202*d57664e9SAndroid Build Coastguard Worker if (!options_.products.empty()) {
2203*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Warn(android::DiagMessage()
2204*d57664e9SAndroid Build Coastguard Worker << "can't select products when building static library");
2205*d57664e9SAndroid Build Coastguard Worker }
2206*d57664e9SAndroid Build Coastguard Worker } else {
2207*d57664e9SAndroid Build Coastguard Worker ProductFilter product_filter(options_.products, /* remove_default_config_values = */ false);
2208*d57664e9SAndroid Build Coastguard Worker if (!product_filter.Consume(context_, &final_table_)) {
2209*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed stripping products");
2210*d57664e9SAndroid Build Coastguard Worker return 1;
2211*d57664e9SAndroid Build Coastguard Worker }
2212*d57664e9SAndroid Build Coastguard Worker }
2213*d57664e9SAndroid Build Coastguard Worker
2214*d57664e9SAndroid Build Coastguard Worker if (!options_.no_auto_version) {
2215*d57664e9SAndroid Build Coastguard Worker AutoVersioner versioner;
2216*d57664e9SAndroid Build Coastguard Worker if (!versioner.Consume(context_, &final_table_)) {
2217*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed versioning styles");
2218*d57664e9SAndroid Build Coastguard Worker return 1;
2219*d57664e9SAndroid Build Coastguard Worker }
2220*d57664e9SAndroid Build Coastguard Worker }
2221*d57664e9SAndroid Build Coastguard Worker
2222*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() != PackageType::kStaticLib && context_->GetMinSdkVersion() > 0) {
2223*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
2224*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage()
2225*d57664e9SAndroid Build Coastguard Worker << "collapsing resource versions for minimum SDK "
2226*d57664e9SAndroid Build Coastguard Worker << context_->GetMinSdkVersion());
2227*d57664e9SAndroid Build Coastguard Worker }
2228*d57664e9SAndroid Build Coastguard Worker
2229*d57664e9SAndroid Build Coastguard Worker VersionCollapser collapser;
2230*d57664e9SAndroid Build Coastguard Worker if (!collapser.Consume(context_, &final_table_)) {
2231*d57664e9SAndroid Build Coastguard Worker return 1;
2232*d57664e9SAndroid Build Coastguard Worker }
2233*d57664e9SAndroid Build Coastguard Worker }
2234*d57664e9SAndroid Build Coastguard Worker
2235*d57664e9SAndroid Build Coastguard Worker if (!options_.exclude_configs_.empty()) {
2236*d57664e9SAndroid Build Coastguard Worker std::vector<ConfigDescription> excluded_configs;
2237*d57664e9SAndroid Build Coastguard Worker
2238*d57664e9SAndroid Build Coastguard Worker for (auto& config_string : options_.exclude_configs_) {
2239*d57664e9SAndroid Build Coastguard Worker TRACE_NAME("ConfigDescription::Parse");
2240*d57664e9SAndroid Build Coastguard Worker ConfigDescription config_description;
2241*d57664e9SAndroid Build Coastguard Worker
2242*d57664e9SAndroid Build Coastguard Worker if (!ConfigDescription::Parse(config_string, &config_description)) {
2243*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(
2244*d57664e9SAndroid Build Coastguard Worker android::DiagMessage() << "failed to parse --excluded-configs " << config_string);
2245*d57664e9SAndroid Build Coastguard Worker return 1;
2246*d57664e9SAndroid Build Coastguard Worker }
2247*d57664e9SAndroid Build Coastguard Worker
2248*d57664e9SAndroid Build Coastguard Worker excluded_configs.push_back(config_description);
2249*d57664e9SAndroid Build Coastguard Worker }
2250*d57664e9SAndroid Build Coastguard Worker
2251*d57664e9SAndroid Build Coastguard Worker ResourceExcluder excluder(excluded_configs);
2252*d57664e9SAndroid Build Coastguard Worker if (!excluder.Consume(context_, &final_table_)) {
2253*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
2254*d57664e9SAndroid Build Coastguard Worker << "failed excluding configurations");
2255*d57664e9SAndroid Build Coastguard Worker return 1;
2256*d57664e9SAndroid Build Coastguard Worker }
2257*d57664e9SAndroid Build Coastguard Worker }
2258*d57664e9SAndroid Build Coastguard Worker
2259*d57664e9SAndroid Build Coastguard Worker if (!options_.no_resource_deduping) {
2260*d57664e9SAndroid Build Coastguard Worker ResourceDeduper deduper;
2261*d57664e9SAndroid Build Coastguard Worker if (!deduper.Consume(context_, &final_table_)) {
2262*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed deduping resources");
2263*d57664e9SAndroid Build Coastguard Worker return 1;
2264*d57664e9SAndroid Build Coastguard Worker }
2265*d57664e9SAndroid Build Coastguard Worker }
2266*d57664e9SAndroid Build Coastguard Worker
2267*d57664e9SAndroid Build Coastguard Worker proguard::KeepSet proguard_keep_set =
2268*d57664e9SAndroid Build Coastguard Worker proguard::KeepSet(options_.generate_conditional_proguard_rules);
2269*d57664e9SAndroid Build Coastguard Worker proguard::KeepSet proguard_main_dex_keep_set;
2270*d57664e9SAndroid Build Coastguard Worker
2271*d57664e9SAndroid Build Coastguard Worker if (context_->GetPackageType() == PackageType::kStaticLib) {
2272*d57664e9SAndroid Build Coastguard Worker if (options_.table_splitter_options.config_filter != nullptr ||
2273*d57664e9SAndroid Build Coastguard Worker !options_.table_splitter_options.preferred_densities.empty()) {
2274*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Warn(android::DiagMessage()
2275*d57664e9SAndroid Build Coastguard Worker << "can't strip resources when building static library");
2276*d57664e9SAndroid Build Coastguard Worker }
2277*d57664e9SAndroid Build Coastguard Worker } else {
2278*d57664e9SAndroid Build Coastguard Worker // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or
2279*d57664e9SAndroid Build Coastguard Worker // equal to the minSdk.
2280*d57664e9SAndroid Build Coastguard Worker const size_t origConstraintSize = options_.split_constraints.size();
2281*d57664e9SAndroid Build Coastguard Worker options_.split_constraints =
2282*d57664e9SAndroid Build Coastguard Worker AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints);
2283*d57664e9SAndroid Build Coastguard Worker
2284*d57664e9SAndroid Build Coastguard Worker if (origConstraintSize != options_.split_constraints.size()) {
2285*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Warn(android::DiagMessage()
2286*d57664e9SAndroid Build Coastguard Worker << "requested to split resources prior to min sdk of "
2287*d57664e9SAndroid Build Coastguard Worker << context_->GetMinSdkVersion());
2288*d57664e9SAndroid Build Coastguard Worker }
2289*d57664e9SAndroid Build Coastguard Worker TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options);
2290*d57664e9SAndroid Build Coastguard Worker if (!table_splitter.VerifySplitConstraints(context_)) {
2291*d57664e9SAndroid Build Coastguard Worker return 1;
2292*d57664e9SAndroid Build Coastguard Worker }
2293*d57664e9SAndroid Build Coastguard Worker table_splitter.SplitTable(&final_table_);
2294*d57664e9SAndroid Build Coastguard Worker
2295*d57664e9SAndroid Build Coastguard Worker // Now we need to write out the Split APKs.
2296*d57664e9SAndroid Build Coastguard Worker auto path_iter = options_.split_paths.begin();
2297*d57664e9SAndroid Build Coastguard Worker auto split_constraints_iter = options_.split_constraints.begin();
2298*d57664e9SAndroid Build Coastguard Worker for (std::unique_ptr<ResourceTable>& split_table : table_splitter.splits()) {
2299*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
2300*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage(*path_iter)
2301*d57664e9SAndroid Build Coastguard Worker << "generating split with configurations '"
2302*d57664e9SAndroid Build Coastguard Worker << util::Joiner(split_constraints_iter->configs, ", ")
2303*d57664e9SAndroid Build Coastguard Worker << "'");
2304*d57664e9SAndroid Build Coastguard Worker }
2305*d57664e9SAndroid Build Coastguard Worker
2306*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(*path_iter);
2307*d57664e9SAndroid Build Coastguard Worker if (!archive_writer) {
2308*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to create archive");
2309*d57664e9SAndroid Build Coastguard Worker return 1;
2310*d57664e9SAndroid Build Coastguard Worker }
2311*d57664e9SAndroid Build Coastguard Worker
2312*d57664e9SAndroid Build Coastguard Worker // Generate an AndroidManifest.xml for each split.
2313*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> split_manifest =
2314*d57664e9SAndroid Build Coastguard Worker GenerateSplitManifest(app_info_, *split_constraints_iter);
2315*d57664e9SAndroid Build Coastguard Worker
2316*d57664e9SAndroid Build Coastguard Worker XmlReferenceLinker linker(&final_table_);
2317*d57664e9SAndroid Build Coastguard Worker if (!linker.Consume(context_, split_manifest.get())) {
2318*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage()
2319*d57664e9SAndroid Build Coastguard Worker << "failed to create Split AndroidManifest.xml");
2320*d57664e9SAndroid Build Coastguard Worker return 1;
2321*d57664e9SAndroid Build Coastguard Worker }
2322*d57664e9SAndroid Build Coastguard Worker
2323*d57664e9SAndroid Build Coastguard Worker if (!WriteApk(archive_writer.get(), &proguard_keep_set, split_manifest.get(),
2324*d57664e9SAndroid Build Coastguard Worker split_table.get())) {
2325*d57664e9SAndroid Build Coastguard Worker return 1;
2326*d57664e9SAndroid Build Coastguard Worker }
2327*d57664e9SAndroid Build Coastguard Worker
2328*d57664e9SAndroid Build Coastguard Worker ++path_iter;
2329*d57664e9SAndroid Build Coastguard Worker ++split_constraints_iter;
2330*d57664e9SAndroid Build Coastguard Worker }
2331*d57664e9SAndroid Build Coastguard Worker }
2332*d57664e9SAndroid Build Coastguard Worker
2333*d57664e9SAndroid Build Coastguard Worker // Start writing the base APK.
2334*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(options_.output_path);
2335*d57664e9SAndroid Build Coastguard Worker if (!archive_writer) {
2336*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed to create archive");
2337*d57664e9SAndroid Build Coastguard Worker return 1;
2338*d57664e9SAndroid Build Coastguard Worker }
2339*d57664e9SAndroid Build Coastguard Worker
2340*d57664e9SAndroid Build Coastguard Worker bool error = false;
2341*d57664e9SAndroid Build Coastguard Worker {
2342*d57664e9SAndroid Build Coastguard Worker // AndroidManifest.xml has no resource name, but the CallSite is built from the name
2343*d57664e9SAndroid Build Coastguard Worker // (aka, which package the AndroidManifest.xml is coming from).
2344*d57664e9SAndroid Build Coastguard Worker // So we give it a package name so it can see local resources.
2345*d57664e9SAndroid Build Coastguard Worker manifest_xml->file.name.package = context_->GetCompilationPackage();
2346*d57664e9SAndroid Build Coastguard Worker
2347*d57664e9SAndroid Build Coastguard Worker XmlReferenceLinker manifest_linker(&final_table_);
2348*d57664e9SAndroid Build Coastguard Worker if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
2349*d57664e9SAndroid Build Coastguard Worker if (options_.generate_proguard_rules_path &&
2350*d57664e9SAndroid Build Coastguard Worker !proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
2351*d57664e9SAndroid Build Coastguard Worker error = true;
2352*d57664e9SAndroid Build Coastguard Worker }
2353*d57664e9SAndroid Build Coastguard Worker
2354*d57664e9SAndroid Build Coastguard Worker if (options_.generate_main_dex_proguard_rules_path &&
2355*d57664e9SAndroid Build Coastguard Worker !proguard::CollectProguardRulesForManifest(manifest_xml.get(),
2356*d57664e9SAndroid Build Coastguard Worker &proguard_main_dex_keep_set, true)) {
2357*d57664e9SAndroid Build Coastguard Worker error = true;
2358*d57664e9SAndroid Build Coastguard Worker }
2359*d57664e9SAndroid Build Coastguard Worker
2360*d57664e9SAndroid Build Coastguard Worker if (options_.generate_java_class_path) {
2361*d57664e9SAndroid Build Coastguard Worker // The FeatureFlagsFilter may remove <permission> and <permission-group> elements that
2362*d57664e9SAndroid Build Coastguard Worker // generate constants in the Manifest Java file. While we want those permissions and
2363*d57664e9SAndroid Build Coastguard Worker // permission groups removed in the SDK (i.e., if a feature flag is disabled), the
2364*d57664e9SAndroid Build Coastguard Worker // constants should still remain so that code referencing it (e.g., within a feature
2365*d57664e9SAndroid Build Coastguard Worker // flag check) will still compile. Therefore we use the manifest XML before the filter.
2366*d57664e9SAndroid Build Coastguard Worker if (!WriteManifestJavaFile(pre_flags_filter_manifest_xml.get())) {
2367*d57664e9SAndroid Build Coastguard Worker error = true;
2368*d57664e9SAndroid Build Coastguard Worker }
2369*d57664e9SAndroid Build Coastguard Worker }
2370*d57664e9SAndroid Build Coastguard Worker
2371*d57664e9SAndroid Build Coastguard Worker if (options_.no_xml_namespaces) {
2372*d57664e9SAndroid Build Coastguard Worker // PackageParser will fail if URIs are removed from
2373*d57664e9SAndroid Build Coastguard Worker // AndroidManifest.xml.
2374*d57664e9SAndroid Build Coastguard Worker XmlNamespaceRemover namespace_remover(true /* keepUris */);
2375*d57664e9SAndroid Build Coastguard Worker if (!namespace_remover.Consume(context_, manifest_xml.get())) {
2376*d57664e9SAndroid Build Coastguard Worker error = true;
2377*d57664e9SAndroid Build Coastguard Worker }
2378*d57664e9SAndroid Build Coastguard Worker }
2379*d57664e9SAndroid Build Coastguard Worker } else {
2380*d57664e9SAndroid Build Coastguard Worker error = true;
2381*d57664e9SAndroid Build Coastguard Worker }
2382*d57664e9SAndroid Build Coastguard Worker }
2383*d57664e9SAndroid Build Coastguard Worker
2384*d57664e9SAndroid Build Coastguard Worker if (error) {
2385*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Error(android::DiagMessage() << "failed processing manifest");
2386*d57664e9SAndroid Build Coastguard Worker return 1;
2387*d57664e9SAndroid Build Coastguard Worker }
2388*d57664e9SAndroid Build Coastguard Worker
2389*d57664e9SAndroid Build Coastguard Worker if (!VerifyLocaleFormat(manifest_xml.get(), context_->GetDiagnostics())) {
2390*d57664e9SAndroid Build Coastguard Worker return 1;
2391*d57664e9SAndroid Build Coastguard Worker };
2392*d57664e9SAndroid Build Coastguard Worker
2393*d57664e9SAndroid Build Coastguard Worker if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
2394*d57664e9SAndroid Build Coastguard Worker if (!GenerateJavaClasses()) {
2395*d57664e9SAndroid Build Coastguard Worker return 1;
2396*d57664e9SAndroid Build Coastguard Worker }
2397*d57664e9SAndroid Build Coastguard Worker }
2398*d57664e9SAndroid Build Coastguard Worker
2399*d57664e9SAndroid Build Coastguard Worker if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
2400*d57664e9SAndroid Build Coastguard Worker return 1;
2401*d57664e9SAndroid Build Coastguard Worker }
2402*d57664e9SAndroid Build Coastguard Worker
2403*d57664e9SAndroid Build Coastguard Worker if (!CopyAssetsDirsToApk(archive_writer.get())) {
2404*d57664e9SAndroid Build Coastguard Worker return 1;
2405*d57664e9SAndroid Build Coastguard Worker }
2406*d57664e9SAndroid Build Coastguard Worker
2407*d57664e9SAndroid Build Coastguard Worker if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) {
2408*d57664e9SAndroid Build Coastguard Worker return 1;
2409*d57664e9SAndroid Build Coastguard Worker }
2410*d57664e9SAndroid Build Coastguard Worker
2411*d57664e9SAndroid Build Coastguard Worker if (!WriteProguardFile(options_.generate_main_dex_proguard_rules_path,
2412*d57664e9SAndroid Build Coastguard Worker proguard_main_dex_keep_set)) {
2413*d57664e9SAndroid Build Coastguard Worker return 1;
2414*d57664e9SAndroid Build Coastguard Worker }
2415*d57664e9SAndroid Build Coastguard Worker return 0;
2416*d57664e9SAndroid Build Coastguard Worker }
2417*d57664e9SAndroid Build Coastguard Worker
2418*d57664e9SAndroid Build Coastguard Worker private:
2419*d57664e9SAndroid Build Coastguard Worker LinkOptions options_;
2420*d57664e9SAndroid Build Coastguard Worker LinkContext* context_;
2421*d57664e9SAndroid Build Coastguard Worker ResourceTable final_table_;
2422*d57664e9SAndroid Build Coastguard Worker
2423*d57664e9SAndroid Build Coastguard Worker AppInfo app_info_;
2424*d57664e9SAndroid Build Coastguard Worker
2425*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<TableMerger> table_merger_;
2426*d57664e9SAndroid Build Coastguard Worker
2427*d57664e9SAndroid Build Coastguard Worker // A pointer to the FileCollection representing the filesystem (not archives).
2428*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::FileCollection> file_collection_;
2429*d57664e9SAndroid Build Coastguard Worker
2430*d57664e9SAndroid Build Coastguard Worker // A vector of IFileCollections. This is mainly here to retain ownership of the
2431*d57664e9SAndroid Build Coastguard Worker // collections.
2432*d57664e9SAndroid Build Coastguard Worker std::vector<std::unique_ptr<io::IFileCollection>> collections_;
2433*d57664e9SAndroid Build Coastguard Worker
2434*d57664e9SAndroid Build Coastguard Worker // The set of merged APKs. This is mainly here to retain ownership of the APKs.
2435*d57664e9SAndroid Build Coastguard Worker std::vector<std::unique_ptr<LoadedApk>> merged_apks_;
2436*d57664e9SAndroid Build Coastguard Worker
2437*d57664e9SAndroid Build Coastguard Worker // The set of included APKs (not merged). This is mainly here to retain ownership of the APKs.
2438*d57664e9SAndroid Build Coastguard Worker std::vector<std::unique_ptr<LoadedApk>> static_library_includes_;
2439*d57664e9SAndroid Build Coastguard Worker
2440*d57664e9SAndroid Build Coastguard Worker // The set of shared libraries being used, mapping their assigned package ID to package name.
2441*d57664e9SAndroid Build Coastguard Worker std::map<size_t, std::string> shared_libs_;
2442*d57664e9SAndroid Build Coastguard Worker
2443*d57664e9SAndroid Build Coastguard Worker // The package name of the base application, if it is included.
2444*d57664e9SAndroid Build Coastguard Worker std::optional<std::string> included_feature_base_;
2445*d57664e9SAndroid Build Coastguard Worker };
2446*d57664e9SAndroid Build Coastguard Worker
Action(const std::vector<std::string> & args)2447*d57664e9SAndroid Build Coastguard Worker int LinkCommand::Action(const std::vector<std::string>& args) {
2448*d57664e9SAndroid Build Coastguard Worker TRACE_FLUSH(trace_folder_ ? trace_folder_.value() : "", "LinkCommand::Action");
2449*d57664e9SAndroid Build Coastguard Worker LinkContext context(diag_);
2450*d57664e9SAndroid Build Coastguard Worker
2451*d57664e9SAndroid Build Coastguard Worker // Expand all argument-files passed into the command line. These start with '@'.
2452*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> arg_list;
2453*d57664e9SAndroid Build Coastguard Worker for (const std::string& arg : args) {
2454*d57664e9SAndroid Build Coastguard Worker if (util::StartsWith(arg, "@")) {
2455*d57664e9SAndroid Build Coastguard Worker const std::string path = arg.substr(1, arg.size() - 1);
2456*d57664e9SAndroid Build Coastguard Worker std::string error;
2457*d57664e9SAndroid Build Coastguard Worker if (!file::AppendArgsFromFile(path, &arg_list, &error)) {
2458*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
2459*d57664e9SAndroid Build Coastguard Worker return 1;
2460*d57664e9SAndroid Build Coastguard Worker }
2461*d57664e9SAndroid Build Coastguard Worker } else {
2462*d57664e9SAndroid Build Coastguard Worker arg_list.push_back(arg);
2463*d57664e9SAndroid Build Coastguard Worker }
2464*d57664e9SAndroid Build Coastguard Worker }
2465*d57664e9SAndroid Build Coastguard Worker
2466*d57664e9SAndroid Build Coastguard Worker // Expand all argument-files passed to -R.
2467*d57664e9SAndroid Build Coastguard Worker for (const std::string& arg : overlay_arg_list_) {
2468*d57664e9SAndroid Build Coastguard Worker if (util::StartsWith(arg, "@")) {
2469*d57664e9SAndroid Build Coastguard Worker const std::string path = arg.substr(1, arg.size() - 1);
2470*d57664e9SAndroid Build Coastguard Worker std::string error;
2471*d57664e9SAndroid Build Coastguard Worker if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) {
2472*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
2473*d57664e9SAndroid Build Coastguard Worker return 1;
2474*d57664e9SAndroid Build Coastguard Worker }
2475*d57664e9SAndroid Build Coastguard Worker } else {
2476*d57664e9SAndroid Build Coastguard Worker options_.overlay_files.push_back(arg);
2477*d57664e9SAndroid Build Coastguard Worker }
2478*d57664e9SAndroid Build Coastguard Worker }
2479*d57664e9SAndroid Build Coastguard Worker
2480*d57664e9SAndroid Build Coastguard Worker if (verbose_) {
2481*d57664e9SAndroid Build Coastguard Worker context.SetVerbose(verbose_);
2482*d57664e9SAndroid Build Coastguard Worker }
2483*d57664e9SAndroid Build Coastguard Worker
2484*d57664e9SAndroid Build Coastguard Worker if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) {
2485*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()
2486*d57664e9SAndroid Build Coastguard Worker ->Error(android::DiagMessage()
2487*d57664e9SAndroid Build Coastguard Worker << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
2488*d57664e9SAndroid Build Coastguard Worker return 1;
2489*d57664e9SAndroid Build Coastguard Worker }
2490*d57664e9SAndroid Build Coastguard Worker
2491*d57664e9SAndroid Build Coastguard Worker if (shared_lib_ && options_.private_symbols) {
2492*d57664e9SAndroid Build Coastguard Worker // If a shared library styleable in a public R.java uses a private attribute, attempting to
2493*d57664e9SAndroid Build Coastguard Worker // reference the private attribute within the styleable array will cause a link error because
2494*d57664e9SAndroid Build Coastguard Worker // the private attribute will not be emitted in the public R.java.
2495*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage()
2496*d57664e9SAndroid Build Coastguard Worker << "--shared-lib cannot currently be used in combination with"
2497*d57664e9SAndroid Build Coastguard Worker << " --private-symbols");
2498*d57664e9SAndroid Build Coastguard Worker return 1;
2499*d57664e9SAndroid Build Coastguard Worker }
2500*d57664e9SAndroid Build Coastguard Worker
2501*d57664e9SAndroid Build Coastguard Worker if (options_.merge_only && !static_lib_) {
2502*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()
2503*d57664e9SAndroid Build Coastguard Worker ->Error(android::DiagMessage()
2504*d57664e9SAndroid Build Coastguard Worker << "the --merge-only flag can be only used when building a static library");
2505*d57664e9SAndroid Build Coastguard Worker return 1;
2506*d57664e9SAndroid Build Coastguard Worker }
2507*d57664e9SAndroid Build Coastguard Worker if (options_.use_sparse_encoding) {
2508*d57664e9SAndroid Build Coastguard Worker options_.table_flattener_options.sparse_entries = SparseEntriesMode::Enabled;
2509*d57664e9SAndroid Build Coastguard Worker }
2510*d57664e9SAndroid Build Coastguard Worker
2511*d57664e9SAndroid Build Coastguard Worker // The default build type.
2512*d57664e9SAndroid Build Coastguard Worker context.SetPackageType(PackageType::kApp);
2513*d57664e9SAndroid Build Coastguard Worker context.SetPackageId(kAppPackageId);
2514*d57664e9SAndroid Build Coastguard Worker
2515*d57664e9SAndroid Build Coastguard Worker if (shared_lib_) {
2516*d57664e9SAndroid Build Coastguard Worker context.SetPackageType(PackageType::kSharedLib);
2517*d57664e9SAndroid Build Coastguard Worker context.SetPackageId(0x00);
2518*d57664e9SAndroid Build Coastguard Worker } else if (static_lib_) {
2519*d57664e9SAndroid Build Coastguard Worker context.SetPackageType(PackageType::kStaticLib);
2520*d57664e9SAndroid Build Coastguard Worker options_.output_format = OutputFormat::kProto;
2521*d57664e9SAndroid Build Coastguard Worker } else if (proto_format_) {
2522*d57664e9SAndroid Build Coastguard Worker options_.output_format = OutputFormat::kProto;
2523*d57664e9SAndroid Build Coastguard Worker }
2524*d57664e9SAndroid Build Coastguard Worker
2525*d57664e9SAndroid Build Coastguard Worker if (package_id_) {
2526*d57664e9SAndroid Build Coastguard Worker if (context.GetPackageType() != PackageType::kApp) {
2527*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(
2528*d57664e9SAndroid Build Coastguard Worker android::DiagMessage() << "can't specify --package-id when not building a regular app");
2529*d57664e9SAndroid Build Coastguard Worker return 1;
2530*d57664e9SAndroid Build Coastguard Worker }
2531*d57664e9SAndroid Build Coastguard Worker
2532*d57664e9SAndroid Build Coastguard Worker const std::optional<uint32_t> maybe_package_id_int =
2533*d57664e9SAndroid Build Coastguard Worker ResourceUtils::ParseInt(package_id_.value());
2534*d57664e9SAndroid Build Coastguard Worker if (!maybe_package_id_int) {
2535*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage()
2536*d57664e9SAndroid Build Coastguard Worker << "package ID '" << package_id_.value()
2537*d57664e9SAndroid Build Coastguard Worker << "' is not a valid integer");
2538*d57664e9SAndroid Build Coastguard Worker return 1;
2539*d57664e9SAndroid Build Coastguard Worker }
2540*d57664e9SAndroid Build Coastguard Worker
2541*d57664e9SAndroid Build Coastguard Worker const uint32_t package_id_int = maybe_package_id_int.value();
2542*d57664e9SAndroid Build Coastguard Worker if (package_id_int > std::numeric_limits<uint8_t>::max()
2543*d57664e9SAndroid Build Coastguard Worker || package_id_int == kFrameworkPackageId
2544*d57664e9SAndroid Build Coastguard Worker || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) {
2545*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(
2546*d57664e9SAndroid Build Coastguard Worker android::DiagMessage() << StringPrintf(
2547*d57664e9SAndroid Build Coastguard Worker "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
2548*d57664e9SAndroid Build Coastguard Worker return 1;
2549*d57664e9SAndroid Build Coastguard Worker }
2550*d57664e9SAndroid Build Coastguard Worker context.SetPackageId(static_cast<uint8_t>(package_id_int));
2551*d57664e9SAndroid Build Coastguard Worker }
2552*d57664e9SAndroid Build Coastguard Worker
2553*d57664e9SAndroid Build Coastguard Worker // Populate the set of extra packages for which to generate R.java.
2554*d57664e9SAndroid Build Coastguard Worker for (std::string& extra_package : extra_java_packages_) {
2555*d57664e9SAndroid Build Coastguard Worker // A given package can actually be a colon separated list of packages.
2556*d57664e9SAndroid Build Coastguard Worker for (StringPiece package : util::Split(extra_package, ':')) {
2557*d57664e9SAndroid Build Coastguard Worker options_.extra_java_packages.emplace(package);
2558*d57664e9SAndroid Build Coastguard Worker }
2559*d57664e9SAndroid Build Coastguard Worker }
2560*d57664e9SAndroid Build Coastguard Worker
2561*d57664e9SAndroid Build Coastguard Worker if (product_list_) {
2562*d57664e9SAndroid Build Coastguard Worker for (StringPiece product : util::Tokenize(product_list_.value(), ',')) {
2563*d57664e9SAndroid Build Coastguard Worker if (product != "" && product != "default") {
2564*d57664e9SAndroid Build Coastguard Worker options_.products.emplace(product);
2565*d57664e9SAndroid Build Coastguard Worker }
2566*d57664e9SAndroid Build Coastguard Worker }
2567*d57664e9SAndroid Build Coastguard Worker }
2568*d57664e9SAndroid Build Coastguard Worker
2569*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<IConfigFilter> filter;
2570*d57664e9SAndroid Build Coastguard Worker if (!configs_.empty()) {
2571*d57664e9SAndroid Build Coastguard Worker filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics());
2572*d57664e9SAndroid Build Coastguard Worker if (filter == nullptr) {
2573*d57664e9SAndroid Build Coastguard Worker return 1;
2574*d57664e9SAndroid Build Coastguard Worker }
2575*d57664e9SAndroid Build Coastguard Worker options_.table_splitter_options.config_filter = filter.get();
2576*d57664e9SAndroid Build Coastguard Worker }
2577*d57664e9SAndroid Build Coastguard Worker
2578*d57664e9SAndroid Build Coastguard Worker if (preferred_density_) {
2579*d57664e9SAndroid Build Coastguard Worker std::optional<uint16_t> density =
2580*d57664e9SAndroid Build Coastguard Worker ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
2581*d57664e9SAndroid Build Coastguard Worker if (!density) {
2582*d57664e9SAndroid Build Coastguard Worker return 1;
2583*d57664e9SAndroid Build Coastguard Worker }
2584*d57664e9SAndroid Build Coastguard Worker options_.table_splitter_options.preferred_densities.push_back(density.value());
2585*d57664e9SAndroid Build Coastguard Worker }
2586*d57664e9SAndroid Build Coastguard Worker
2587*d57664e9SAndroid Build Coastguard Worker // Parse the split parameters.
2588*d57664e9SAndroid Build Coastguard Worker for (const std::string& split_arg : split_args_) {
2589*d57664e9SAndroid Build Coastguard Worker options_.split_paths.push_back({});
2590*d57664e9SAndroid Build Coastguard Worker options_.split_constraints.push_back({});
2591*d57664e9SAndroid Build Coastguard Worker if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(),
2592*d57664e9SAndroid Build Coastguard Worker &options_.split_constraints.back())) {
2593*d57664e9SAndroid Build Coastguard Worker return 1;
2594*d57664e9SAndroid Build Coastguard Worker }
2595*d57664e9SAndroid Build Coastguard Worker }
2596*d57664e9SAndroid Build Coastguard Worker
2597*d57664e9SAndroid Build Coastguard Worker // Parse the feature flag values. An argument that starts with '@' points to a file to read flag
2598*d57664e9SAndroid Build Coastguard Worker // values from.
2599*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> all_feature_flags_args;
2600*d57664e9SAndroid Build Coastguard Worker for (const std::string& arg : feature_flags_args_) {
2601*d57664e9SAndroid Build Coastguard Worker if (util::StartsWith(arg, "@")) {
2602*d57664e9SAndroid Build Coastguard Worker const std::string path = arg.substr(1, arg.size() - 1);
2603*d57664e9SAndroid Build Coastguard Worker std::string error;
2604*d57664e9SAndroid Build Coastguard Worker if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) {
2605*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
2606*d57664e9SAndroid Build Coastguard Worker return 1;
2607*d57664e9SAndroid Build Coastguard Worker }
2608*d57664e9SAndroid Build Coastguard Worker } else {
2609*d57664e9SAndroid Build Coastguard Worker all_feature_flags_args.push_back(arg);
2610*d57664e9SAndroid Build Coastguard Worker }
2611*d57664e9SAndroid Build Coastguard Worker }
2612*d57664e9SAndroid Build Coastguard Worker
2613*d57664e9SAndroid Build Coastguard Worker for (const std::string& arg : all_feature_flags_args) {
2614*d57664e9SAndroid Build Coastguard Worker if (!ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
2615*d57664e9SAndroid Build Coastguard Worker return 1;
2616*d57664e9SAndroid Build Coastguard Worker }
2617*d57664e9SAndroid Build Coastguard Worker }
2618*d57664e9SAndroid Build Coastguard Worker
2619*d57664e9SAndroid Build Coastguard Worker if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
2620*d57664e9SAndroid Build Coastguard Worker if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
2621*d57664e9SAndroid Build Coastguard Worker &options_.stable_id_map)) {
2622*d57664e9SAndroid Build Coastguard Worker return 1;
2623*d57664e9SAndroid Build Coastguard Worker }
2624*d57664e9SAndroid Build Coastguard Worker }
2625*d57664e9SAndroid Build Coastguard Worker
2626*d57664e9SAndroid Build Coastguard Worker if (no_compress_regex) {
2627*d57664e9SAndroid Build Coastguard Worker std::string regex = no_compress_regex.value();
2628*d57664e9SAndroid Build Coastguard Worker if (util::StartsWith(regex, "@")) {
2629*d57664e9SAndroid Build Coastguard Worker const std::string path = regex.substr(1, regex.size() -1);
2630*d57664e9SAndroid Build Coastguard Worker std::string error;
2631*d57664e9SAndroid Build Coastguard Worker if (!file::AppendSetArgsFromFile(path, &options_.extensions_to_not_compress, &error)) {
2632*d57664e9SAndroid Build Coastguard Worker context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
2633*d57664e9SAndroid Build Coastguard Worker return 1;
2634*d57664e9SAndroid Build Coastguard Worker }
2635*d57664e9SAndroid Build Coastguard Worker } else {
2636*d57664e9SAndroid Build Coastguard Worker options_.regex_to_not_compress = GetRegularExpression(no_compress_regex.value());
2637*d57664e9SAndroid Build Coastguard Worker }
2638*d57664e9SAndroid Build Coastguard Worker }
2639*d57664e9SAndroid Build Coastguard Worker
2640*d57664e9SAndroid Build Coastguard Worker // Populate some default no-compress extensions that are already compressed.
2641*d57664e9SAndroid Build Coastguard Worker options_.extensions_to_not_compress.insert({
2642*d57664e9SAndroid Build Coastguard Worker // Image extensions
2643*d57664e9SAndroid Build Coastguard Worker ".jpg", ".jpeg", ".png", ".gif", ".webp",
2644*d57664e9SAndroid Build Coastguard Worker // Audio extensions
2645*d57664e9SAndroid Build Coastguard Worker ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy",
2646*d57664e9SAndroid Build Coastguard Worker ".xmf", ".amr", ".awb",
2647*d57664e9SAndroid Build Coastguard Worker // Audio/video extensions
2648*d57664e9SAndroid Build Coastguard Worker ".mpg", ".mpeg", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".wma", ".wmv",
2649*d57664e9SAndroid Build Coastguard Worker ".webm", ".mkv"});
2650*d57664e9SAndroid Build Coastguard Worker
2651*d57664e9SAndroid Build Coastguard Worker // Turn off auto versioning for static-libs.
2652*d57664e9SAndroid Build Coastguard Worker if (context.GetPackageType() == PackageType::kStaticLib) {
2653*d57664e9SAndroid Build Coastguard Worker options_.no_auto_version = true;
2654*d57664e9SAndroid Build Coastguard Worker options_.no_version_vectors = true;
2655*d57664e9SAndroid Build Coastguard Worker options_.no_version_transitions = true;
2656*d57664e9SAndroid Build Coastguard Worker }
2657*d57664e9SAndroid Build Coastguard Worker
2658*d57664e9SAndroid Build Coastguard Worker Linker cmd(&context, options_);
2659*d57664e9SAndroid Build Coastguard Worker return cmd.Run(arg_list);
2660*d57664e9SAndroid Build Coastguard Worker }
2661*d57664e9SAndroid Build Coastguard Worker
2662*d57664e9SAndroid Build Coastguard Worker } // namespace aapt
2663