diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 3557c61..cc4d2d2 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -9,3 +9,7 @@ bpaf = { version = "0.9.15", features = ["derive"] } cargo_metadata = "0.19.1" serde = "1.0.217" serde_json = "1.0.137" + +[target.'cfg(windows)'.dependencies] +zip = { version = "2.6.1", features = ["deflate", "time"], default-features = false } +time = "0.3.41" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index a970576..134be4c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,7 +1,8 @@ use bpaf::{Args, Bpaf, Parser}; use cargo_metadata::{MetadataCommand, Package}; use serde::Deserialize; -use std::{env, ffi::OsString, process::Command}; +use std::{env, ffi::OsString, fs::File, path::PathBuf, process::Command}; +use zip::{write::SimpleFileOptions, ZipWriter}; #[derive(Debug, Clone, Bpaf)] #[bpaf(options)] @@ -10,8 +11,8 @@ enum Options { /// Compile ZLUDA (default command) Build(#[bpaf(external(build))] Build), #[bpaf(command)] - /// Build ZLUDA package - Zip, + /// Compile ZLUDA and build a package + Zip(#[bpaf(external(build))] Build), } #[derive(Debug, Clone, Bpaf)] @@ -43,28 +44,68 @@ struct Cargo { struct Project { name: String, - clib_name: Option, + target_name: String, + target_kind: ProjectTarget, meta: ZludaMetadata, } impl Project { fn try_new(p: Package) -> Option { let name = p.name; - let clib_name = p.targets.into_iter().find_map(|target| { - if target.is_cdylib() { - Some(target.name) - } else { - None - } - }); serde_json::from_value::>(p.metadata) .unwrap() - .map(|m| Self { - name, - clib_name, - meta: m.zluda, + .map(|m| { + let (target_name, target_kind) = p + .targets + .into_iter() + .find_map(|target| { + if target.is_cdylib() { + Some((target.name, ProjectTarget::Cdylib)) + } else if target.is_bin() { + Some((target.name, ProjectTarget::Bin)) + } else { + None + } + }) + .unwrap(); + Self { + name, + target_name, + target_kind, + meta: m.zluda, + } }) } + + #[cfg(unix)] + fn prefix(&self) -> &'static str { + match self.clib_name { + None => "", + Some(_) => "lib", + } + } + + #[cfg(unix)] + fn suffix(&self) -> &'static str { + match self.target_kind { + ProjectTarget::Bin => "", + ProjectTarget::Cdylib => ".so", + } + } + + #[cfg(not(unix))] + fn suffix(&self) -> &'static str { + match self.target_kind { + ProjectTarget::Bin => ".exe", + ProjectTarget::Cdylib => ".dll", + } + } +} + +#[derive(Clone, Copy)] +enum ProjectTarget { + Cdylib, + Bin, } #[derive(Deserialize)] @@ -79,6 +120,7 @@ struct ZludaMetadata { windows_only: bool, #[serde(default)] debug_only: bool, + #[cfg_attr(not(unix), allow(unused))] #[serde(default)] linux_symlinks: Vec, } @@ -95,12 +137,14 @@ fn main() { }, }; match options { - Options::Build(b) => compile(b), - Options::Zip => zip(), + Options::Build(b) => { + compile(b); + } + Options::Zip(b) => zip(b), } } -fn compile(b: Build) { +fn compile(b: Build) -> (PathBuf, String, Vec) { let profile = sniff_out_profile_name(&b.cargo_arguments); let meta = MetadataCommand::new().no_deps().exec().unwrap(); let target_directory = meta.target_directory.into_std_path_buf(); @@ -125,7 +169,8 @@ fn compile(b: Build) { } command.args(b.cargo_arguments); assert!(command.status().unwrap().success()); - os::make_symlinks(target_directory, projects, profile); + os::make_symlinks(&target_directory, &*projects, &*profile); + (target_directory, profile, projects) } fn sniff_out_profile_name(b: &[OsString]) -> String { @@ -143,8 +188,32 @@ fn sniff_out_profile_name(b: &[OsString]) -> String { } } -fn zip() { - todo!() +fn zip(zip: Build) { + fn file_options_from_time(from: &File) -> std::io::Result { + let metadata = from.metadata()?; + let modified = metadata.modified()?; + let modified = time::OffsetDateTime::from(modified); + Ok(SimpleFileOptions::default().last_modified_time( + zip::DateTime::try_from(modified).map_err(|err| std::io::Error::other(err))?, + )) + } + + let (target_dir, profile, projects) = compile(zip); + let zip_file = File::create(format!("{}/{profile}/zluda.zip", target_dir.display())).unwrap(); + let mut zip = ZipWriter::new(zip_file); + zip.add_directory("zluda", SimpleFileOptions::default()) + .unwrap(); + for project in projects.iter() { + let name = &project.target_name; + let ext = project.suffix(); + let mut file = + std::fs::File::open(format!("{}/{profile}/{name}{ext}", target_dir.display())).unwrap(); + let file_options = file_options_from_time(&file).unwrap_or_default(); + zip.start_file(format!("zluda/{name}{ext}"), file_options) + .unwrap(); + std::io::copy(&mut file, &mut zip).unwrap(); + } + zip.finish().unwrap(); } #[cfg(unix)] @@ -152,9 +221,9 @@ mod os { use std::path::PathBuf; pub fn make_symlinks( - target_directory: std::path::PathBuf, - projects: Vec, - profile: String, + target_directory: &std::path::PathBuf, + _projects: &[super::Project], + profile: &str, ) { use std::fs; use std::os::unix::fs as unix_fs; @@ -175,7 +244,7 @@ mod os { }, ); let mut link = target_directory.clone(); - link.extend([&*profile, source]); + link.extend([profile, source]); let mut dir = link.clone(); assert!(dir.pop()); fs::create_dir_all(dir).unwrap(); @@ -190,9 +259,9 @@ mod os { #[cfg(not(unix))] mod os { pub fn make_symlinks( - target_directory: std::path::PathBuf, - projects: Vec, - profile: String, + _target_directory: &std::path::PathBuf, + _projects: &[super::Project], + _profile: &str, ) { } }