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 776441f5a7f8..dbd5fabe14fe 100644 --- a/crates/tauri-cli/src/helpers/fs.rs +++ b/crates/tauri-cli/src/helpers/fs.rs @@ -2,8 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{error::ErrorExt, Error}; -use std::path::Path; +use crate::{ + error::{Context, ErrorExt}, + Error, +}; +use std::path::{Path, PathBuf}; pub fn copy_file(from: impl AsRef, to: impl AsRef) -> crate::Result<()> { let from = from.as_ref(); @@ -28,3 +31,25 @@ pub fn copy_file(from: impl AsRef, to: impl AsRef) -> crate::Result< std::fs::copy(from, to).fs_context("failed to copy file", from.to_path_buf())?; 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) -> crate::Result { + let pattern = glob::Pattern::new(glob_pattern) + .with_context(|| format!("failed to parse glob pattern {glob_pattern}"))?; + for entry in std::fs::read_dir(path) + .with_context(|| format!("failed to read directory {}", path.display()))? + { + let entry = entry.context("failed to read directory entry")?; + if pattern.matches_path(&entry.path()) { + return Ok(entry.path()); + } + } + crate::error::bail!( + "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 c5d447d7ad5e..c3dfdff4b95e 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 e5abf6ef15c3..47fd33a5612a 100644 --- a/crates/tauri-cli/src/mobile/android/build.rs +++ b/crates/tauri-cli/src/mobile/android/build.rs @@ -161,6 +161,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { config.app(), config.project_dir(), MobileTarget::Android, + options.ci, )?; let mut env = env(options.ci)?; diff --git a/crates/tauri-cli/src/mobile/android/dev.rs b/crates/tauri-cli/src/mobile/android/dev.rs index e5a3b2124d34..5c5e7daa7a32 100644 --- a/crates/tauri-cli/src/mobile/android/dev.rs +++ b/crates/tauri-cli/src/mobile/android/dev.rs @@ -205,6 +205,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 712e7d0a827e..20ed2d57bfeb 100644 --- a/crates/tauri-cli/src/mobile/ios/build.rs +++ b/crates/tauri-cli/src/mobile/ios/build.rs @@ -202,6 +202,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 406a72a0b211..9f76a91caca7 100644 --- a/crates/tauri-cli/src/mobile/ios/dev.rs +++ b/crates/tauri-cli/src/mobile/ios/dev.rs @@ -210,6 +210,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 c35e55b3dff4..34d697614b77 100644 --- a/crates/tauri-cli/src/mobile/ios/xcode_script.rs +++ b/crates/tauri-cli/src/mobile/ios/xcode_script.rs @@ -138,6 +138,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 a764ae360f79..2a77053782ff 100644 --- a/crates/tauri-cli/src/mobile/mod.rs +++ b/crates/tauri-cli/src/mobile/mod.rs @@ -499,6 +499,7 @@ fn ensure_init( app: &App, project_dir: PathBuf, target: Target, + noninteractive: bool, ) -> Result<()> { if !project_dir.exists() { crate::error::bail!( @@ -529,32 +530,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"), - ) - .fs_context( - "missing project.pbxproj file in the Xcode project directory", - project_dir - .join(format!("{}.xcodeproj", app.name())) - .join("project.pbxproj"), - )?; - - 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 "); crate::error::bail!( - "{} project directory is outdated because {reason}. Please run `tauri {} init` and try again.", + "{} project directory is outdated because {reason}. Please delete {}, run `tauri {} init` and try again.", target.ide_name(), + project_dir.display(), target.command_name(), ) }