xcode_script.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use super::{env, get_app, get_config, read_options};
  5. use crate::{
  6. helpers::config::get as get_tauri_config,
  7. interface::{AppInterface, AppSettings, Interface, Options as InterfaceOptions},
  8. Result,
  9. };
  10. use cargo_mobile2::{apple::target::Target, opts::Profile};
  11. use clap::Parser;
  12. use std::{
  13. collections::HashMap,
  14. env::{current_dir, set_current_dir, var_os},
  15. ffi::OsStr,
  16. path::PathBuf,
  17. };
  18. #[derive(Debug, Parser)]
  19. pub struct Options {
  20. /// Value of `PLATFORM_DISPLAY_NAME` env var
  21. #[clap(long)]
  22. platform: String,
  23. /// Value of `SDKROOT` env var
  24. #[clap(long)]
  25. sdk_root: PathBuf,
  26. /// Value of `FRAMEWORK_SEARCH_PATHS` env var
  27. #[clap(long)]
  28. framework_search_paths: String,
  29. /// Value of `GCC_PREPROCESSOR_DEFINITIONS` env var
  30. #[clap(long)]
  31. gcc_preprocessor_definitions: String,
  32. /// Value of `HEADER_SEARCH_PATHS` env var
  33. #[clap(long)]
  34. header_search_paths: String,
  35. /// Value of `CONFIGURATION` env var
  36. #[clap(long)]
  37. configuration: String,
  38. /// Value of `FORCE_COLOR` env var
  39. #[clap(long)]
  40. force_color: bool,
  41. /// Value of `ARCHS` env var
  42. #[clap(index = 1, required = true)]
  43. arches: Vec<String>,
  44. }
  45. pub fn command(options: Options) -> Result<()> {
  46. fn macos_from_platform(platform: &str) -> bool {
  47. platform == "macOS"
  48. }
  49. fn profile_from_configuration(configuration: &str) -> Profile {
  50. if configuration == "release" {
  51. Profile::Release
  52. } else {
  53. Profile::Debug
  54. }
  55. }
  56. // `xcode-script` is ran from the `gen/apple` folder when not using NPM.
  57. if var_os("npm_lifecycle_event").is_none() {
  58. set_current_dir(current_dir()?.parent().unwrap().parent().unwrap()).unwrap();
  59. }
  60. let profile = profile_from_configuration(&options.configuration);
  61. let macos = macos_from_platform(&options.platform);
  62. let tauri_config = get_tauri_config(tauri_utils::platform::Target::Ios, None)?;
  63. let (config, metadata, cli_options) = {
  64. let tauri_config_guard = tauri_config.lock().unwrap();
  65. let tauri_config_ = tauri_config_guard.as_ref().unwrap();
  66. let cli_options = read_options(&tauri_config_.tauri.bundle.identifier);
  67. let (config, metadata) = get_config(&get_app(tauri_config_), tauri_config_, &cli_options);
  68. (config, metadata, cli_options)
  69. };
  70. let env = env()?.explicit_env_vars(cli_options.vars);
  71. if !options.sdk_root.is_dir() {
  72. return Err(anyhow::anyhow!(
  73. "SDK root provided by Xcode was invalid. {} doesn't exist or isn't a directory",
  74. options.sdk_root.display(),
  75. ));
  76. }
  77. let include_dir = options.sdk_root.join("usr/include");
  78. if !include_dir.is_dir() {
  79. return Err(anyhow::anyhow!(
  80. "Include dir was invalid. {} doesn't exist or isn't a directory",
  81. include_dir.display()
  82. ));
  83. }
  84. // Host flags that are used by build scripts
  85. let macos_isysroot = {
  86. let macos_sdk_root = options
  87. .sdk_root
  88. .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk");
  89. if !macos_sdk_root.is_dir() {
  90. return Err(anyhow::anyhow!(
  91. "Invalid SDK root {}",
  92. macos_sdk_root.display()
  93. ));
  94. }
  95. format!("-isysroot {}", macos_sdk_root.display())
  96. };
  97. let mut host_env = HashMap::<&str, &OsStr>::new();
  98. host_env.insert("RUST_BACKTRACE", "1".as_ref());
  99. host_env.insert("CFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref());
  100. host_env.insert("CXXFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref());
  101. host_env.insert(
  102. "OBJC_INCLUDE_PATH_x86_64_apple_darwin",
  103. include_dir.as_os_str(),
  104. );
  105. host_env.insert(
  106. "FRAMEWORK_SEARCH_PATHS",
  107. options.framework_search_paths.as_ref(),
  108. );
  109. host_env.insert(
  110. "GCC_PREPROCESSOR_DEFINITIONS",
  111. options.gcc_preprocessor_definitions.as_ref(),
  112. );
  113. host_env.insert("HEADER_SEARCH_PATHS", options.header_search_paths.as_ref());
  114. let macos_target = Target::macos();
  115. let isysroot = format!("-isysroot {}", options.sdk_root.display());
  116. for arch in options.arches {
  117. // Set target-specific flags
  118. let (env_triple, rust_triple) = match arch.as_str() {
  119. "arm64" => ("aarch64_apple_ios", "aarch64-apple-ios"),
  120. "arm64-sim" => ("aarch64_apple_ios_sim", "aarch64-apple-ios-sim"),
  121. "x86_64" => ("x86_64_apple_ios", "x86_64-apple-ios"),
  122. "Simulator" => {
  123. // when using Xcode, the arches for a simulator build will be ['Simulator', 'arm64-sim'] instead of ['arm64-sim']
  124. // so we ignore that on our end
  125. continue;
  126. }
  127. _ => {
  128. return Err(anyhow::anyhow!(
  129. "Arch specified by Xcode was invalid. {} isn't a known arch",
  130. arch
  131. ))
  132. }
  133. };
  134. let interface = AppInterface::new(
  135. tauri_config.lock().unwrap().as_ref().unwrap(),
  136. Some(rust_triple.into()),
  137. )?;
  138. let cflags = format!("CFLAGS_{}", env_triple);
  139. let cxxflags = format!("CFLAGS_{}", env_triple);
  140. let objc_include_path = format!("OBJC_INCLUDE_PATH_{}", env_triple);
  141. let mut target_env = host_env.clone();
  142. target_env.insert(cflags.as_ref(), isysroot.as_ref());
  143. target_env.insert(cxxflags.as_ref(), isysroot.as_ref());
  144. target_env.insert(objc_include_path.as_ref(), include_dir.as_ref());
  145. let target = if macos {
  146. &macos_target
  147. } else {
  148. Target::for_arch(&arch).ok_or_else(|| {
  149. anyhow::anyhow!(
  150. "Arch specified by Xcode was invalid. {} isn't a known arch",
  151. arch
  152. )
  153. })?
  154. };
  155. target.compile_lib(
  156. &config,
  157. &metadata,
  158. cli_options.noise_level,
  159. true,
  160. profile,
  161. &env,
  162. target_env,
  163. )?;
  164. let bin_path = interface
  165. .app_settings()
  166. .app_binary_path(&InterfaceOptions {
  167. debug: matches!(profile, Profile::Debug),
  168. target: Some(rust_triple.into()),
  169. ..Default::default()
  170. })?;
  171. let out_dir = bin_path.parent().unwrap();
  172. let lib_path = out_dir.join(format!("lib{}.a", config.app().lib_name()));
  173. if !lib_path.exists() {
  174. return Err(anyhow::anyhow!("Library not found at {}. Make sure your Cargo.toml file has a [lib] block with `crate-type = [\"staticlib\", \"cdylib\", \"rlib\"]`", lib_path.display()));
  175. }
  176. let project_dir = config.project_dir();
  177. let externals_lib_dir = project_dir.join(format!("Externals/{arch}/{}", profile.as_str()));
  178. std::fs::create_dir_all(&externals_lib_dir)?;
  179. std::fs::copy(
  180. lib_path,
  181. externals_lib_dir.join(format!("lib{}.a", config.app().lib_name())),
  182. )?;
  183. }
  184. Ok(())
  185. }