From b9c2759cfdbbf650b949e41b4eb24aa1c0e8d3f5 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 27 Aug 2025 18:01:37 -0300 Subject: [PATCH 1/2] fix(cli): ensure Xcode project is up to date with Cargo project name closes #13542 --- .changes/fix-ios-cmd-pkg-name-change.md | 6 ++ crates/tauri-cli/src/helpers/fs.rs | 17 +++- .../mobile/android/android_studio_script.rs | 1 + crates/tauri-cli/src/mobile/android/build.rs | 1 + crates/tauri-cli/src/mobile/android/dev.rs | 1 + crates/tauri-cli/src/mobile/ios/build.rs | 1 + crates/tauri-cli/src/mobile/ios/dev.rs | 1 + .../tauri-cli/src/mobile/ios/xcode_script.rs | 1 + crates/tauri-cli/src/mobile/mod.rs | 83 ++++++++++++++++--- 9 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 .changes/fix-ios-cmd-pkg-name-change.md diff --git a/.changes/fix-ios-cmd-pkg-name-change.md b/.changes/fix-ios-cmd-pkg-name-change.md new file mode 100644 index 000000000000..42c4eb1e71f6 --- /dev/null +++ b/.changes/fix-ios-cmd-pkg-name-change.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:bug +"@tauri-apps/cli": patch:bug +--- + +Fix iOS CLI usage after modifying the package name. diff --git a/crates/tauri-cli/src/helpers/fs.rs b/crates/tauri-cli/src/helpers/fs.rs index dd7491db13d4..e92ee2a855c6 100644 --- a/crates/tauri-cli/src/helpers/fs.rs +++ b/crates/tauri-cli/src/helpers/fs.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use anyhow::Result; -use std::path::Path; +use std::path::{Path, PathBuf}; pub fn copy_file(from: impl AsRef, to: impl AsRef) -> Result<()> { let from = from.as_ref(); @@ -19,3 +19,18 @@ pub fn copy_file(from: impl AsRef, to: impl AsRef) -> Result<()> { std::fs::copy(from, to)?; Ok(()) } + +pub fn find_in_directory(path: &Path, glob_pattern: &str) -> Result { + let pattern = glob::Pattern::new(glob_pattern)?; + for entry in std::fs::read_dir(path)? { + let entry = entry?; + if pattern.matches_path(&entry.path()) { + return Ok(entry.path()); + } + } + Err(anyhow::anyhow!( + "No file found in {} matching {}", + path.display(), + glob_pattern + )) +} diff --git a/crates/tauri-cli/src/mobile/android/android_studio_script.rs b/crates/tauri-cli/src/mobile/android/android_studio_script.rs index 7c9938852b88..d66b36401f4f 100644 --- a/crates/tauri-cli/src/mobile/android/android_studio_script.rs +++ b/crates/tauri-cli/src/mobile/android/android_studio_script.rs @@ -91,6 +91,7 @@ pub fn command(options: Options) -> Result<()> { config.app(), config.project_dir(), MobileTarget::Android, + std::env::var("CI").is_ok(), )?; if !cli_options.config.is_empty() { diff --git a/crates/tauri-cli/src/mobile/android/build.rs b/crates/tauri-cli/src/mobile/android/build.rs index 16a614cf2d50..2f74fc5c4a6b 100644 --- a/crates/tauri-cli/src/mobile/android/build.rs +++ b/crates/tauri-cli/src/mobile/android/build.rs @@ -160,6 +160,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { config.app(), config.project_dir(), MobileTarget::Android, + options.ci, )?; let mut env = env()?; diff --git a/crates/tauri-cli/src/mobile/android/dev.rs b/crates/tauri-cli/src/mobile/android/dev.rs index a6d3398845b0..88dd996b540d 100644 --- a/crates/tauri-cli/src/mobile/android/dev.rs +++ b/crates/tauri-cli/src/mobile/android/dev.rs @@ -202,6 +202,7 @@ fn run_command(options: Options, noise_level: NoiseLevel) -> Result<()> { config.app(), config.project_dir(), MobileTarget::Android, + false, )?; run_dev( interface, diff --git a/crates/tauri-cli/src/mobile/ios/build.rs b/crates/tauri-cli/src/mobile/ios/build.rs index 177155dc2168..f0a758e1b299 100644 --- a/crates/tauri-cli/src/mobile/ios/build.rs +++ b/crates/tauri-cli/src/mobile/ios/build.rs @@ -190,6 +190,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { config.app(), config.project_dir(), MobileTarget::Ios, + options.ci, )?; inject_resources(&config, tauri_config.lock().unwrap().as_ref().unwrap())?; diff --git a/crates/tauri-cli/src/mobile/ios/dev.rs b/crates/tauri-cli/src/mobile/ios/dev.rs index 67608a84de35..034d5d0e8ecc 100644 --- a/crates/tauri-cli/src/mobile/ios/dev.rs +++ b/crates/tauri-cli/src/mobile/ios/dev.rs @@ -202,6 +202,7 @@ fn run_command(options: Options, noise_level: NoiseLevel) -> Result<()> { config.app(), config.project_dir(), MobileTarget::Ios, + false, )?; inject_resources(&config, tauri_config.lock().unwrap().as_ref().unwrap())?; diff --git a/crates/tauri-cli/src/mobile/ios/xcode_script.rs b/crates/tauri-cli/src/mobile/ios/xcode_script.rs index 7b226d3c0704..f1bc718809b5 100644 --- a/crates/tauri-cli/src/mobile/ios/xcode_script.rs +++ b/crates/tauri-cli/src/mobile/ios/xcode_script.rs @@ -126,6 +126,7 @@ pub fn command(options: Options) -> Result<()> { config.app(), config.project_dir(), MobileTarget::Ios, + std::env::var("CI").is_ok(), )?; if !cli_options.config.is_empty() { diff --git a/crates/tauri-cli/src/mobile/mod.rs b/crates/tauri-cli/src/mobile/mod.rs index abdd9610779f..61d10c2b72c3 100644 --- a/crates/tauri-cli/src/mobile/mod.rs +++ b/crates/tauri-cli/src/mobile/mod.rs @@ -482,6 +482,7 @@ fn ensure_init( app: &App, project_dir: PathBuf, target: Target, + noninteractive: bool, ) -> Result<()> { if !project_dir.exists() { bail!( @@ -512,27 +513,83 @@ fn ensure_init( } #[cfg(target_os = "macos")] Target::Ios => { - 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 !(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"); + let xcodeproj_path = crate::helpers::fs::find_in_directory(&project_dir, "*.xcodeproj") + .with_context(|| format!("failed to locate xcodeproj in {}", project_dir.display()))?; + + let xcodeproj_name = xcodeproj_path.file_stem().unwrap().to_str().unwrap(); + if xcodeproj_name != app.name() { + let rename_targets = vec![ + // first rename the entitlements + ( + format!("{xcodeproj_name}_iOS/{xcodeproj_name}_iOS.entitlements"), + format!("{xcodeproj_name}_iOS/{}_iOS.entitlements", app.name()), + ), + // then the scheme folder + ( + format!("{xcodeproj_name}_iOS"), + format!("{}_iOS", app.name()), + ), + ( + format!("{xcodeproj_name}.xcodeproj"), + format!("{}.xcodeproj", app.name()), + ), + ]; + let rename_info = rename_targets + .iter() + .map(|(from, to)| format!("- {from} to {to}")) + .collect::>() + .join("\n"); + log::error!( + "you have modified your package name from {current_project_name} to {new_project_name}\nWe need to apply the name change to the Xcode project, renaming:\n{rename_info}", + new_project_name = app.name(), + current_project_name = xcodeproj_name, + ); + if noninteractive { + project_outdated_reasons + .push("you have modified your [lib.name] or [package.name] in the Cargo.toml file"); + } else { + let confirm = crate::helpers::prompts::confirm( + "Do you want to apply the name change to the Xcode project?", + Some(true), + ) + .unwrap_or_default(); + if confirm { + for (from, to) in rename_targets { + std::fs::rename(project_dir.join(&from), project_dir.join(&to)) + .with_context(|| format!("failed to rename {from} to {to}"))?; + } + + // update scheme name in pbxproj + // identifier / product name are synchronized by the dev/build commands + let pbxproj_path = + project_dir.join(format!("{}.xcodeproj/project.pbxproj", app.name())); + let pbxproj_contents = std::fs::read_to_string(&pbxproj_path) + .with_context(|| format!("failed to read {}", pbxproj_path.display()))?; + std::fs::write( + &pbxproj_path, + pbxproj_contents.replace( + &format!("{xcodeproj_name}_iOS"), + &format!("{}_iOS", app.name()), + ), + ) + .with_context(|| format!("failed to write {}", pbxproj_path.display()))?; + } else { + project_outdated_reasons + .push("you have modified your [lib.name] or [package.name] in the Cargo.toml file"); + } + } } + + // note: pbxproj is synchronied by the dev/build commands } } if !project_outdated_reasons.is_empty() { let reason = project_outdated_reasons.join(" and "); bail!( - "{} project directory is outdated because {reason}. Please run `tauri {} init` and try again.", + "{} project directory is outdated because {reason}. Please delete {} and run `tauri {} init` and try again.", target.ide_name(), + project_dir.display(), target.command_name(), ) } From 002e3dae1e164fa89d180b620565a47eb3b6f6be Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 27 Aug 2025 18:17:42 -0300 Subject: [PATCH 2/2] clippy --- crates/tauri-cli/src/helpers/fs.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/tauri-cli/src/helpers/fs.rs b/crates/tauri-cli/src/helpers/fs.rs index e92ee2a855c6..096d91c4ba54 100644 --- a/crates/tauri-cli/src/helpers/fs.rs +++ b/crates/tauri-cli/src/helpers/fs.rs @@ -20,6 +20,10 @@ pub fn copy_file(from: impl AsRef, to: impl AsRef) -> Result<()> { Ok(()) } +/// Find an entry in a directory matching a glob pattern. +/// Currently does not traverse subdirectories. +// currently only used on macOS +#[allow(dead_code)] pub fn find_in_directory(path: &Path, glob_pattern: &str) -> Result { let pattern = glob::Pattern::new(glob_pattern)?; for entry in std::fs::read_dir(path)? {