Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI can select target arch #270

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 69 additions & 4 deletions cli/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
use std::collections::HashMap;
use std::env;

use dialoguer::theme::ColorfulTheme;
use dialoguer::MultiSelect;
use dialoguer::Select;

use crate::print::print_build_success_message;
use crate::style;
use crate::style::blue_bold;
use crate::style::print_green_bold;
use dialoguer::theme::ColorfulTheme;
use dialoguer::MultiSelect;
use dialoguer::Select;
use std::env;

const MODES: [&str; 2] = ["debug", "release"];
const PLATFORMS: [&str; 2] = ["ios", "android"];
// Note that *_ARCH should align with `ios.rs` and `andriod.rs` in "mopro-ffi/src/app_config"
const IOS_ARCHS: [&str; 3] = [
"aarch64-apple-ios",
"aarch64-apple-ios-sim",
"x86_64-apple-ios",
];
const ANDROID_ARCHS: [&str; 4] = [
"x86_64-linux-android",
"i686-linux-android",
"armv7-linux-androideabi",
"aarch64-linux-android",
];

pub fn build_project(
arg_mode: &Option<String>,
Expand Down Expand Up @@ -54,16 +69,40 @@ pub fn build_project(
}
};

let mut selected_architectures: HashMap<String, Vec<String>> = HashMap::new();

for platform in &platforms {
let archs = match platform.as_str() {
"ios" => select_architectures("iOS", &IOS_ARCHS)?,
"android" => select_architectures("Android", &ANDROID_ARCHS)?,
_ => vec![],
};

selected_architectures.insert(platform.clone(), archs);
}

