From f71cea6afa2df31d3134e0ece22ca5d2b552abbd Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:52:56 +0900 Subject: [PATCH] fix: windows compatibility for C# (#4980) * fix: windows env vars for c# * fix bin name and windows flags * Fix env vars and remove symlinks for windows * More env vars * Format error * Default dotnet path on windows and unix * fix Unused var * fix unused --- .../windmill-worker/src/csharp_executor.rs | 141 ++++++++++++++---- backend/windmill-worker/src/worker.rs | 8 +- 2 files changed, 121 insertions(+), 28 deletions(-) diff --git a/backend/windmill-worker/src/csharp_executor.rs b/backend/windmill-worker/src/csharp_executor.rs index 2195117091..7d36e1fef0 100644 --- a/backend/windmill-worker/src/csharp_executor.rs +++ b/backend/windmill-worker/src/csharp_executor.rs @@ -46,10 +46,16 @@ use crate::SYSTEM_ROOT; const NSJAIL_CONFIG_RUN_CSHARP_CONTENT: &str = include_str!("../nsjail/run.csharp.config.proto"); #[cfg(feature = "csharp")] -lazy_static::lazy_static! { - static ref HOME_DIR: String = std::env::var("HOME").expect("Could not find the HOME environment variable"); - static ref DOTNET_ROOT: String = std::env::var("DOTNET_ROOT").expect("Could not find the DOTNET_ROOT environment variable"); +#[cfg(windows)] +const DOTNET_ROOT_DEFAULT: &str = "C:\\Program Files\\dotnet"; +#[cfg(feature = "csharp")] +#[cfg(unix)] +const DOTNET_ROOT_DEFAULT: &str = "/usr/share/dotnet"; + +#[cfg(feature = "csharp")] +lazy_static::lazy_static! { + static ref DOTNET_ROOT: String = std::env::var("DOTNET_ROOT").unwrap_or_else(|_| DOTNET_ROOT_DEFAULT.to_string()); } #[cfg(feature = "csharp")] @@ -83,6 +89,30 @@ pub async fn generate_nuget_lockfile( .args(vec!["restore", "--use-lock-file"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); + #[cfg(windows)] + gen_lockfile_cmd + .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| "C:\\tmp".to_string()), + ) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "APPDATA", + std::env::var("APPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Roaming", HOME_ENV.as_str())), + ) + .env( + "ProgramFiles", + std::env::var("ProgramFiles").unwrap_or_else(|_| String::from("C:\\Program Files")), + ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ); + let gen_lockfile_process = start_child_process(gen_lockfile_cmd, DOTNET_PATH.as_str()).await?; handle_child( job_id, @@ -147,6 +177,17 @@ fn gen_cs_proj( }) .join("\n"); + let item_group = if pkgs.is_empty() { + "".to_string() + } else { + format!( + r#" +{pkgs} + +"# + ) + }; + write_file( job_dir, "Main.csproj", @@ -159,10 +200,7 @@ fn gen_cs_proj( WindmillScriptCSharpInternal.Wrapper true - -{pkgs} - - +{item_group} "# ), @@ -297,13 +335,27 @@ async fn build_cs_proj( .stderr(Stdio::piped()); #[cfg(windows)] - { - build_cs_cmd.env("SystemRoot", SYSTEM_ROOT.as_str()); - build_cs_cmd.env( + build_cs_cmd + .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| "C:\\tmp".to_string()), + ) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "APPDATA", + std::env::var("APPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Roaming", HOME_ENV.as_str())), + ) + .env( + "ProgramFiles", + std::env::var("ProgramFiles").unwrap_or_else(|_| String::from("C:\\Program Files")), + ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), ); - } let build_cs_process = start_child_process(build_cs_cmd, DOTNET_PATH.as_str()).await?; handle_child( @@ -322,7 +374,6 @@ async fn build_cs_proj( ) .await?; append_logs(job_id, w_id, "\n\n", db).await; - if let Err(e) = std::fs::remove_file(Path::new(job_dir).join("nuget.config")) { if e.kind() != io::ErrorKind::NotFound { Err(anyhow!("Error erasing nuget.config: {}", e))?; @@ -330,11 +381,15 @@ async fn build_cs_proj( } let bin_path = format!("{}/{hash}", CSHARP_CACHE_DIR); + #[cfg(unix)] + let target = format!("{job_dir}/Main"); + #[cfg(windows)] + let target = format!("{job_dir}/Main.exe"); match save_cache( &bin_path, &format!("{CSHARP_OBJECT_STORE_PREFIX}{hash}"), - &format!("{job_dir}/Main"), + &target, ) .await { @@ -408,18 +463,16 @@ pub async fn handle_csharp_job( let (cache, cache_logs) = windmill_common::worker::load_cache(&bin_path, &remote_path).await; let cache_logs = if cache { - let target = format!("{job_dir}/Main"); - #[cfg(unix)] - let symlink = std::os::unix::fs::symlink(&bin_path, &target); - #[cfg(windows)] - let symlink = std::os::windows::fs::symlink_dir(&bin_path, &target); - - symlink.map_err(|e| { - Error::ExecutionErr(format!( - "could not copy cached binary from {bin_path} to {job_dir}/main: {e:?}" - )) - })?; + { + let target = format!("{job_dir}/Main"); + let symlink = std::os::unix::fs::symlink(&bin_path, &target); + symlink.map_err(|e| { + Error::ExecutionErr(format!( + "could not copy cached binary from {bin_path} to {job_dir}/Main: {e:?}" + )) + })?; + } cache_logs } else { @@ -497,10 +550,21 @@ pub async fn handle_csharp_job( .args(vec!["--config", "run.config.proto", "--", "/tmp/main"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); + + #[cfg(windows)] + nsjail_cmd.env("SystemRoot", SYSTEM_ROOT.as_str()); + start_child_process(nsjail_cmd, NSJAIL_PATH.as_str()).await? } else { - let compiled_executable_name = "./Main"; - let mut run_csharp = Command::new(compiled_executable_name); + #[cfg(unix)] + let compiled_executable_name = "./Main".to_string(); + #[cfg(windows)] + let compiled_executable_name = if cache { + bin_path.to_string() + } else { + format!("{job_dir}/Main.exe") + }; + let mut run_csharp = Command::new(&compiled_executable_name); run_csharp .current_dir(job_dir) .env_clear() @@ -515,8 +579,31 @@ pub async fn handle_csharp_job( .env("HOME", HOME_ENV.as_str()) .stdout(Stdio::piped()) .stderr(Stdio::piped()); + #[cfg(windows)] + run_csharp + .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| "C:\\tmp".to_string()), + ) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "APPDATA", + std::env::var("APPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Roaming", HOME_ENV.as_str())), + ) + .env( + "ProgramFiles", + std::env::var("ProgramFiles").unwrap_or_else(|_| String::from("C:\\Program Files")), + ) + .env( + "LOCALAPPDATA", + std::env::var("LOCALAPPDATA") + .unwrap_or_else(|_| format!("{}\\AppData\\Local", HOME_ENV.as_str())), + ); - start_child_process(run_csharp, compiled_executable_name).await? + start_child_process(run_csharp, &compiled_executable_name).await? }; handle_child( diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index aa99743e26..88e556a2b7 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -331,6 +331,12 @@ lazy_static::lazy_static! { pub static ref WORKER_EXECUTION_DURATION: Arc>> = Arc::new(RwLock::new(HashMap::new())); } +#[cfg(windows)] +const DOTNET_DEFAULT_PATH: &str = "C:\\Program Files\\dotnet\\dotnet.exe"; +#[cfg(unix)] +const DOTNET_DEFAULT_PATH: &str = "/usr/bin/dotnet"; + + lazy_static::lazy_static! { pub static ref JOB_TOKEN: Option = std::env::var("JOB_TOKEN").ok(); @@ -385,7 +391,7 @@ lazy_static::lazy_static! { pub static ref POWERSHELL_PATH: String = std::env::var("POWERSHELL_PATH").unwrap_or_else(|_| "/usr/bin/pwsh".to_string()); pub static ref PHP_PATH: String = std::env::var("PHP_PATH").unwrap_or_else(|_| "/usr/bin/php".to_string()); pub static ref COMPOSER_PATH: String = std::env::var("COMPOSER_PATH").unwrap_or_else(|_| "/usr/bin/composer".to_string()); - pub static ref DOTNET_PATH: String = std::env::var("DOTNET_PATH").unwrap_or_else(|_| "/usr/bin/dotnet".to_string()); + pub static ref DOTNET_PATH: String = std::env::var("DOTNET_PATH").unwrap_or_else(|_| DOTNET_DEFAULT_PATH.to_string()); pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string()); pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new()); pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());