cli/commands/build/
apple.rs

1use super::{BuildContext, SharedBuildCommand};
2use crate::error::*;
3use apple_bundle::prelude::InfoPlist;
4use clap::Clap;
5use creator_tools::{commands::apple, types::*, utils::Config};
6use std::path::{Path, PathBuf};
7
8#[derive(Clap, Clone, Debug)]
9pub struct AppleBuildCommand {
10    #[clap(flatten)]
11    pub shared: SharedBuildCommand,
12    /// Specify custom cargo binary
13    #[clap(long, conflicts_with = "example")]
14    pub bin: Option<String>,
15    /// Build for the given apple architecture.
16    /// Supported targets are: 'aarch64-apple-ios`, `armv7-apple-ios`, `armv7s-apple-ios`, `i386-apple-ios`, `x86_64-apple-ios`
17    #[clap(long, default_value = "aarch64-apple-ios")]
18    pub target: Vec<AppleTarget>,
19    /// Provisioning profile name to find in this directory: `~/Library/MobileDevice/Provisioning\ Profiles/`
20    #[clap(long, conflicts_with = "profile-path")]
21    pub profile_name: Option<String>,
22    /// Absolute path to provisioning profile
23    #[clap(long)]
24    pub profile_path: Option<PathBuf>,
25    /// The team identifier of your signing identity
26    #[clap(long)]
27    pub team_identifier: Option<String>,
28    /// The id of the identity used for signing. It won't start the signing process until you provide this flag
29    #[clap(long)]
30    pub identity: Option<String>,
31}
32
33impl AppleBuildCommand {
34    pub fn run(&self, config: &Config) -> Result<()> {
35        let context = BuildContext::new(config, self.shared.target_dir.clone())?;
36        self.execute(config, &context)?;
37        Ok(())
38    }
39
40    pub fn execute(
41        &self,
42        config: &Config,
43        context: &BuildContext,
44    ) -> Result<(InfoPlist, Vec<PathBuf>)> {
45        let project_path = &context.project_path;
46        let profile = self.shared.profile();
47        let (target, package_name) = if let Some(example) = &self.shared.example {
48            (Target::Example(example.clone()), example.clone())
49        } else if let Some(bin) = &self.bin {
50            (Target::Bin(bin.clone()), bin.clone())
51        } else {
52            (Target::Bin(context.package_name()), context.package_name())
53        };
54        let properties = context.gen_info_plist(&package_name)?;
55        config.status_message("Starting build process", &package_name)?;
56        config.status("Compiling app")?;
57        let build_targets = context.apple_build_targets(&self.target);
58        let mut app_paths = vec![];
59        for build_target in build_targets {
60            let app_path = self.build_app(
61                config,
62                context,
63                target.clone(),
64                project_path,
65                build_target,
66                &properties,
67                profile,
68                &package_name,
69            )?;
70            app_paths.push(app_path);
71        }
72        Ok((properties, app_paths))
73    }
74
75    fn build_app(
76        &self,
77        config: &Config,
78        context: &BuildContext,
79        target: Target,
80        project_path: &Path,
81        build_target: AppleTarget,
82        properties: &InfoPlist,
83        profile: Profile,
84        name: &str,
85    ) -> Result<PathBuf> {
86        let rust_triple = build_target.rust_triple();
87        config.status_message("Compiling for architecture", rust_triple)?;
88        apple::compile_rust_for_ios(
89            target,
90            build_target,
91            &project_path,
92            profile,
93            self.shared.features.clone(),
94            self.shared.all_features,
95            self.shared.no_default_features,
96        )?;
97        let out_dir = context.target_dir.join(rust_triple).join(&profile);
98        let bin_path = out_dir.join(&name);
99        config.status("Generating app folder")?;
100        let apple_target_dir = &context
101            .target_dir
102            .join("apple")
103            .join(rust_triple)
104            .join(&profile);
105        let app_path = apple::gen_apple_app_folder(
106            &apple_target_dir,
107            &name,
108            context.apple_res().as_ref().map(|r| project_path.join(r)),
109            context
110                .apple_assets()
111                .as_ref()
112                .map(|r| project_path.join(r)),
113        )?;
114        config.status("Copying binary to app folder")?;
115        std::fs::copy(&bin_path, &app_path.join(&name)).unwrap();
116        config.status_message("Generating", "Info.plist")?;
117        apple::save_apple_plist(&app_path, properties, false).unwrap();
118        if self.identity.is_some() {
119            config.status("Starting code signing process")?;
120            apple::copy_profile(
121                &app_path,
122                self.profile_name.clone(),
123                self.profile_path.clone(),
124            )?;
125            config.status_message("Generating", "xcent file")?;
126            let xcent_path = apple::gen_xcent(
127                &app_path,
128                &name,
129                self.team_identifier
130                    .as_ref()
131                    .ok_or(Error::TeamIdentifierNotProvided)?,
132                &properties.identification.bundle_identifier,
133                false,
134            )?;
135            config.status("Signing the binary")?;
136            apple::codesign(&app_path.join(&name), true, self.identity.clone(), None)?;
137            config.status("Signing the bundle itself")?;
138            apple::codesign(&app_path, true, self.identity.clone(), Some(xcent_path))?;
139            config.status("Code signing process finished")?;
140        }
141        config.status("Generating ipa file")?;
142        apple::gen_apple_ipa(&apple_target_dir, &app_path, &name)?;
143        config.status("Build finished successfully")?;
144        Ok(app_path)
145    }
146}