diff --git a/Cargo.lock b/Cargo.lock index e9bbeb8..b9f340a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1612,17 +1612,18 @@ version = "0.1.1" dependencies = [ "anyhow", "base64", - "cc", + "bindgen", + "chrono", "clap", "escargot", "fn-traits", + "glob", "hashbrown 0.16.1", "hex", "hmac", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-bin", - "libc", "rquickjs", "serde", "serde_json", diff --git a/Justfile b/Justfile index 3529358..ecfac5f 100644 --- a/Justfile +++ b/Justfile @@ -18,7 +18,7 @@ export HYPERLIGHT_CFLAGS := \ export CMAKE_GENERATOR := if os() == "windows" { "Ninja" } else { "" } ensure-tools: - cargo install cargo-hyperlight --locked --version 0.1.3 + cargo install cargo-hyperlight --locked --version 0.1.7 # Check if npm is installed, install automatically if missing (Linux) [private] @@ -39,8 +39,7 @@ check-license-headers: ./dev/check-license-headers.sh clippy target=default-target features="": (ensure-tools) - cd src/hyperlight-js-runtime && \ - cargo hyperlight clippy \ + cargo hyperlight clippy -p hyperlight-js-runtime \ --profile={{ if target == "debug" {"dev"} else { target } }} \ -- -D warnings cargo clippy --all-targets \ diff --git a/src/hyperlight-js-runtime/Cargo.toml b/src/hyperlight-js-runtime/Cargo.toml index e8d1635..674928e 100644 --- a/src/hyperlight-js-runtime/Cargo.toml +++ b/src/hyperlight-js-runtime/Cargo.toml @@ -33,17 +33,18 @@ tracing = { version = "0.1.44", default-features = false, features = ["log","att hyperlight-common = { workspace = true, default-features = false } hyperlight-guest = { workspace = true } hyperlight-guest-bin = { workspace = true } +chrono = { version = "0.4", default-features = false } [target.'cfg(not(hyperlight))'.dependencies] clap = { version = "4.5", features = ["derive"] } -libc = "0.2" [target.'cfg(not(hyperlight))'.dev-dependencies] escargot = "0.5" tempfile = "3.26" [build-dependencies] -cc = "1.2" +bindgen = "0.72" +glob = "0.3" [features] default = [] diff --git a/src/hyperlight-js-runtime/build.rs b/src/hyperlight-js-runtime/build.rs index 34ef786..3334657 100644 --- a/src/hyperlight-js-runtime/build.rs +++ b/src/hyperlight-js-runtime/build.rs @@ -13,16 +13,36 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -fn main() { - if std::env::var_os("CARGO_CFG_HYPERLIGHT").is_none() { - return; - } - let files = ["stubs/clock.c", "stubs/localtime.c"]; +use std::env; +use std::path::PathBuf; - for file in files { - println!("cargo:rerun-if-changed={}", file); - } +use bindgen::RustEdition::Edition2024; - cc::Build::new().files(files).compile("stubs"); +fn main() -> Result<(), Box> { + let mut bindings = bindgen::builder() + .use_core() + .wrap_unsafe_ops(true) + .rust_edition(Edition2024) + .clang_arg("-D_POSIX_MONOTONIC_CLOCK=1") + .clang_arg("-D_POSIX_C_SOURCE=200809L"); + + bindings = bindings.header_contents( + "libc_stubs.h", + " + #pragma once + #include + #include + #include + ", + ); + + println!("cargo:rerun-if-changed=stubs/include/stdio.h"); + println!("cargo:rerun-if-changed=stubs/include/time.h"); + + // Write the generated bindings to an output file. + let out_path = PathBuf::from(env::var("OUT_DIR")?).join("libc_stubs.rs"); + bindings.generate()?.write_to_file(out_path)?; + + Ok(()) } diff --git a/src/hyperlight-js-runtime/src/lib.rs b/src/hyperlight-js-runtime/src/lib.rs index e7e8065..4d89e2e 100644 --- a/src/hyperlight-js-runtime/src/lib.rs +++ b/src/hyperlight-js-runtime/src/lib.rs @@ -20,6 +20,7 @@ extern crate alloc; mod globals; pub mod host; mod host_fn; +mod libc; mod modules; pub(crate) mod utils; diff --git a/src/hyperlight-js-runtime/src/libc.rs b/src/hyperlight-js-runtime/src/libc.rs new file mode 100644 index 0000000..37629fc --- /dev/null +++ b/src/hyperlight-js-runtime/src/libc.rs @@ -0,0 +1,33 @@ +/* +Copyright 2026 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +mod bindings { + #![allow( + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + dead_code, + unnecessary_transmutes, + clippy::upper_case_acronyms, + clippy::ptr_offset_with_cast + )] + include!(concat!(env!("OUT_DIR"), "/libc_stubs.rs")); +} + +#[allow(unused_imports)] // not used in native.rs +pub(crate) use core::ffi::*; + +#[allow(unused_imports)] // not used in native.rs +pub(crate) use bindings::*; diff --git a/src/hyperlight-js-runtime/src/main.rs b/src/hyperlight-js-runtime/src/main.rs index 5774b8c..7985180 100644 --- a/src/hyperlight-js-runtime/src/main.rs +++ b/src/hyperlight-js-runtime/src/main.rs @@ -16,6 +16,8 @@ limitations under the License. #![cfg_attr(hyperlight, no_std)] #![cfg_attr(hyperlight, no_main)] +mod libc; + #[cfg(hyperlight)] include!("main/hyperlight.rs"); diff --git a/src/hyperlight-js-runtime/src/main/hyperlight.rs b/src/hyperlight-js-runtime/src/main/hyperlight.rs index f5f60f1..350ab50 100644 --- a/src/hyperlight-js-runtime/src/main/hyperlight.rs +++ b/src/hyperlight-js-runtime/src/main/hyperlight.rs @@ -15,9 +15,6 @@ limitations under the License. */ extern crate alloc; -use core::ffi::*; -use core::time::Duration; - use alloc::format; use alloc::string::String; use alloc::vec::Vec; @@ -32,6 +29,8 @@ use hyperlight_guest_bin::{guest_function, host_function}; use spin::Mutex; use tracing::instrument; +mod stubs; + struct Host; pub trait CatchGuestErrorExt { @@ -126,11 +125,6 @@ fn register_host_modules(host_modules_json: String) -> Result<()> { Ok(()) } -#[unsafe(no_mangle)] -pub extern "C" fn srand(_seed: u32) { - // No-op -} - #[unsafe(no_mangle)] pub fn guest_dispatch_function(function_call: FunctionCall) -> Result> { let params = function_call.parameters.unwrap_or_default(); @@ -139,42 +133,3 @@ pub fn guest_dispatch_function(function_call: FunctionCall) -> Result> { let result = RUNTIME.lock().run_handler(function_name, event, run_gc)?; Ok(get_flatbuffer_result(result.as_str())) } - -/// # Safety -/// This function is used by the C code to get the current time in seconds and nanoseconds. -/// `ts` must be a valid pointer to an array of two `u64` values. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn _current_time(ts: *mut u64) -> c_int { - #[host_function("CurrentTimeMicros")] - fn current_time_micros() -> Result; - - let dur = current_time_micros().unwrap_or(1609459200u64 * 1_000_000u64); - let dur = Duration::from_micros(dur); - - let ts = unsafe { core::slice::from_raw_parts_mut(ts, 2) }; - ts[0] = dur.as_secs(); - ts[1] = dur.subsec_nanos() as u64; - - 0 -} - -#[unsafe(no_mangle)] -pub extern "C" fn putchar(c: c_int) -> c_int { - unsafe { hyperlight_guest_bin::host_comm::_putchar(c as c_char) }; - if c == '\n' as c_int { - // force a flush of the internal buffer in the hyperlight putchar implementation - unsafe { hyperlight_guest_bin::host_comm::_putchar(0) }; - } - (c as c_char) as c_int -} - -#[unsafe(no_mangle)] -pub extern "C" fn fflush(f: *mut c_void) -> c_int { - if !f.is_null() { - // we only support flushing all streams, and stdout is our only stream - return -1; - } - // flush stdout - putchar('\0' as _); - 0 -} diff --git a/src/hyperlight-js-runtime/src/main/stubs/clock.rs b/src/hyperlight-js-runtime/src/main/stubs/clock.rs new file mode 100644 index 0000000..af54bab --- /dev/null +++ b/src/hyperlight-js-runtime/src/main/stubs/clock.rs @@ -0,0 +1,57 @@ +/* +Copyright 2026 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use hyperlight_guest::error::Result; +use hyperlight_guest_bin::host_function; + +use crate::libc; + +fn micros_since_epoch() -> u64 { + #[host_function("CurrentTimeMicros")] + fn current_time_micros() -> Result; + + current_time_micros().unwrap_or(1609459200u64 * 1_000_000u64) +} + +#[unsafe(no_mangle)] +extern "C" fn clock_gettime(clk_id: libc::clockid_t, ts: *mut libc::timespec) -> libc::c_int { + const CLOCK_REALTIME: libc::clockid_t = libc::CLOCK_REALTIME as libc::clockid_t; + const CLOCK_MONOTONIC: libc::clockid_t = libc::CLOCK_MONOTONIC as libc::clockid_t; + + if clk_id != CLOCK_REALTIME && clk_id != CLOCK_MONOTONIC { + unsafe { libc::__errno_location().write(libc::EINVAL as _) }; + return -1; + } + let micros = micros_since_epoch(); + unsafe { + ts.write(libc::timespec { + tv_sec: (micros / 1_000_000) as _, + tv_nsec: ((micros % 1_000_000) * 1000) as _, + }) + }; + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn gettimeofday(tp: *mut libc::timeval, _tz: *mut libc::c_void) -> libc::c_int { + let micros = micros_since_epoch(); + unsafe { + tp.write(libc::timeval { + tv_sec: (micros / 1_000_000) as _, + tv_usec: (micros % 1_000_000) as _, + }); + } + 0 +} diff --git a/src/hyperlight-js-runtime/src/main/stubs/io.rs b/src/hyperlight-js-runtime/src/main/stubs/io.rs new file mode 100644 index 0000000..7842b93 --- /dev/null +++ b/src/hyperlight-js-runtime/src/main/stubs/io.rs @@ -0,0 +1,38 @@ +/* +Copyright 2026 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use crate::libc; + +#[unsafe(no_mangle)] +extern "C" fn putchar(c: libc::c_int) -> libc::c_int { + unsafe { libc::_putchar(c as libc::c_char) }; + if c == '\n' as libc::c_int { + // force a flush of the internal buffer in the hyperlight putchar implementation + unsafe { libc::_putchar(0) }; + } + (c as libc::c_char) as libc::c_int +} + +#[unsafe(no_mangle)] +extern "C" fn fflush(f: *mut libc::c_void) -> libc::c_int { + if !f.is_null() { + // we only support flushing all streams, and stdout is our only stream + unsafe { libc::__errno_location().write(libc::EINVAL as _) }; + return -1; + } + // flush stdout + unsafe { libc::_putchar(0) }; + 0 +} diff --git a/src/hyperlight-js-runtime/src/main/stubs/localtime.rs b/src/hyperlight-js-runtime/src/main/stubs/localtime.rs new file mode 100644 index 0000000..cce5708 --- /dev/null +++ b/src/hyperlight-js-runtime/src/main/stubs/localtime.rs @@ -0,0 +1,50 @@ +/* +Copyright 2026 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use chrono::{DateTime, TimeDelta, Datelike as _, Timelike as _}; + +use crate::libc; + +#[unsafe(no_mangle)] +extern "C" fn localtime_r(time: *const libc::time_t, result: *mut libc::tm) -> *mut libc::tm { + let offset = unsafe { time.read() }; + + let Some(offset) = TimeDelta::try_seconds(offset) else { + unsafe { libc::__errno_location().write(libc::EOVERFLOW as _) }; + return result; + }; + let Some(time) = DateTime::UNIX_EPOCH.checked_add_signed(offset) else { + unsafe { libc::__errno_location().write(libc::EOVERFLOW as _) }; + return result; + }; + + unsafe { + result.write(libc::tm { + tm_sec: time.second() as _, + tm_min: time.minute() as _, + tm_hour: time.hour() as _, + tm_mday: time.day() as _, + tm_mon: time.month0() as _, + tm_year: (time.year() - 1900) as _, + tm_wday: time.weekday().num_days_from_sunday() as _, + tm_yday: time.ordinal0() as _, + tm_isdst: 0, + __tm_gmtoff: 0, + __tm_zone: c"GMT".as_ptr() as _, + }) + }; + + result +} diff --git a/src/hyperlight-js-runtime/src/main/stubs/mod.rs b/src/hyperlight-js-runtime/src/main/stubs/mod.rs new file mode 100644 index 0000000..bc9a075 --- /dev/null +++ b/src/hyperlight-js-runtime/src/main/stubs/mod.rs @@ -0,0 +1,23 @@ +/* +Copyright 2026 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +//! Some of the libc functions that rquickjs requires are not implemented in +//! the libc provided by the hyperlight runtime, so we provide our own implementations +//! here. We also re-export the generated bindings for the rest of the libc functions. + +mod clock; +mod io; +mod localtime; +mod srand; diff --git a/src/hyperlight-js-runtime/src/main/stubs/srand.rs b/src/hyperlight-js-runtime/src/main/stubs/srand.rs new file mode 100644 index 0000000..b0ca0fa --- /dev/null +++ b/src/hyperlight-js-runtime/src/main/stubs/srand.rs @@ -0,0 +1,21 @@ +/* +Copyright 2026 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use crate::libc; + +#[unsafe(no_mangle)] +extern "C" fn srand(_seed: libc::c_uint) { + // No-op +} diff --git a/src/hyperlight-js-runtime/src/modules/io.rs b/src/hyperlight-js-runtime/src/modules/io.rs index d0d3187..a8210e9 100644 --- a/src/hyperlight-js-runtime/src/modules/io.rs +++ b/src/hyperlight-js-runtime/src/modules/io.rs @@ -15,6 +15,8 @@ limitations under the License. */ use alloc::string::String; +use crate::libc; + #[rquickjs::module(rename_vars = "camelCase", rename_types = "camelCase")] #[allow(clippy::module_inception)] pub mod io { @@ -22,11 +24,8 @@ pub mod io { #[rquickjs::function] pub fn print(txt: String) { - unsafe extern "C" { - safe fn putchar(c: core::ffi::c_int) -> core::ffi::c_int; - } for byte in txt.bytes() { - let _ = putchar(byte as _); + let _ = unsafe { libc::putchar(byte as _) }; } flush() } @@ -34,9 +33,6 @@ pub mod io { #[rquickjs::function] pub fn flush() { // Flush the output buffer of libc to make sure all output is printed out. - unsafe extern "C" { - fn fflush(f: *mut core::ffi::c_void) -> core::ffi::c_int; - } - let _ = unsafe { fflush(core::ptr::null_mut()) }; + let _ = unsafe { libc::fflush(core::ptr::null_mut()) }; } } diff --git a/src/hyperlight-js-runtime/stubs/clock.c b/src/hyperlight-js-runtime/stubs/clock.c deleted file mode 100644 index a81d625..0000000 --- a/src/hyperlight-js-runtime/stubs/clock.c +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2026 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -#define _POSIX_MONOTONIC_CLOCK 1 - -#include -#include -#include -#include -#include - -extern void _current_time(uint64_t *ts); - -int gettimeofday(struct timeval *__restrict tv, void *__restrict __tz) { - (void)__tz; // Unused parameter - uint64_t current_time[2]; - _current_time(current_time); - tv->tv_sec = current_time[0]; - tv->tv_usec = current_time[1] / 1000; - - return 0; -} - -int clock_gettime(clockid_t clk_id, struct timespec *tp) { - uint64_t current_time[2]; - switch (clk_id) { - case CLOCK_REALTIME: - case CLOCK_MONOTONIC: - _current_time(current_time); - tp->tv_sec = current_time[0]; - tp->tv_nsec = current_time[1]; - break; - default: - errno = EINVAL; - return -1; - } - - return 0; -} diff --git a/src/hyperlight-js-runtime/stubs/include/immintrin.h b/src/hyperlight-js-runtime/stubs/include/immintrin.h deleted file mode 100644 index e69de29..0000000 diff --git a/src/hyperlight-js-runtime/stubs/include/stdio.h b/src/hyperlight-js-runtime/stubs/include/stdio.h index 3e8366d..edeffed 100644 --- a/src/hyperlight-js-runtime/stubs/include/stdio.h +++ b/src/hyperlight-js-runtime/stubs/include/stdio.h @@ -5,7 +5,8 @@ #define stdout NULL -#define putchar(c) _putchar((char)(c)) +int putchar(int c); + #define vfprintf(f, ...) vprintf(__VA_ARGS__) #define fprintf(f, ...) printf(__VA_ARGS__) #define fputc(c, f) putc((char)(c), f) diff --git a/src/hyperlight-js-runtime/stubs/include/time.h b/src/hyperlight-js-runtime/stubs/include/time.h index 0014cef..7d06f6d 100644 --- a/src/hyperlight-js-runtime/stubs/include/time.h +++ b/src/hyperlight-js-runtime/stubs/include/time.h @@ -2,6 +2,10 @@ #include_next "time.h" +#include +#include +#include + #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 1 diff --git a/src/hyperlight-js-runtime/stubs/localtime.c b/src/hyperlight-js-runtime/stubs/localtime.c deleted file mode 100644 index da634d4..0000000 --- a/src/hyperlight-js-runtime/stubs/localtime.c +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include - -/* 2000-03-01 (mod 400 year, immediately after feb29 */ -#define LEAPOCH (946684800LL + 86400*(31+29)) - -#define DAYS_PER_400Y (365*400 + 97) -#define DAYS_PER_100Y (365*100 + 24) -#define DAYS_PER_4Y (365*4 + 1) - -static inline int __secs_to_tm(long long t, struct tm *tm) { - long long days, secs, years; - int remdays, remsecs, remyears; - int qc_cycles, c_cycles, q_cycles; - int months; - int wday, yday, leap; - static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; - - /* Reject time_t values whose year would overflow int */ - if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) - return -1; - - secs = t - LEAPOCH; - days = secs / 86400; - remsecs = secs % 86400; - if (remsecs < 0) { - remsecs += 86400; - days--; - } - - wday = (3+days)%7; - if (wday < 0) wday += 7; - - qc_cycles = days / DAYS_PER_400Y; - remdays = days % DAYS_PER_400Y; - if (remdays < 0) { - remdays += DAYS_PER_400Y; - qc_cycles--; - } - - c_cycles = remdays / DAYS_PER_100Y; - if (c_cycles == 4) c_cycles--; - remdays -= c_cycles * DAYS_PER_100Y; - - q_cycles = remdays / DAYS_PER_4Y; - if (q_cycles == 25) q_cycles--; - remdays -= q_cycles * DAYS_PER_4Y; - - remyears = remdays / 365; - if (remyears == 4) remyears--; - remdays -= remyears * 365; - - leap = !remyears && (q_cycles || !c_cycles); - yday = remdays + 31 + 28 + leap; - if (yday >= 365+leap) yday -= 365+leap; - - years = remyears + 4*q_cycles + 100*c_cycles + 400LL*qc_cycles; - - for (months=0; days_in_month[months] <= remdays; months++) - remdays -= days_in_month[months]; - - if (months >= 10) { - months -= 12; - years++; - } - - if (years+100 > INT_MAX || years+100 < INT_MIN) - return -1; - - tm->tm_year = years + 100; - tm->tm_mon = months + 2; - tm->tm_mday = remdays + 1; - tm->tm_wday = wday; - tm->tm_yday = yday; - - tm->tm_hour = remsecs / 3600; - tm->tm_min = remsecs / 60 % 60; - tm->tm_sec = remsecs % 60; - - return 0; -} - -struct tm *localtime_r(const time_t *timer, struct tm *tm) { - tm->tm_isdst = 0; - tm->tm_gmtoff = 0; - tm->tm_zone = "GMT"; - if (__secs_to_tm((long long)*timer, tm) < 0) { - errno = EOVERFLOW; - return 0; - } - return tm; -} diff --git a/src/hyperlight-js/build.rs b/src/hyperlight-js/build.rs index e8d5961..9c00f9f 100644 --- a/src/hyperlight-js/build.rs +++ b/src/hyperlight-js/build.rs @@ -150,6 +150,11 @@ fn build_js_runtime() -> PathBuf { let stubs_inc = runtime_dir.join("stubs").join("include"); let cflags = format!("-I{} -D__wasi__=1", stubs_inc.display()); + // in windows escape the backslash to make bindgen happy + // TODO(jprendes): this should probably go in cargo-hyperlight instead, where + // we already do something similar, but looks like its not enough. + let cflags = cflags.replace("\\", "\\\\"); + let mut cargo_cmd = cargo_hyperlight::cargo().unwrap(); let cmd = cargo_cmd .arg("build")