Explorar el Código

feat(cli): synchronize Tauri config and lib name with iOS Xcode project (#10802)

- the Xcode project now uses a fixed output library name, which means changes to the Cargo.toml lib name won't affect it (backwards compatible change, we're checking if this new format is being used or not by reading the project.pbxproj)
- sync config identifier with the pbxproj
- sync development team config with the pbxproj

the sync runs both on dev and on build

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
Lucas Fernandes Nogueira hace 11 meses
padre
commit
f67a9eb6de

+ 6 - 0
.changes/synchronize-config-and-xcode-project.md

@@ -0,0 +1,6 @@
+---
+"tauri-cli": patch:enhance
+"@tauri-apps/cli": patch:enhance
+---
+
+Synchronize identifier, development team and lib name with the iOS Xcode project.

+ 2 - 2
Cargo.lock

@@ -747,9 +747,9 @@ dependencies = [
 
 [[package]]
 name = "cargo-mobile2"
-version = "0.15.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b1baf430f92ddf6e492c9186e4e97dc3051366345b8aa7ffdc0e0f952c9a35b"
+checksum = "d0b8132519bea2d46174e777bd36d480d93afbe1df31c27cacfb411ff152bba1"
 dependencies = [
  "colored",
  "core-foundation 0.10.0",

+ 1 - 1
crates/tauri-cli/Cargo.toml

@@ -36,7 +36,7 @@ name = "cargo-tauri"
 path = "src/main.rs"
 
 [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
-cargo-mobile2 = { version = "0.15", default-features = false }
+cargo-mobile2 = { version = "0.15.1", default-features = false }
 
 [dependencies]
 jsonrpsee = { version = "0.24", features = ["server"] }

+ 7 - 5
crates/tauri-cli/src/helpers/pbxproj.rs

@@ -230,12 +230,14 @@ impl Pbxproj {
       .iter_mut()
       .find(|s| s.key == key)
     {
-      let Some(line) = self.raw_lines.get_mut(build_setting.line_number) else {
-        return;
-      };
+      if build_setting.value != value {
+        let Some(line) = self.raw_lines.get_mut(build_setting.line_number) else {
+          return;
+        };
 
-      *line = format!("{}{key} = {value};", build_setting.identation);
-      self.has_changes = true;
+        *line = format!("{}{key} = {value};", build_setting.identation);
+        self.has_changes = true;
+      }
     } else {
       let Some(last_build_setting) = build_configuration.build_settings.last().cloned() else {
         return;

+ 4 - 3
crates/tauri-cli/src/mobile/ios/build.rs

@@ -146,7 +146,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
     tauri_utils::platform::Target::Ios,
     options.config.as_ref().map(|c| &c.0),
   )?;
-  let (interface, app, mut config) = {
+  let (interface, mut config) = {
     let tauri_config_guard = tauri_config.lock().unwrap();
     let tauri_config_ = tauri_config_guard.as_ref().unwrap();
 
@@ -160,7 +160,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
       build_options.features.as_ref(),
       &Default::default(),
     );
-    (interface, app, config)
+    (interface, config)
   };
 
   let tauri_path = tauri_dir();
@@ -198,7 +198,8 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
 
   // synchronize pbxproj and exportoptions
   synchronize_project_config(
-    &app,
+    &config,
+    &tauri_config,
     &mut pbxproj,
     &mut export_options_plist,
     &project_config,

+ 21 - 2
crates/tauri-cli/src/mobile/ios/dev.rs

@@ -3,8 +3,8 @@
 // SPDX-License-Identifier: MIT
 
 use super::{
-  device_prompt, ensure_init, env, get_app, get_config, inject_resources, merge_plist,
-  open_and_wait, MobileTarget,
+  device_prompt, ensure_init, env, get_app, get_config, inject_resources, load_pbxproj,
+  merge_plist, open_and_wait, synchronize_project_config, MobileTarget, ProjectConfig,
 };
 use crate::{
   dev::Options as DevOptions,
@@ -191,6 +191,25 @@ fn run_command(options: Options, noise_level: NoiseLevel) -> Result<()> {
   ])?;
   merged_info_plist.to_file_xml(&info_plist_path)?;
 
+  let mut pbxproj = load_pbxproj(&config)?;
+
+  // synchronize pbxproj
+  synchronize_project_config(
+    &config,
+    &tauri_config,
+    &mut pbxproj,
+    &mut plist::Dictionary::new(),
+    &ProjectConfig {
+      code_sign_identity: None,
+      team_id: None,
+      provisioning_profile_uuid: None,
+    },
+    !options.release_mode,
+  )?;
+  if pbxproj.has_changes() {
+    pbxproj.save()?;
+  }
+
   run_dev(
     interface,
     options,

+ 32 - 6
crates/tauri-cli/src/mobile/ios/mod.rs

@@ -29,7 +29,7 @@ use super::{
 use crate::{
   helpers::{
     app_paths::tauri_dir,
-    config::{BundleResources, Config as TauriConfig},
+    config::{BundleResources, Config as TauriConfig, ConfigHandle},
     pbxproj,
   },
   Result,
@@ -49,6 +49,7 @@ pub(crate) mod project;
 mod xcode_script;
 
 pub const APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME: &str = "APPLE_DEVELOPMENT_TEAM";
+pub const LIB_OUTPUT_FILE_NAME: &str = "libapp.a";
 
 #[derive(Parser)]
 #[clap(
@@ -131,7 +132,7 @@ pub fn get_config(
               log::warn!("No code signing certificates found. You must add one and set the certificate development team ID on the `bundle > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. To list the available certificates, run `tauri info`.");
               None
             }
-            1 => Some(teams.first().unwrap().id.clone()),
+            1 =>None,
             _ => {
               log::warn!("You must set the code signing certificate development team ID on  the `bundle > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. Available certificates: {}", teams.iter().map(|t| format!("{} (ID: {})", t.name, t.id)).collect::<Vec<String>>().join(", "));
               None
@@ -421,12 +422,21 @@ pub fn load_pbxproj(config: &AppleConfig) -> Result<pbxproj::Pbxproj> {
 }
 
 pub fn synchronize_project_config(
-  app: &App,
+  config: &AppleConfig,
+  tauri_config: &ConfigHandle,
   pbxproj: &mut pbxproj::Pbxproj,
   export_options_list: &mut plist::Dictionary,
   project_config: &ProjectConfig,
   debug: bool,
 ) -> Result<()> {
+  let identifier = tauri_config
+    .lock()
+    .unwrap()
+    .as_ref()
+    .unwrap()
+    .identifier
+    .clone();
+
   let manual_signing = project_config.code_sign_identity.is_some()
     || project_config.provisioning_profile_uuid.is_some();
 
@@ -437,10 +447,26 @@ pub fn synchronize_project_config(
     .find(|l| l.comment.contains("_iOS"))
   {
     for build_configuration_ref in xc_configuration_list.build_configurations {
-      if manual_signing {
-        pbxproj.set_build_settings(&build_configuration_ref.id, "CODE_SIGN_STYLE", "Manual");
+      pbxproj.set_build_settings(
+        &build_configuration_ref.id,
+        "CODE_SIGN_STYLE",
+        if manual_signing {
+          "Manual"
+        } else {
+          "Automatic"
+        },
+      );
+
+      if let Some(team) = config.development_team() {
+        pbxproj.set_build_settings(&build_configuration_ref.id, "DEVELOPMENT_TEAM", team);
       }
 
+      pbxproj.set_build_settings(
+        &build_configuration_ref.id,
+        "PRODUCT_BUNDLE_IDENTIFIER",
+        &identifier,
+      );
+
       if let Some(identity) = &project_config.code_sign_identity {
         let identity = format!("\"{identity}\"");
         pbxproj.set_build_settings(&build_configuration_ref.id, "CODE_SIGN_IDENTITY", &identity);
@@ -536,7 +562,7 @@ pub fn synchronize_project_config(
       });
     if let Some(profile_uuid) = profile_uuid {
       let mut provisioning_profiles = plist::Dictionary::new();
-      provisioning_profiles.insert(app.identifier().to_string(), profile_uuid.into());
+      provisioning_profiles.insert(config.app().identifier().to_string(), profile_uuid.into());
       export_options_list.insert(
         "provisioningProfiles".to_string(),
         provisioning_profiles.into(),

+ 3 - 0
crates/tauri-cli/src/mobile/ios/project.rs

@@ -4,6 +4,7 @@
 
 use crate::{
   helpers::{config::Config as TauriConfig, template},
+  mobile::ios::LIB_OUTPUT_FILE_NAME,
   Result,
 };
 use anyhow::Context;
@@ -76,6 +77,8 @@ pub fn gen(
   #[cfg(not(target_arch = "aarch64"))]
   let default_archs = ["arm64", "x86_64"];
 
+  map.insert("lib-output-file-name", LIB_OUTPUT_FILE_NAME);
+
   map.insert("file-groups", &source_dirs);
   map.insert("ios-frameworks", metadata.ios().frameworks());
   map.insert("ios-valid-archs", default_archs);

+ 23 - 4
crates/tauri-cli/src/mobile/ios/xcode_script.rs

@@ -6,9 +6,11 @@ use super::{ensure_init, env, get_app, get_config, read_options, MobileTarget};
 use crate::{
   helpers::config::get as get_tauri_config,
   interface::{AppInterface, AppSettings, Interface, Options as InterfaceOptions},
+  mobile::ios::LIB_OUTPUT_FILE_NAME,
   Result,
 };
 
+use anyhow::Context;
 use cargo_mobile2::{apple::target::Target, opts::Profile};
 use clap::Parser;
 
@@ -16,6 +18,7 @@ use std::{
   collections::HashMap,
   env::{current_dir, set_current_dir, var, var_os},
   ffi::OsStr,
+  fs::read_to_string,
   path::{Path, PathBuf},
   process::Command,
 };
@@ -233,10 +236,26 @@ pub fn command(options: Options) -> Result<()> {
     let project_dir = config.project_dir();
     let externals_lib_dir = project_dir.join(format!("Externals/{arch}/{}", profile.as_str()));
     std::fs::create_dir_all(&externals_lib_dir)?;
-    std::fs::copy(
-      lib_path,
-      externals_lib_dir.join(format!("lib{}.a", config.app().lib_name())),
-    )?;
+
+    // backwards compatible lib output file name
+    let uses_new_lib_output_file_name = {
+      let pbxproj_contents = read_to_string(
+        project_dir
+          .join(format!("{}.xcodeproj", config.app().name()))
+          .join("project.pbxproj"),
+      )
+      .context("missing project.pbxproj file in the Xcode project")?;
+
+      pbxproj_contents.contains(LIB_OUTPUT_FILE_NAME)
+    };
+
+    let lib_output_file_name = if uses_new_lib_output_file_name {
+      LIB_OUTPUT_FILE_NAME.to_string()
+    } else {
+      format!("lib{}.a", config.app().lib_name())
+    };
+
+    std::fs::copy(lib_path, externals_lib_dir.join(lib_output_file_name))?;
   }
   Ok(())
 }

+ 9 - 10
crates/tauri-cli/src/mobile/mod.rs

@@ -329,17 +329,16 @@ fn ensure_init(
     }
     #[cfg(target_os = "macos")]
     Target::Ios => {
-      let project_yml = read_to_string(project_dir.join("project.yml"))
-        .context("missing project.yml file in the Xcode project directory")?;
-      if !project_yml.contains(&format!(
-        "PRODUCT_BUNDLE_IDENTIFIER: {}",
-        tauri_config_.identifier.replace('_', "-")
-      )) {
-        project_outdated_reasons
-          .push("you have modified your \"identifier\" in the Tauri configuration");
-      }
+      let pbxproj_contents = read_to_string(
+        project_dir
+          .join(format!("{}.xcodeproj", app.name()))
+          .join("project.pbxproj"),
+      )
+      .context("missing project.yml file in the Xcode project directory")?;
 
-      if !project_yml.contains(&format!("framework: lib{}.a", app.lib_name())) {
+      if !(pbxproj_contents.contains(ios::LIB_OUTPUT_FILE_NAME)
+        || pbxproj_contents.contains(&format!("lib{}.a", app.lib_name())))
+      {
         project_outdated_reasons
           .push("you have modified your [lib.name] or [package.name] in the Cargo.toml file");
       }

+ 4 - 4
crates/tauri-cli/templates/mobile/ios/project.yml

@@ -86,7 +86,7 @@ targets:
         EXCLUDED_ARCHS[sdk=iphoneos*]: arm64-sim x86_64
       groups: [app]
     dependencies:
-      - framework: lib{{app.lib-name}}.a
+      - framework: {{ lib-output-file-name }}
         embed: false
       {{~#each ios-libraries}}
       - framework: {{this}}
@@ -125,9 +125,9 @@ targets:
         name: Build Rust Code
         basedOnDependencyAnalysis: false
         outputFiles:
-          - $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/lib{{app.lib-name}}.a
-          - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/lib{{app.lib-name}}.a
-          - $(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/lib{{app.lib-name}}.a
+          - $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/{{ lib-output-file-name }}
+          - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/{{ lib-output-file-name }}
+          - $(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/{{ lib-output-file-name }}
     {{~#if ios-post-compile-scripts}}
     postCompileScripts:
       {{~#each ios-post-compile-scripts}}{{#if this.path}}