build.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. use super::{
  2. detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config,
  3. Error, MobileTarget,
  4. };
  5. use crate::{
  6. helpers::{config::get as get_tauri_config, flock},
  7. interface::{AppSettings, Interface, Options as InterfaceOptions},
  8. mobile::{write_options, CliOptions},
  9. Result,
  10. };
  11. use clap::Parser;
  12. use cargo_mobile::{
  13. apple::{config::Config as AppleConfig, target::Target},
  14. env::Env,
  15. opts::{NoiseLevel, Profile},
  16. target::{call_for_targets_with_fallback, TargetInvalid, TargetTrait},
  17. };
  18. use std::fs;
  19. #[derive(Debug, Clone, Parser)]
  20. #[clap(about = "Android build")]
  21. pub struct Options {
  22. /// Builds with the debug flag
  23. #[clap(short, long)]
  24. pub debug: bool,
  25. /// Which targets to build.
  26. #[clap(
  27. short,
  28. long = "target",
  29. multiple_occurrences(true),
  30. multiple_values(true),
  31. default_value = Target::DEFAULT_KEY,
  32. value_parser(clap::builder::PossibleValuesParser::new(Target::name_list()))
  33. )]
  34. pub targets: Vec<String>,
  35. /// List of cargo features to activate
  36. #[clap(short, long, multiple_occurrences(true), multiple_values(true))]
  37. pub features: Option<Vec<String>>,
  38. /// JSON string or path to JSON file to merge with tauri.conf.json
  39. #[clap(short, long)]
  40. pub config: Option<String>,
  41. /// Build number to append to the app version.
  42. #[clap(long)]
  43. pub build_number: Option<u32>,
  44. /// Open Xcode
  45. #[clap(short, long)]
  46. pub open: bool,
  47. }
  48. impl From<Options> for crate::build::Options {
  49. fn from(options: Options) -> Self {
  50. Self {
  51. runner: None,
  52. debug: options.debug,
  53. target: None,
  54. features: options.features,
  55. bundles: None,
  56. config: options.config,
  57. args: Vec::new(),
  58. }
  59. }
  60. }
  61. pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
  62. with_config(
  63. Some(Default::default()),
  64. |root_conf, config, _metadata, _cli_options| {
  65. ensure_init(config.project_dir(), MobileTarget::Ios)
  66. .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
  67. let env = env()?;
  68. init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?;
  69. let open = options.open;
  70. run_build(options, config, &env, noise_level)
  71. .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?;
  72. if open {
  73. open_and_wait(config, &env);
  74. }
  75. Ok(())
  76. },
  77. )
  78. .map_err(Into::into)
  79. }
  80. fn run_build(
  81. mut options: Options,
  82. config: &AppleConfig,
  83. env: &Env,
  84. noise_level: NoiseLevel,
  85. ) -> Result<()> {
  86. let profile = if options.debug {
  87. Profile::Debug
  88. } else {
  89. Profile::Release
  90. };
  91. let bundle_identifier = {
  92. let tauri_config = get_tauri_config(None)?;
  93. let tauri_config_guard = tauri_config.lock().unwrap();
  94. let tauri_config_ = tauri_config_guard.as_ref().unwrap();
  95. tauri_config_.tauri.bundle.identifier.clone()
  96. };
  97. let mut build_options = options.clone().into();
  98. let interface = crate::build::setup(&mut build_options)?;
  99. let app_settings = interface.app_settings();
  100. let bin_path = app_settings.app_binary_path(&InterfaceOptions {
  101. debug: build_options.debug,
  102. ..Default::default()
  103. })?;
  104. let out_dir = bin_path.parent().unwrap();
  105. let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?;
  106. let cli_options = CliOptions {
  107. features: build_options.features.clone(),
  108. args: build_options.args.clone(),
  109. noise_level,
  110. vars: Default::default(),
  111. };
  112. write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?;
  113. options
  114. .features
  115. .get_or_insert(Vec::new())
  116. .push("custom-protocol".into());
  117. let mut out_files = Vec::new();
  118. call_for_targets_with_fallback(
  119. options.targets.iter(),
  120. &detect_target_ok,
  121. env,
  122. |target: &Target| {
  123. let mut app_version = config.bundle_version().clone();
  124. if let Some(build_number) = options.build_number {
  125. app_version.push_extra(build_number);
  126. }
  127. target.build(config, env, noise_level, profile)?;
  128. target.archive(config, env, noise_level, profile, Some(app_version))?;
  129. target.export(config, env, noise_level)?;
  130. if let Ok(ipa_path) = config.ipa_path() {
  131. let out_dir = config.export_dir().join(target.arch);
  132. fs::create_dir_all(&out_dir)?;
  133. let path = out_dir.join(ipa_path.file_name().unwrap());
  134. fs::rename(&ipa_path, &path)?;
  135. out_files.push(path);
  136. }
  137. anyhow::Result::Ok(())
  138. },
  139. )
  140. .map_err(|e: TargetInvalid| Error::TargetInvalid(e.to_string()))?
  141. .map_err(|e: anyhow::Error| e)?;
  142. log_finished(out_files, "IPA");
  143. Ok(())
  144. }