if platforms.is_empty() {
style::print_yellow("No platform selected. Use space to select platform(s).".to_string());
build_project(&Some(mode), &None)?;
} else {
for platform in platforms.clone() {
let arch_key = match platform.as_str() {
"ios" => "IOS_ARCH",
"android" => "ANDROID_ARCH",
_ => unreachable!(),
};

let selected_arch = selected_architectures
.get(&platform)
.map(|archs| archs.join(","))
.unwrap_or_else(|| "".to_string());

let status = std::process::Command::new("cargo")
.arg("run")
.arg("--bin")
.arg(platform.clone())
.env("CONFIGURATION", mode.clone())
.env(arch_key, selected_arch)
.status()?;

if !status.success() {
Expand Down Expand Up @@ -102,6 +141,32 @@ fn select_platforms() -> anyhow::Result<Vec<String>> {
.collect())
}

fn select_architectures(platform: &str, archs: &[&str]) -> anyhow::Result<Vec<String>> {
// At least one architecture must be selected
loop {
let selected_archs = MultiSelect::with_theme(&ColorfulTheme::default())
.with_prompt(format!(
"Select {} architecture(s) to compile (default: all)",
platform
))
.items(archs)
.defaults(&vec![true; archs.len()])
.interact()?;

if selected_archs.is_empty() {
style::print_yellow(format!(
"No architectures selected for {}. Please select at least one architecture.",
platform
));
} else {
return Ok(selected_archs
.iter()
.map(|&idx| archs[idx].to_owned())
.collect());
}
}
}

fn print_binding_message(platforms: Vec<String>) -> anyhow::Result<()> {
let current_dir = env::current_dir()?;
print_green_bold("✨ Bindings Built Successfully! ✨".to_string());
Expand Down
23 changes: 22 additions & 1 deletion cli/src/template/init/src/bin/android.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
use mopro_ffi::app_config::android::ANDROID_ARCHS;

fn main() {
let android_archs: Vec<String> = if let Ok(android_archs) = std::env::var("ANDROID_ARCHS") {
android_archs
.split(',')
.map(|arch| arch.to_string())
.collect()
} else {
// Default case: select all supported architectures if none are provided
ANDROID_ARCHS.iter().map(|arch| arch.to_string()).collect()
};

// Check 'ANDRIOD_ARCH' input validation
for arch in &android_archs {
assert!(
ANDROID_ARCHS.contains(&arch.as_str()),
"Unsupported architecture: {}",
arch
);
}

// A simple wrapper around a build command provided by mopro.
// In the future this will likely be published in the mopro crate itself.
mopro_ffi::app_config::android::build();
mopro_ffi::app_config::android::build(&android_archs);
}
20 changes: 19 additions & 1 deletion cli/src/template/init/src/bin/ios.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
use mopro_ffi::app_config::ios::IOS_ARCHS;

fn main() {
let ios_archs: Vec<String> = if let Ok(ios_archs) = std::env::var("IOS_ARCHS") {
ios_archs.split(',').map(|arch| arch.to_string()).collect()
} else {
// Default case: select all supported architectures if none are provided
IOS_ARCHS.iter().map(|&arch| arch.to_string()).collect()
};

// Check 'IOS_ARCH' input validation
for arch in &ios_archs {
assert!(
IOS_ARCHS.contains(&arch.as_str()),
"Unsupported architecture: {}",
arch
);
}

// A simple wrapper around a build command provided by mopro.
// In the future this will likely be published in the mopro crate itself.
mopro_ffi::app_config::ios::build();
mopro_ffi::app_config::ios::build(&ios_archs);
Comment on lines 3 to +22
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think passing archs as inputs doesn't make much sense to me
and I think letting users maintaining the line 4-18 is too much work
(and we have to maintain double files in template and test-e2e, and also documentation)

if it is in env, it can just let mopro_ffi::app_config::ios::build() to read the env and handle the error assertions?
like the debug/release mode, it is also handled by mopro_ffi::app_config::ios::build()

}
17 changes: 9 additions & 8 deletions mopro-ffi/src/app_config/android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ use super::install_arch;
use super::install_ndk;
use super::mktemp_local;

pub fn build() {
// This variable should be align with `cli/build.rs`
pub const ANDROID_ARCHS: [&str; 4] = [
"x86_64-linux-android",
"i686-linux-android",
"armv7-linux-androideabi",
"aarch64-linux-android",
];

pub fn build(target_archs: &[String]) {
let cwd = std::env::current_dir().expect("Failed to get current directory");
let manifest_dir =
std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| cwd.to_str().unwrap().to_string());
Expand All @@ -19,13 +27,6 @@ pub fn build() {
let bindings_out = work_dir.join("MoproAndroidBindings");
let bindings_dest = Path::new(&manifest_dir).join("MoproAndroidBindings");

let target_archs = vec![
"x86_64-linux-android",
"i686-linux-android",
"armv7-linux-androideabi",
"aarch64-linux-android",
];

let mode = std::env::var("CONFIGURATION")
.unwrap_or_else(|_| "debug".to_string())
.to_lowercase();
Expand Down
54 changes: 46 additions & 8 deletions mopro-ffi/src/app_config/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ use super::cleanup_tmp_local;
use super::install_arch;
use super::mktemp_local;

// This variable should be align with `cli/build.rs`
pub const IOS_ARCHS: [&str; 3] = [
"aarch64-apple-ios",
"aarch64-apple-ios-sim",
"x86_64-apple-ios",
];

// Load environment variables that are specified by by xcode
pub fn build() {
pub fn build(target_archs: &[String]) {
let cwd = std::env::current_dir().unwrap();
let manifest_dir =
std::env::var("CARGO_MANIFEST_DIR").unwrap_or(cwd.to_str().unwrap().to_string());
Expand All @@ -23,11 +30,6 @@ pub fn build() {
let bindings_dest = Path::new(&manifest_dir).join("MoproiOSBindings");
let framework_out = bindings_out.join("MoproBindings.xcframework");

let target_archs = vec![
vec!["aarch64-apple-ios"],
vec!["aarch64-apple-ios-sim", "x86_64-apple-ios"],
];

// https://developer.apple.com/documentation/xcode/build-settings-reference#Architectures
let mode;
if let Ok(configuration) = std::env::var("CONFIGURATION") {
Expand Down Expand Up @@ -72,7 +74,7 @@ pub fn build() {
}
// now lipo the libraries together
let mut lipo_cmd = Command::new("lipo");
let lib_out = mktemp_local(build_dir_path).join("libmopro_bindings.a");
let lib_out = mktemp_local(&build_dir_path).join("libmopro_bindings.a");
lipo_cmd
.arg("-create")
.arg("-output")
Expand Down Expand Up @@ -106,7 +108,7 @@ pub fn build() {
)
.expect("Failed to move mopro.swift into place");

let out_lib_paths: Vec<PathBuf> = target_archs
let out_lib_paths: Vec<PathBuf> = group_target_archs(target_archs)
.iter()
.map(|v| build_combined_archs(v))
.collect();
Expand Down Expand Up @@ -145,6 +147,42 @@ pub fn build() {
cleanup_tmp_local(build_dir_path)
}

// More general cases
fn group_target_archs(target_archs: &[String]) -> Vec<Vec<&str>> {
// Detect the current architecture
let current_arch = std::env::consts::ARCH;

// Determine the device architecture prefix based on the current architecture
let device_prefix = match current_arch {
arch if arch.starts_with("x86_64") => "x86_64",
arch if arch.starts_with("aarch64") => "aarch64",
_ => panic!("Unsupported host architecture: {}", current_arch),
};

let mut device_archs = Vec::new();
let mut simulator_archs = Vec::new();

for arch in target_archs.iter().map(String::as_str) {
if arch.ends_with("sim") {
simulator_archs.push(arch);
} else if arch.starts_with(device_prefix) {
device_archs.push(arch);
} else {
simulator_archs.push(arch);
}
}

let mut grouped_archs = Vec::new();
if !device_archs.is_empty() {
grouped_archs.push(device_archs);
}
if !simulator_archs.is_empty() {
grouped_archs.push(simulator_archs);
}

grouped_archs
}

/// Recursively renames all module maps in the given directory to "module.modulemap".
/// This is necessary because uniffi-bindgen creates module maps with the name "moproFFI.modulemap"
/// by default. We're looking for multiple files as there are separate modules for the
Expand Down
25 changes: 24 additions & 1 deletion test-e2e/src/bin/android.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
use mopro_ffi::app_config::android::ANDROID_ARCHS;

fn main() {
mopro_ffi::app_config::android::build();
let android_archs: Vec<String> = if let Ok(android_archs) = std::env::var("ANDROID_ARCHS") {
android_archs
.split(',')
.map(|arch| arch.to_string())
.collect()
} else {
// Default case: select all supported architectures if none are provided
ANDROID_ARCHS.iter().map(|arch| arch.to_string()).collect()
};

// Check 'ANDRIOD_ARCH' input validation
for arch in &android_archs {
assert!(
ANDROID_ARCHS.contains(&arch.as_str()),
"Unsupported architecture: {}",
arch
);
}

// A simple wrapper around a build command provided by mopro.
// In the future this will likely be published in the mopro crate itself.
mopro_ffi::app_config::android::build(&android_archs);
}
22 changes: 21 additions & 1 deletion test-e2e/src/bin/ios.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
use mopro_ffi::app_config::ios::IOS_ARCHS;

fn main() {
mopro_ffi::app_config::ios::build();
let ios_archs: Vec<String> = if let Ok(ios_archs) = std::env::var("IOS_ARCHS") {
ios_archs.split(',').map(|arch| arch.to_string()).collect()
} else {
// Default case: select all supported architectures if none are provided
IOS_ARCHS.iter().map(|&arch| arch.to_string()).collect()
};

// Check 'IOS_ARCH' input validation
for arch in &ios_archs {
assert!(
IOS_ARCHS.contains(&arch.as_str()),
"Unsupported architecture: {}",
arch
);
}

// A simple wrapper around a build command provided by mopro.
// In the future this will likely be published in the mopro crate itself.
mopro_ffi::app_config::ios::build(&ios_archs);
}
Loading