Skip to content

Commit 2e0d293

Browse files
committed
fix: make msb_krun conditional for cross-platform worker builds
msb_krun (libkrun VMM) only compiles on Linux GNU targets due to kvm_bindings and libc::statx dependencies. This caused build failures on macOS, musl, and aarch64 cross-compilation. Changes: - Make msb_krun a target-conditional dependency (linux + gnu only) - Gate vm_boot module, libkrun adapter, and VmBoot command behind cfg - Add UnsupportedAdapter stub for non-Linux platforms - Install cross-arch system deps (libcap-ng-dev:arm64) for aarch64 - Add pkg-config cross-compilation env vars - Restore all 5 worker binary targets in release workflow
1 parent 9ec81ac commit 2e0d293

11 files changed

Lines changed: 216 additions & 143 deletions

File tree

.github/workflows/release-iii.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ jobs:
304304
dry_run: ${{ needs.setup.outputs.dry_run == 'true' }}
305305
features: embed-init,embed-libkrunfw
306306
init_artifacts: true
307-
targets: '["x86_64-unknown-linux-gnu","aarch64-unknown-linux-gnu"]'
307+
targets: '["x86_64-apple-darwin","aarch64-apple-darwin","x86_64-unknown-linux-gnu","x86_64-unknown-linux-musl","aarch64-unknown-linux-gnu"]'
308308
system_deps: libcap-ng-dev
309309
slack_thread_ts: ${{ needs.setup.outputs.slack_ts }}
310310
slack_label: Worker Binary

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/iii-worker/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ embed-init = ["iii-filesystem/embed-init"]
2121
[dependencies]
2222
iii-filesystem = { path = "../iii-filesystem" }
2323
iii-network = { path = "../iii-network" }
24-
msb_krun = { version = "0.1.9", features = ["net"] }
2524
oci-client = "0.16"
2625
oci-spec = "0.9"
2726
tokio = { version = "1", features = ["process", "macros", "rt-multi-thread", "fs", "signal", "time", "io-std"] }
@@ -45,6 +44,9 @@ tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
4544
futures = "0.3"
4645
indicatif = "0.17"
4746

47+
[target.'cfg(all(target_os = "linux", not(target_env = "musl")))'.dependencies]
48+
msb_krun = { version = "0.1.9", features = ["net"] }
49+
4850
[dev-dependencies]
4951
tokio = { version = "1", features = ["test-util"] }
5052
tempfile = "3"

crates/iii-worker/src/cli/app.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ pub enum Commands {
136136
},
137137

138138
/// Internal: boot a libkrun VM (crash-isolated subprocess)
139+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
139140
#[command(name = "__vm-boot", hide = true)]
140141
VmBoot(super::vm_boot::VmBootArgs),
141142
}

crates/iii-worker/src/cli/dev.rs

Lines changed: 135 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use colored::Colorize;
1010
use std::collections::HashMap;
1111

1212
use super::project::{ProjectInfo, WORKER_MANIFEST, load_project_info};
13+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
1314
use super::rootfs::clone_rootfs;
1415

1516
async fn detect_lan_ip() -> Option<String> {
@@ -83,6 +84,7 @@ pub async fn handle_worker_dev(
8384
}
8485
};
8586

