android_tools_rs/aapt2/
link.rs

1use crate::error::{CommandExt, Result};
2use std::{
3    path::{Path, PathBuf},
4    process::Command,
5};
6
7/// ## Link
8/// In the link phase, AAPT2 merges all the intermediate files generated from the
9/// compilation phase such as resource tables, binary XML files, and processed
10/// PNG files and packages them into a single APK. Additionally, other auxiliary
11/// files like `R.java` and ProGuard rules files can be generated during this phase.
12/// However, the generated APK does not contain DEX bytecode and is unsigned.
13/// That is, you can't deploy this APK to a device. If you're not using the Android
14/// Gradle Plugin to [`build your app from the command line`], you can use other command
15/// line tools, such as [`d8`] to compile Java bytecode into DEX bytecode and
16/// [`apksigner`] to sign your APK.
17///
18/// ## Link syntax
19/// The general syntax for using link is as follows:
20///
21/// ```sh
22/// aapt2 link path-to-input-files [options] -o
23/// outputdirectory/outputfilename.apk --manifest AndroidManifest.xml
24/// ```
25///
26/// In the following example, AAPT2 merges the two intermediate files -
27/// `drawable_Image.flat` and `values_values.arsc.flat`, and the `AndroidManifest.xml`
28/// file. AAPT2 links the result against `android.jar` file which holds the resources
29/// defined in the android package:
30///
31/// ```sh
32///  aapt2 link -o output.apk
33///  -I android_sdk/platforms/android_version/android.jar
34///     compiled/res/values_values.arsc.flat
35///     compiled/res/drawable_Image.flat --manifest /path/to/AndroidManifest.xml -v
36/// ```
37///
38/// [`d8`]: https://developer.android.com/studio/command-line/d8
39/// [`apksigner`]: https://developer.android.com/studio/command-line/apksigner
40/// [`build your app from the command line`]: https://developer.android.com/studio/build/building-cmdline
41#[derive(Debug, PartialEq, Default)]
42pub struct Aapt2Link {
43    inputs: Vec<PathBuf>,
44    compiled_res: Option<PathBuf>,
45    output_apk: PathBuf,
46    manifest: PathBuf,
47    android_jar: Option<PathBuf>,
48    assets: Option<PathBuf>,
49    individual_flat: Option<PathBuf>,
50    package_id: Option<String>,
51    allow_reserved_package_id: bool,
52    java: Option<PathBuf>,
53    proguard_options: Option<PathBuf>,
54    proguard_main_dex: Option<PathBuf>,
55    proguard_conditional_keep_rules: bool,
56    proguard_minimal_keep_rules: bool,
57    no_auto_version: bool,
58    no_version_vectors: bool,
59    no_version_transitions: bool,
60    no_resource_deduping: bool,
61    no_resource_removal: bool,
62    enable_sparse_encoding: bool,
63    package_identifier: bool,
64    suggested_strings: bool,
65    config: Vec<String>,
66    preferred_density: Option<i32>,
67    product: Option<PathBuf>,
68    output_to_dir: bool,
69    no_xml_namespaces: bool,
70    min_sdk_version: Option<u32>,
71    target_sdk_version: Option<u32>,
72    version_code: Option<u32>,
73    version_code_major: Option<u32>,
74    version_name: Option<String>,
75    replace_version: bool,
76    compile_sdk_version_code: Option<u32>,
77    compile_sdk_version_name: Option<String>,
78    shared_lib: bool,
79    static_lib: bool,
80    proto_format: bool,
81    no_static_lib_packages: bool,
82    non_final_ids: bool,
83    no_proguard_location_reference: bool,
84    emit_ids: Option<PathBuf>,
85    stable_ids: Option<PathBuf>,
86    private_symbols: Option<String>,
87    custom_package: Option<PathBuf>,
88    extra_packages: Option<PathBuf>,
89    add_javadoc_annotation: Option<String>,
90    output_text_symbols: Option<PathBuf>,
91    auto_add_overlay: bool,
92    override_styles_instead_of_overlaying: bool,
93    rename_manifest_package: Option<String>,
94    rename_resources_package: Option<String>,
95    rename_instrumentation_target_package: Option<String>,
96    extensions: Vec<String>,
97    no_compress: bool,
98    keep_raw_values: bool,
99    no_compress_regex: Option<String>,
100    warn_manifest_validation: bool,
101    split: Option<PathBuf>,
102    strict_visibility: bool,
103    exclude_sources: bool,
104    trace_folder: Option<String>,
105    merge_only: bool,
106    verbose: bool,
107    help: bool,
108}
109
110impl Aapt2Link {
111    /// Specifies the output path for the linked resource APK.
112    ///
113    /// This is a required flag because you must specify the path for the output APK that
114    /// can hold the linked resources.
115    ///
116    /// Specifies the path to the Android manifest file to build.
117    ///
118    /// This is a required flag because the manifest file encloses essential information
119    /// about your app like package name and application ID.
120    pub fn new(inputs: &[PathBuf], output_apk: &Path, manifest: &Path) -> Self {
121        Self {
122            inputs: inputs.to_vec(),
123            compiled_res: None,
124            output_apk: output_apk.to_owned(),
125            manifest: manifest.to_owned(),
126            ..Default::default()
127        }
128    }
129    /// Specifies the output path for the linked resource APK.
130    ///
131    /// This is a required flag because you must specify the path for the output APK that
132    /// can hold the linked resources.
133    ///
134    /// Specifies the path to the Android manifest file to build.
135    ///
136    /// This is a required flag because the manifest file encloses essential information
137    /// about your app like package name and application ID.
138    pub fn new_from_compiled_res(
139        compiled_res: Option<PathBuf>,
140        output_apk: &Path,
141        manifest: &Path,
142    ) -> Self {
143        Self {
144            inputs: Vec::new(),
145            compiled_res,
146            output_apk: output_apk.to_owned(),
147            manifest: manifest.to_owned(),
148            ..Default::default()
149        }
150    }
151    /// Generates output file for ProGuard rules for the main dex.
152    pub fn proguard_main_dex(&mut self, proguard_main_dex: PathBuf) -> &mut Self {
153        self.proguard_main_dex = Some(proguard_main_dex);
154        self
155    }
156    /// Generate a minimal set of Proguard keep rules.
157    pub fn proguard_minimal_keep_rules(&mut self, proguard_minimal_keep_rules: bool) -> &mut Self {
158        self.proguard_minimal_keep_rules = proguard_minimal_keep_rules;
159        self
160    }
161    /// Disables automatic removal of resources without
162    pub fn no_resource_removal(&mut self, no_resource_removal: bool) -> &mut Self {
163        self.no_resource_removal = no_resource_removal;
164        self
165    }
166    /// Legacy flag that specifies to use the package identifier 0x01.
167    pub fn package_identifier(&mut self, package_identifier: bool) -> &mut Self {
168        self.package_identifier = package_identifier;
169        self
170    }
171    /// Comma separated list of product names to keep
172    pub fn product(&mut self, product: PathBuf) -> &mut Self {
173        self.product = Some(product);
174        self
175    }
176    /// Removes XML namespace prefix and URI information
177    pub fn no_xml_namespaces(&mut self, no_xml_namespaces: bool) -> &mut Self {
178        self.no_xml_namespaces = no_xml_namespaces;
179        self
180    }
181    /// Version code major (integer) to inject into the AndroidManifest.xml if none is
182    /// present.
183    pub fn version_code_major(&mut self, version_code_major: u32) -> &mut Self {
184        self.version_code_major = Some(version_code_major);
185        self
186    }
187    /// Version name to inject into the AndroidManifest.xml if none is present.
188    pub fn version_name(&mut self, version_name: String) -> &mut Self {
189        self.version_name = Some(version_name);
190        self
191    }
192    /// If --version-code and/or --version-name are specified, these values will replace
193    /// any value already in the manifest. By default, nothing is changed if the manifest
194    /// already defines these attributes.
195    pub fn replace_version(&mut self, replace_version: bool) -> &mut Self {
196        self.replace_version = replace_version;
197        self
198    }
199    /// Version code (integer) to inject into the AndroidManifest.xml if none is present.
200    pub fn compile_sdk_version_code(&mut self, compile_sdk_version_code: u32) -> &mut Self {
201        self.compile_sdk_version_code = Some(compile_sdk_version_code);
202        self
203    }
204    /// Generates a shared Android runtime library.
205    pub fn shared_lib(&mut self, shared_lib: bool) -> &mut Self {
206        self.shared_lib = shared_lib;
207        self
208    }
209    /// Generate a static Android library.
210    pub fn static_lib(&mut self, static_lib: bool) -> &mut Self {
211        self.static_lib = static_lib;
212        self
213    }
214    /// Merge all library resources under the app's package.
215    pub fn no_static_lib_packages(&mut self, no_static_lib_packages: bool) -> &mut Self {
216        self.no_static_lib_packages = no_static_lib_packages;
217        self
218    }
219    /// Keep proguard rules files from having a reference to the source file
220    pub fn no_proguard_location_reference(
221        &mut self,
222        no_proguard_location_reference: bool,
223    ) -> &mut Self {
224        self.no_proguard_location_reference = no_proguard_location_reference;
225        self
226    }
227    /// Package name to use when generating R.java for private symbols. If not specified,
228    /// public and private symbols will use the application's package name.
229    pub fn private_symbols(&mut self, private_symbols: String) -> &mut Self {
230        self.private_symbols = Some(private_symbols);
231        self
232    }
233    /// Causes styles defined in -R resources to replace previous definitions instead of
234    /// merging into them
235    pub fn override_styles_instead_of_overlaying(
236        &mut self,
237        override_styles_instead_of_overlaying: bool,
238    ) -> &mut Self {
239        self.override_styles_instead_of_overlaying = override_styles_instead_of_overlaying;
240        self
241    }
242    /// Renames the package in resources table
243    pub fn rename_resources_package(&mut self, rename_resources_package: String) -> &mut Self {
244        self.rename_resources_package = Some(rename_resources_package);
245        self
246    }
247    /// Provides the path to the platform's android.jar or other APKs like
248    /// framework-res.apk  which might be useful while building features. This flag is
249    /// required if you are using attributes with android namespace (for example,
250    /// android:id) in your resource files.
251    pub fn android_jar(&mut self, android_jar: PathBuf) -> &mut Self {
252        self.android_jar = Some(android_jar);
253        self
254    }
255    /// Specifies an assets directory to be included in the APK.
256    ///
257    /// You can use this directory to store original unprocessed files. To learn more,
258    /// read [`Accessing original`] files.
259    ///
260    /// [`Accessing original`]: https://developer.android.com/guide/topics/resources/providing-resources#OriginalFiles
261    pub fn assets(&mut self, assets: PathBuf) -> &mut Self {
262        self.assets = Some(assets);
263        self
264    }
265    /// Pass individual .flat file to link, using `overlay` semantics without using the
266    /// `<add-resource>` tag.
267    ///
268    /// When you a provide a resource file that overlays (extends or modifies) an existing
269    /// file, the last conflicting resource given is used.
270    pub fn individual_flat(&mut self, individual_flat: PathBuf) -> &mut Self {
271        self.individual_flat = Some(individual_flat);
272        self
273    }
274    /// Specifies the package ID to use for your app.
275    ///
276    /// The package ID that you specify must be greater than or equal to 0x7f unless used
277    /// in combination with `--allow-reserved-package-id`.
278    pub fn package_id(&mut self, package_id: String) -> &mut Self {
279        self.package_id = Some(package_id);
280        self
281    }
282    /// Allows the use of a reserved package ID.
283    ///
284    /// Reserved package IDs are IDs that are normally assigned to shared libraries and
285    /// are in the range from 0x02 to 0x7e inclusive. By using
286    /// --allow-reserved-package-id, you can assign IDs that fall in the range of reserved
287    /// package IDs.
288    ///
289    /// This should only be used for packages with a min-sdk version of 26 or lower.
290    pub fn allow_reserved_package_id(&mut self, allow_reserved_package_id: bool) -> &mut Self {
291        self.allow_reserved_package_id = allow_reserved_package_id;
292        self
293    }
294    /// Specifies the directory in which to generate R.java.
295    pub fn java(&mut self, java: PathBuf) -> &mut Self {
296        self.java = Some(java);
297        self
298    }
299    /// Generates output file for ProGuard rules.
300    pub fn proguard_options(&mut self, proguard_options: PathBuf) -> &mut Self {
301        self.proguard_options = Some(proguard_options);
302        self
303    }
304    /// Output file for generated Proguard rules for the main dex.
305    pub fn proguard_conditional_keep_rules(
306        &mut self,
307        proguard_conditional_keep_rules: bool,
308    ) -> &mut Self {
309        self.proguard_conditional_keep_rules = proguard_conditional_keep_rules;
310        self
311    }
312    /// Disables automatic style and layout SDK versioning.
313    pub fn no_auto_version(&mut self, no_auto_version: bool) -> &mut Self {
314        self.no_auto_version = no_auto_version;
315        self
316    }
317    /// Disables automatic versioning of vector drawables. Use this only when building
318    /// your APK with the Vector Drawable Library.
319    pub fn no_version_vectors(&mut self, no_version_vectors: bool) -> &mut Self {
320        self.no_version_vectors = no_version_vectors;
321        self
322    }
323    /// Disables automatic versioning of transition resources. Use this only when building
324    /// your APK with Transition Support library.
325    pub fn no_version_transitions(&mut self, no_version_transitions: bool) -> &mut Self {
326        self.no_version_transitions = no_version_transitions;
327        self
328    }
329    /// Disables automatic de-duplication of resources with identical values across
330    /// compatible configurations.
331    pub fn no_resource_deduping(&mut self, no_resource_deduping: bool) -> &mut Self {
332        self.no_resource_deduping = no_resource_deduping;
333        self
334    }
335    /// Enables encoding of sparse entries using a binary search tree. This is useful for
336    /// optimization of APK size, but at the cost of resource retrieval performance.
337    pub fn enable_sparse_encoding(&mut self, enable_sparse_encoding: bool) -> &mut Self {
338        self.enable_sparse_encoding = enable_sparse_encoding;
339        self
340    }
341    /// Requires localization of strings marked 'suggested'.
342    pub fn suggested_strings(&mut self, suggested_strings: bool) -> &mut Self {
343        self.suggested_strings = suggested_strings;
344        self
345    }
346    /// Provides a list of configurations separated by commas.
347    ///
348    /// For example, if you have dependencies on the support library (which contains
349    /// translations for multiple languages), you can filter resources just for the given
350    /// language configuration, like English or Spanish.
351    ///
352    /// You must define the language configuration by a two-letter ISO 639-1 language
353    /// code, optionally followed by a two letter ISO 3166-1-alpha-2 region code preceded
354    /// by lowercase 'r' (for example, en-rUS).
355    pub fn config(&mut self, config: String) -> &mut Self {
356        self.config.push(config);
357        self
358    }
359    /// Allows AAPT2 to select the closest matching density and strip out all others.
360    ///
361    /// There are several pixel density qualifiers available to use in your app, such as
362    /// ldpi, hdpi, and xhdpi. When you specify a preferred density, AAPT2 selects and
363    /// stores the closest matching density in the resource table and removes all others.
364    pub fn preferred_density(&mut self, preferred_density: i32) -> &mut Self {
365        self.preferred_density = Some(preferred_density);
366        self
367    }
368    /// Outputs the APK contents to a directory specified by -o.
369    ///
370    /// If you get any errors using this flag, you can resolve them by upgrading to
371    /// [`Android SDK Build Tools 28.0.0 or higher`].
372    ///
373    /// [`Android SDK Build Tools 28.0.0 or higher`]: https://developer.android.com/studio/releases/build-tools
374    pub fn output_to_dir(&mut self, output_to_dir: bool) -> &mut Self {
375        self.output_to_dir = output_to_dir;
376        self
377    }
378    /// Sets the default minimum SDK version to use for `AndroidManifest.xml`.
379    pub fn min_sdk_version(&mut self, min_sdk_version: u32) -> &mut Self {
380        self.min_sdk_version = Some(min_sdk_version);
381        self
382    }
383    /// Sets the default target SDK version to use for `AndroidManifest.xml`.
384    pub fn target_sdk_version(&mut self, target_sdk_version: u32) -> &mut Self {
385        self.target_sdk_version = Some(target_sdk_version);
386        self
387    }
388    /// Specifies the version code (integer) to inject into the AndroidManifest.xml if
389    /// none is present.
390    pub fn version_code(&mut self, version_code: u32) -> &mut Self {
391        self.version_code = Some(version_code);
392        self
393    }
394    /// Specifies the version name to inject into the AndroidManifest.xml if none is
395    /// present.
396    pub fn compile_sdk_version_name(&mut self, compile_sdk_version_name: String) -> &mut Self {
397        self.compile_sdk_version_name = Some(compile_sdk_version_name);
398        self
399    }
400    /// Generates compiled resources in Protobuf format.
401    /// Suitable as input to the [`bundle tool`] for generating an Android App Bundle.
402    ///
403    /// [`bundle tool`]: https://developer.android.com/studio/build/building-cmdline#bundletool-build
404    pub fn proto_format(&mut self, proto_format: bool) -> &mut Self {
405        self.proto_format = proto_format;
406        self
407    }
408    /// Generates `R.java` with non-final resource IDs (references to the IDs from app's
409    /// code will not get inlined during kotlinc/javac compilation).
410    pub fn non_final_ids(&mut self, non_final_ids: bool) -> &mut Self {
411        self.non_final_ids = non_final_ids;
412        self
413    }
414    /// Emits a file at the given path with a list of names of resource types and their ID
415    /// mappings. It is suitable to use with --stable-ids.
416    pub fn emit_ids(&mut self, emit_ids: PathBuf) -> &mut Self {
417        self.emit_ids = Some(emit_ids);
418        self
419    }
420    /// Consumes the file generated with --emit-ids containing the list of names of
421    /// resource types and their assigned IDs.
422    ///
423    /// This option allows assigned IDs to remain stable even when you delete or add new
424    /// resources while linking
425    pub fn stable_ids(&mut self, stable_ids: PathBuf) -> &mut Self {
426        self.stable_ids = Some(stable_ids);
427        self
428    }
429    /// Specifies custom Java package under which to generate R.java.
430    pub fn custom_package(&mut self, custom_package: PathBuf) -> &mut Self {
431        self.custom_package = Some(custom_package);
432        self
433    }
434    /// Generates the same R.java file but with different package names.
435    pub fn extra_packages(&mut self, extra_packages: PathBuf) -> &mut Self {
436        self.extra_packages = Some(extra_packages);
437        self
438    }
439    /// Adds a JavaDoc annotation to all generated Java classes.
440    pub fn add_javadoc_annotation(&mut self, add_javadoc_annotation: String) -> &mut Self {
441        self.add_javadoc_annotation = Some(add_javadoc_annotation);
442        self
443    }
444    /// Generates a text file containing the resource symbols of the R class in the
445    /// specified file.
446    ///
447    /// You must specify the path to the output file.
448    pub fn output_text_symbols(&mut self, output_text_symbols: PathBuf) -> &mut Self {
449        self.output_text_symbols = Some(output_text_symbols);
450        self
451    }
452    /// Allows the addition of new resources in overlays without using the <add-resource>
453    /// tag.
454    pub fn auto_add_overlay(&mut self, auto_add_overlay: bool) -> &mut Self {
455        self.auto_add_overlay = auto_add_overlay;
456        self
457    }
458    /// Renames the package in AndroidManifest.xml.
459    pub fn rename_manifest_package(&mut self, rename_manifest_package: String) -> &mut Self {
460        self.rename_manifest_package = Some(rename_manifest_package);
461        self
462    }
463    /// Changes the name of the target package for [`instrumentation`].
464    ///
465    /// It should be used in conjunction with --rename-manifest-package.
466    ///
467    /// [`instrumentation`]: https://developer.android.com/reference/android/app/Instrumentation
468    pub fn rename_instrumentation_target_package(
469        &mut self,
470        rename_instrumentation_target_package: String,
471    ) -> &mut Self {
472        self.rename_instrumentation_target_package = Some(rename_instrumentation_target_package);
473        self
474    }
475    /// Do not compress any resources.
476    pub fn no_compress(&mut self, no_compress: bool) -> &mut Self {
477        self.no_compress = no_compress;
478        self
479    }
480    /// Preserve raw attribute values in xml files.
481    pub fn keep_raw_values(&mut self, keep_raw_values: bool) -> &mut Self {
482        self.keep_raw_values = keep_raw_values;
483        self
484    }
485    /// Specifies the extensions of files that you do not want to compress.
486    pub fn extension(&mut self, extension: String) -> &mut Self {
487        self.extensions.push(extension);
488        self
489    }
490    /// Do not compress extensions matching the regular expression. Remember to use the
491    /// '$' symbol for end of line. Uses a case-sensitive ECMAScriptregular expression
492    /// grammar.
493    pub fn no_compress_regex(&mut self, no_compress_regex: String) -> &mut Self {
494        self.no_compress_regex = Some(no_compress_regex);
495        self
496    }
497    /// Treat manifest validation errors as warnings.
498    pub fn warn_manifest_validation(&mut self, warn_manifest_validation: bool) -> &mut Self {
499        self.warn_manifest_validation = warn_manifest_validation;
500        self
501    }
502    /// Splits resources based on a set of configurations to generate a different version
503    /// of the APK.
504    ///
505    /// You must specify the path to the output APK along with the set of configurations.
506    pub fn split(&mut self, split: PathBuf) -> &mut Self {
507        self.split = Some(split);
508        self
509    }
510    /// Do not allow overlays with different visibility levels.
511    pub fn strict_visibility(&mut self, strict_visibility: bool) -> &mut Self {
512        self.strict_visibility = strict_visibility;
513        self
514    }
515    /// Generate systrace json trace fragment to specified folder.
516    pub fn trace_folder(&mut self, trace_folder: String) -> &mut Self {
517        self.trace_folder = Some(trace_folder);
518        self
519    }
520    /// Do not serialize source file information when generating resources in Protobuf
521    /// format.
522    pub fn exclude_sources(&mut self, exclude_sources: bool) -> &mut Self {
523        self.exclude_sources = exclude_sources;
524        self
525    }
526    /// Only merge the resources, without verifying resource references. This flag should
527    /// only be used together with the --static-lib flag.
528    pub fn merge_only(&mut self, merge_only: bool) -> &mut Self {
529        self.merge_only = merge_only;
530        self
531    }
532    /// Enables increased verbosity of the output.
533    pub fn verbose(&mut self, verbose: bool) -> &mut Self {
534        self.verbose = verbose;
535        self
536    }
537    /// Displays this help menu
538    pub fn help(&mut self, help: bool) -> &mut Self {
539        self.help = help;
540        self
541    }
542
543    pub fn run(&self) -> Result<()> {
544        let mut aapt2 = Command::new("aapt2");
545        aapt2.arg("link");
546        if !self.inputs.is_empty() {
547            self.inputs.iter().for_each(|input| {
548                aapt2.arg(input);
549            });
550        } else if let Some(compiled_res) = &self.compiled_res {
551            // TODO: handle errors, return err if path not found
552            let paths = std::fs::read_dir(compiled_res)?
553                .map(|e| e.map(|x| x.path()))
554                .flatten()
555                .collect::<Vec<_>>();
556            paths.iter().for_each(|input| {
557                aapt2.arg(input);
558            });
559        }
560        aapt2.arg("-o").arg(&self.output_apk);
561        aapt2.arg("--manifest").arg(&self.manifest);
562        if let Some(android_jar) = &self.android_jar {
563            aapt2.arg("-I").arg(android_jar);
564        }
565        if let Some(assets) = &self.assets {
566            aapt2.arg("-A").arg(assets);
567        }
568        if let Some(individual_flat) = &self.individual_flat {
569            aapt2.arg("-R").arg(individual_flat);
570        }
571        if let Some(package_id) = &self.package_id {
572            aapt2.arg("--package-id").arg(package_id);
573        }
574        if self.allow_reserved_package_id {
575            aapt2.arg("--allow-reserved-package-id");
576        }
577        if let Some(java) = &self.java {
578            aapt2.arg("--java").arg(java);
579        }
580        if let Some(proguard_options) = &self.proguard_options {
581            aapt2.arg("--proguard").arg(proguard_options);
582        }
583        if self.proguard_conditional_keep_rules {
584            aapt2.arg("--proguard-conditional-keep-rules");
585        }
586        if self.no_auto_version {
587            aapt2.arg("--no-auto-version");
588        }
589        if self.no_version_vectors {
590            aapt2.arg("--no-version-vectors");
591        }
592        if self.no_version_transitions {
593            aapt2.arg("--no-version-transitions");
594        }
595        if self.no_resource_deduping {
596            aapt2.arg("--no-resource-deduping");
597        }
598        if self.enable_sparse_encoding {
599            aapt2.arg("--enable-sparse-encoding");
600        }
601        if self.suggested_strings {
602            aapt2.arg("-z");
603        }
604        if !self.config.is_empty() {
605            aapt2.arg("-c").arg(self.config.join(","));
606        }
607        if let Some(preferred_density) = self.preferred_density {
608            aapt2
609                .arg("--preferred-density")
610                .arg(preferred_density.to_string());
611        }
612        if self.output_to_dir {
613            aapt2.arg("--output-to-dir");
614        }
615        if let Some(min_sdk_version) = self.min_sdk_version {
616            aapt2
617                .arg("--min-sdk-version")
618                .arg(min_sdk_version.to_string());
619        }
620        if let Some(target_sdk_version) = self.target_sdk_version {
621            aapt2
622                .arg("--target-sdk-version")
623                .arg(target_sdk_version.to_string());
624        }
625        if let Some(version_code) = &self.version_code {
626            aapt2.arg("--version-code").arg(version_code.to_string());
627        }
628        if let Some(compile_sdk_version_name) = &self.compile_sdk_version_name {
629            aapt2
630                .arg("--compile-sdk-version-name")
631                .arg(compile_sdk_version_name);
632        }
633        if self.proto_format {
634            aapt2.arg("--proto-format");
635        }
636        if self.non_final_ids {
637            aapt2.arg("--non-final-ids");
638        }
639        if let Some(emit_ids) = &self.emit_ids {
640            aapt2.arg("--emit-ids").arg(emit_ids);
641        }
642        if let Some(stable_ids) = &self.stable_ids {
643            aapt2.arg("--stable-ids").arg(stable_ids);
644        }
645        if let Some(custom_package) = &self.custom_package {
646            aapt2.arg("--custom-package").arg(custom_package);
647        }
648        if let Some(extra_packages) = &self.extra_packages {
649            aapt2.arg("--extra-packages").arg(extra_packages);
650        }
651        if let Some(add_javadoc_annotation) = &self.add_javadoc_annotation {
652            aapt2
653                .arg("--add-javadoc-annotation")
654                .arg(add_javadoc_annotation);
655        }
656        if let Some(output_text_symbols) = &self.output_text_symbols {
657            aapt2.arg("--output-text-symbols").arg(output_text_symbols);
658        }
659        if self.auto_add_overlay {
660            aapt2.arg("--auto-add-overlay");
661        }
662        if let Some(rename_manifest_package) = &self.rename_manifest_package {
663            aapt2
664                .arg("--rename-manifest-package")
665                .arg(rename_manifest_package);
666        }
667        if let Some(rename_instrumentation_target_package) =
668            &self.rename_instrumentation_target_package
669        {
670            aapt2
671                .arg("--rename-instrumentation-target-package")
672                .arg(rename_instrumentation_target_package);
673        }
674        self.extensions.iter().for_each(|extension| {
675            aapt2.arg("-0").arg(extension);
676        });
677        if let Some(split) = &self.split {
678            aapt2.arg("--split").arg(split);
679        }
680        if self.verbose {
681            aapt2.arg("-v");
682        }
683        if self.help {
684            aapt2.arg("-h");
685        }
686        if let Some(proguard_main_dex) = &self.proguard_main_dex {
687            aapt2.arg("--proguard-main-dex").arg(proguard_main_dex);
688        }
689        if self.proguard_minimal_keep_rules {
690            aapt2.arg("--proguard-minimal-keep-rules");
691        }
692        if self.no_resource_removal {
693            aapt2.arg("--no-resource-removal");
694        }
695        if self.package_identifier {
696            aapt2.arg("-x");
697        }
698        if let Some(product) = &self.product {
699            aapt2.arg("--product").arg(product);
700        }
701        if self.no_xml_namespaces {
702            aapt2.arg("--no-xml-namespaces");
703        }
704        if let Some(version_code_major) = &self.version_code_major {
705            aapt2
706                .arg("--version-code-major")
707                .arg(version_code_major.to_string());
708        }
709        if let Some(version_name) = &self.version_name {
710            aapt2.arg("--version-name").arg(version_name);
711        }
712        if self.replace_version {
713            aapt2.arg("--replace-version");
714        }
715        if let Some(compile_sdk_version_code) = &self.compile_sdk_version_code {
716            aapt2
717                .arg("--compile-sdk-version-code")
718                .arg(compile_sdk_version_code.to_string());
719        }
720        if self.shared_lib {
721            aapt2.arg("--shared-lib");
722        }
723        if self.static_lib {
724            aapt2.arg("--static-lib");
725        }
726        if self.no_static_lib_packages {
727            aapt2.arg("--no-static-lib-packages");
728        }
729        if self.no_proguard_location_reference {
730            aapt2.arg("--no-proguard-location-reference");
731        }
732        if let Some(private_symbols) = &self.private_symbols {
733            aapt2.arg("--private-symbols").arg(private_symbols);
734        }
735        if self.override_styles_instead_of_overlaying {
736            aapt2.arg("--override-styles-instead-of-overlaying");
737        }
738        if let Some(rename_resources_package) = &self.rename_resources_package {
739            aapt2
740                .arg("--rename-resources-package")
741                .arg(rename_resources_package);
742        }
743        if self.no_compress {
744            aapt2.arg("--no-compress");
745        }
746        if self.keep_raw_values {
747            aapt2.arg("--keep-raw-values");
748        }
749        if let Some(no_compress_regex) = &self.no_compress_regex {
750            aapt2.arg("--no-compress-regex").arg(no_compress_regex);
751        }
752        if self.warn_manifest_validation {
753            aapt2.arg("--warn-manifest-validation");
754        }
755        if self.strict_visibility {
756            aapt2.arg("--strict-visibility");
757        }
758        if self.exclude_sources {
759            aapt2.arg("--exclude-sources");
760        }
761        if let Some(trace_folder) = &self.trace_folder {
762            aapt2.arg("--trace-folder").arg(trace_folder);
763        }
764        if self.merge_only {
765            aapt2.arg("--merge-only");
766        }
767        aapt2.output_err(true)?;
768        Ok(())
769    }
770}
771
772// #[cfg(test)]
773// mod tests {
774//     use crate::{commands::android, tools::AndroidSdk};
775
776//     use super::*;
777
778//     #[test]
779//     fn test_command_run() {
780
781//         let manifest = android::gen_minimal_android_manifest(
782//             None,
783//             "example",
784//             None,
785//             "0.0.1".to_string(),
786//             None,
787//             None,
788//             30,
789//             None,
790//             None,
791//             false,
792//         );
793//         let manifest_path =
794// android::save_android_manifest(Path::new("C:\\Users\\ZatNieL\\AppData\\Local\\Temp\\.
795// tmpOz3Ra7\\target\\android\\debug\\compiled_res\\"), &manifest).unwrap();
796//         assert!(manifest_path.exists());
797//         let sdk = AndroidSdk::from_env().unwrap();
798//         Aapt2Link::new_from_compiled_res(Some(Path::new(
799//"C:\\Users\\ZatNieL\\AppData\\Local\\Temp\\.tmpOz3Ra7\\target\\android\\debug\\compiled_res\\").to_owned()), Path::new(
800//"C:\\Users\\ZatNieL\\AppData\\Local\\Temp\\.tmpOz3Ra7\\target\\android\\debug\\compiled_res\\"), &manifest_path).android_jar(sdk.android_jar(30).unwrap())
801//.version_code(1)
802//.proto_format(true)
803//.auto_add_overlay(true).run().unwrap();
804//     }
805// }