diff --git a/CHANGELOG.md b/CHANGELOG.md index 400384c..4f22185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ the top. Non pre-release versions sometimes have an associated name. fetching the file with `kerblam data fetch`, but will still treat it as remote for all other purposes (e.g. for `kerblam data clean`). This allows the user to use non-canonical fetching methods. +- Experimental: Kerblam! is now compatible with Windows. + In Windows systems, shell scripts are executed with PowerShell instead of Bash. ### Changed - The way that profiles are handled was changed. diff --git a/Cargo.toml b/Cargo.toml index 66c5e56..e511e8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,9 +77,9 @@ cargo-dist-version = "0.16.0" # CI backends to support ci = "github" # The installers to generate for each app -installers = ["shell"] +installers = ["shell", "powershell"] # Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"] +targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] # Publish jobs to run in CI pr-run-mode = "plan" # Whether to install an updater program diff --git a/README.md b/README.md index 784cdbd..17bdadb 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ It provides an overview of what Kerblam! is and does with an hands-on project. > binaries in the releases tab, the command below or compile directly from git with > `cargo install --git https://github.com/MrHedmad/kerblam`. -In short, use a unix-compatible OS and either: +In short, use: ```bash # Install a prebuilt binary curl --proto '=https' --tlsv1.2 -LsSf https://github.com/MrHedmad/kerblam/releases/latest/download/kerblam-installer.sh | sh diff --git a/docs/src/manual/run.md b/docs/src/manual/run.md index 60ac114..681e755 100644 --- a/docs/src/manual/run.md +++ b/docs/src/manual/run.md @@ -8,7 +8,10 @@ It automatically uses the proper execution strategy based on what extension the file is saved as: either `make` or `bash`. > [!IMPORTANT] -> Shell scripts are always executed in `bash`. +> Shell scripts are always executed in `bash` for Linux/Unix, and as Powershell +> scripts for Windows. +> +> Note that the extension for shell files is *always* `.sh`, even on windows. You can use any workflow manager that is installed on your system through Kerblam! (e.g. `snakemake` or `nextflow`) by writing thin shell wrappers diff --git a/src/commands/data/describe.rs b/src/commands/data/describe.rs index cc66c4e..f4d472f 100644 --- a/src/commands/data/describe.rs +++ b/src/commands/data/describe.rs @@ -1,11 +1,16 @@ use core::iter::Sum; use std::fmt::Display; use std::fs; -use std::os::unix::fs::MetadataExt; use std::path::PathBuf; use crate::options::KerblamTomlOptions; +#[cfg(target_family = "windows")] +use std::os::windows::fs::MetadataExt; + +#[cfg(target_family = "unix")] +use std::os::unix::fs::MetadataExt; + #[derive(Debug, Clone)] /// Wrapper to interpret a usize as a File size. pub struct FileSize { @@ -19,6 +24,14 @@ impl TryFrom for FileSize { fn try_from(value: PathBuf) -> std::result::Result { let meta = fs::metadata(value)?; + #[cfg(target_family = "windows")] + { + Ok(FileSize { + size: meta.file_size() as usize, + }) + } + + #[cfg(target_family = "unix")] Ok(FileSize { size: meta.size() as usize, }) diff --git a/src/execution.rs b/src/execution.rs index fd5002f..598ddbb 100644 --- a/src/execution.rs +++ b/src/execution.rs @@ -109,7 +109,7 @@ impl Executor { /// Execute this executor based on its data /// /// Either builds and runs a container, or executes make and/or - /// bash, depending on the execution strategy. + /// bash (powershell on windows), depending on the execution strategy. /// /// Needs the kerblam config to work, as we need to bind-mount the local /// paths in the containers as locally needed and follow other settings. @@ -180,7 +180,19 @@ impl Executor { stringify![vec!["make", "-f", self.target.to.to_str().unwrap()]] } ExecutionStrategy::Shell => { - stringify![vec!["bash", self.target.to.to_str().unwrap()]] + #[cfg(target_family = "unix")] + { + stringify![vec!["bash", self.target.to.to_str().unwrap()]] + } + + #[cfg(target_family = "windows")] + { + // The double {{ and }} are the escaped forms of { and } for format! + let powershell_slug = format!("& {{Invoke-command -ScriptBlock ([ScriptBlock]::Create((Get-Content {})))}}", + self.target.to.into_os_string().into_string().unwrap() + ); + stringify![vec!["powershell", "-Command", &powershell_slug]] + } } } }; @@ -414,8 +426,17 @@ impl FileMover { fn _link(&self) -> Result { // TODO: Make this compatible with other operating systems. log::debug!("Linking {:?} to {:?}", self.from, self.to); + + #[cfg(target_family = "unix")] std::os::unix::fs::symlink(self.from.clone(), self.to.clone())?; + #[cfg(target_family = "windows")] + if self.from.is_dir() { + std::os::windows::fs::symlink_dir(self.from.clone(), self.to.clone())?; + } else { + std::os::windows::fs::symlink_file(self.from.clone(), self.to.clone())?; + } + Ok(self.to.clone()) }