87+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
8688
if let Err(e) = super::firmware::download::ensure_libkrunfw().await {
8789
tracing::warn!(error = %e, "failed to ensure libkrunfw");
8890
}
@@ -92,7 +94,8 @@ pub async fn handle_worker_dev(
9294
None => {
9395
eprintln!(
9496
"{} No dev runtime available.\n \
95-
Rebuild with --features embed-libkrunfw or place libkrunfw in ~/.iii/lib/",
97+
On Linux: rebuild with --features embed-libkrunfw or place libkrunfw in ~/.iii/lib/\n \
98+
On macOS/musl: VM sandbox is not yet supported on this platform.",
9699
"error:".red()
97100
);
98101
return 1;
@@ -168,143 +171,150 @@ async fn detect_dev_runtime(explicit: Option<&str>) -> Option<String> {
168171
return Some(rt.to_string());
169172
}
170173

171-
{
172-
if super::worker_manager::libkrun::libkrun_available() {
173-
return Some("libkrun".to_string());
174-
}
174+
if super::worker_manager::sandbox_available() {
175+
return Some("libkrun".to_string());
175176
}
176177

177178
None
178179
}
179180

180181
async fn run_dev_worker(
181182
runtime: &str,
183+
_sb_name: &str,
184+
_project_str: &str,
185+
_project: &ProjectInfo,
186+
_engine_url: &str,
187+
_rebuild: bool,
188+
) -> i32 {
189+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
190+
if runtime == "libkrun" {
191+
return run_dev_worker_libkrun(_sb_name, _project_str, _project, _engine_url, _rebuild).await;
192+
}
193+
194+
eprintln!("{} Unknown or unsupported runtime: {}", "error:".red(), runtime);
195+
1
196+
}
197+
198+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
199+
async fn run_dev_worker_libkrun(
182200
sb_name: &str,
183201
project_str: &str,
184202
project: &ProjectInfo,
185203
engine_url: &str,
186204
rebuild: bool,
187205
) -> i32 {
188-
match runtime {
189-
"libkrun" => {
190-
let language = project.language.as_deref().unwrap_or("typescript");
191-
let mut env = build_dev_env(engine_url, &project.env);
192-
193-
let base_rootfs = match super::worker_manager::oci::prepare_rootfs(language).await {
194-
Ok(p) => p,
195-
Err(e) => {
196-
eprintln!("{} {}", "error:".red(), e);
197-
return 1;
198-
}
199-
};
200-
201-
let oci_env = super::worker_manager::oci::read_oci_env(&base_rootfs);
202-
for (key, value) in oci_env {
203-
env.entry(key).or_insert(value);
204-
}
205-
206-
let dev_dir = match dirs::home_dir() {
207-
Some(h) => h.join(".iii").join("dev").join(sb_name),
208-
None => {
209-
eprintln!("{} Cannot determine home directory", "error:".red());
210-
return 1;
211-
}
212-
};
213-
let prepared_marker = dev_dir.join("var").join(".iii-prepared");
214-
215-
if rebuild && dev_dir.exists() {
216-
eprintln!(" Rebuilding: clearing cached sandbox...");
217-
let _ = std::fs::remove_dir_all(&dev_dir);
218-
}
219-
220-
if !dev_dir.exists() {
221-
eprintln!(" Preparing sandbox...");
222-
if let Err(e) = clone_rootfs(&base_rootfs, &dev_dir) {
223-
eprintln!("{} Failed to create project rootfs: {}", "error:".red(), e);
224-
return 1;
225-
}
226-
}
227-
228-
let is_prepared = prepared_marker.exists();
229-
if is_prepared {
230-
eprintln!(
231-
" {} Using cached deps {}",
232-
"✓".green(),
233-
"(use --rebuild to reinstall)".dimmed()
234-
);
235-
}
236-
237-
let script = build_libkrun_dev_script(project, is_prepared);
238-
239-
let script_path = dev_dir.join("tmp").join("iii-dev-run.sh");
240-
if let Err(e) = std::fs::write(&script_path, &script) {
241-
eprintln!("{} Failed to write dev script: {}", "error:".red(), e);
242-
return 1;
243-
}
244-
#[cfg(unix)]
245-
{
246-
use std::os::unix::fs::PermissionsExt;
247-
let _ =
248-
std::fs::set_permissions(&script_path, std::fs::Permissions::from_mode(0o755));
249-
}
250-
251-
let workspace = dev_dir.join("workspace");
252-
std::fs::create_dir_all(&workspace).ok();
253-
if let Err(e) = copy_dir_contents(std::path::Path::new(project_str), &workspace) {
254-
eprintln!("{} Failed to copy project to rootfs: {}", "error:".red(), e);
255-
return 1;
256-
}
257-
258-
let init_path = match super::firmware::download::ensure_init_binary().await {
259-
Ok(p) => p,
260-
Err(e) => {
261-
eprintln!("{} Failed to provision iii-init: {}", "error:".red(), e);
262-
return 1;
263-
}
264-
};
265-
266-
if !iii_filesystem::init::has_init() {
267-
let dest = dev_dir.join("init.krun");
268-
if let Err(e) = std::fs::copy(&init_path, &dest) {
269-
eprintln!(
270-
"{} Failed to copy iii-init to rootfs: {}",
271-
"error:".red(),
272-
e
273-
);
274-
return 1;
275-
}
276-
#[cfg(unix)]
277-
{
278-
use std::os::unix::fs::PermissionsExt;
279-
let _ = std::fs::set_permissions(&dest, std::fs::Permissions::from_mode(0o755));
280-
}
281-
}
282-
283-
let exec_path = "/bin/sh";
284-
let args = vec![
285-
"-c".to_string(),
286-
"cd /workspace && exec bash /tmp/iii-dev-run.sh".to_string(),
287-
];
288-
let manifest_path = std::path::Path::new(project_str).join(WORKER_MANIFEST);
289-
let (vcpus, ram) = parse_manifest_resources(&manifest_path);
290-
291-
super::worker_manager::libkrun::run_dev(
292-
language,
293-
project_str,
294-
exec_path,
295-
&args,
296-
env,
297-
vcpus,
298-
ram,
299-
dev_dir,
300-
)
301-
.await
206+
let language = project.language.as_deref().unwrap_or("typescript");
207+
let mut env = build_dev_env(engine_url, &project.env);
208+
209+
let base_rootfs = match super::worker_manager::oci::prepare_rootfs(language).await {
210+
Ok(p) => p,
211+
Err(e) => {
212+
eprintln!("{} {}", "error:".red(), e);
213+
return 1;
214+
}
215+
};
216+
217+
let oci_env = super::worker_manager::oci::read_oci_env(&base_rootfs);
218+
for (key, value) in oci_env {
219+
env.entry(key).or_insert(value);
220+
}
221+
222+
let dev_dir = match dirs::home_dir() {
223+
Some(h) => h.join(".iii").join("dev").join(sb_name),
224+
None => {
225+
eprintln!("{} Cannot determine home directory", "error:".red());
226+
return 1;
227+
}
228+
};
229+
let prepared_marker = dev_dir.join("var").join(".iii-prepared");
230+
231+
if rebuild && dev_dir.exists() {
232+
eprintln!(" Rebuilding: clearing cached sandbox...");
233+
let _ = std::fs::remove_dir_all(&dev_dir);
234+
}
235+
236+
if !dev_dir.exists() {
237+
eprintln!(" Preparing sandbox...");
238+
if let Err(e) = clone_rootfs(&base_rootfs, &dev_dir) {
239+
eprintln!("{} Failed to create project rootfs: {}", "error:".red(), e);
240+
return 1;
241+
}
242+
}
243+
244+
let is_prepared = prepared_marker.exists();
245+
if is_prepared {
246+
eprintln!(
247+
" {} Using cached deps {}",
248+
"✓".green(),
249+
"(use --rebuild to reinstall)".dimmed()
250+
);
251+
}
252+
253+
let script = build_libkrun_dev_script(project, is_prepared);
254+
255+
let script_path = dev_dir.join("tmp").join("iii-dev-run.sh");
256+
if let Err(e) = std::fs::write(&script_path, &script) {
257+
eprintln!("{} Failed to write dev script: {}", "error:".red(), e);
258+
return 1;
259+
}
260+
#[cfg(unix)]
261+
{
262+
use std::os::unix::fs::PermissionsExt;
263+
let _ =
264+
std::fs::set_permissions(&script_path, std::fs::Permissions::from_mode(0o755));
265+
}
266+
267+
let workspace = dev_dir.join("workspace");
268+
std::fs::create_dir_all(&workspace).ok();
269+
if let Err(e) = copy_dir_contents(std::path::Path::new(project_str), &workspace) {
270+
eprintln!("{} Failed to copy project to rootfs: {}", "error:".red(), e);
271+
return 1;
272+
}
273+
274+
let init_path = match super::firmware::download::ensure_init_binary().await {
275+
Ok(p) => p,
276+
Err(e) => {
277+
eprintln!("{} Failed to provision iii-init: {}", "error:".red(), e);
278+
return 1;
302279
}
303-
_ => {
304-
eprintln!("{} Unknown runtime: {}", "error:".red(), runtime);
305-
1
280+
};
281+
282+
if !iii_filesystem::init::has_init() {
283+
let dest = dev_dir.join("init.krun");
284+
if let Err(e) = std::fs::copy(&init_path, &dest) {
285+
eprintln!(
286+
"{} Failed to copy iii-init to rootfs: {}",
287+
"error:".red(),
288+
e
289+
);
290+
return 1;
291+
}
292+
#[cfg(unix)]
293+
{
294+
use std::os::unix::fs::PermissionsExt;
295+
let _ = std::fs::set_permissions(&dest, std::fs::Permissions::from_mode(0o755));
306296
}
307297
}
298+
299+
let exec_path = "/bin/sh";
300+
let args = vec![
301+
"-c".to_string(),
302+
"cd /workspace && exec bash /tmp/iii-dev-run.sh".to_string(),
303+
];
304+
let manifest_path = std::path::Path::new(project_str).join(WORKER_MANIFEST);
305+
let (vcpus, ram) = parse_manifest_resources(&manifest_path);
306+
307+
super::worker_manager::libkrun::run_dev(
308+
language,
309+
project_str,
310+
exec_path,
311+
&args,
312+
env,
313+
vcpus,
314+
ram,
315+
dev_dir,
316+
)
317+
.await
308318
}
309319

310320
pub fn parse_manifest_resources(manifest_path: &std::path::Path) -> (u32, u32) {
@@ -360,6 +370,7 @@ pub fn copy_dir_contents(src: &std::path::Path, dst: &std::path::Path) -> Result
360370
Ok(())
361371
}
362372

373+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
363374
pub fn build_libkrun_dev_script(project: &ProjectInfo, prepared: bool) -> String {
364375
let env_exports = build_env_exports(&project.env);
365376
let mut parts: Vec<String> = Vec::new();
@@ -466,6 +477,7 @@ mod tests {
466477
assert_eq!(url, "ws://localhost:49134");
467478
}
468479

480+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
469481
#[test]
470482
fn build_libkrun_dev_script_first_run() {
471483
let project = ProjectInfo {
@@ -483,6 +495,7 @@ mod tests {
483495
assert!(script.contains(".iii-prepared"));
484496
}
485497

498+
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
486499
#[test]
487500
fn build_libkrun_dev_script_prepared() {
488501
let project = ProjectInfo {

0 commit comments

Comments
 (0)