Add support for zip packaging, only on Windows for now

This commit is contained in:
Andrzej Janik 2024-04-19 22:56:01 +02:00
commit 954a225a86
7 changed files with 155 additions and 109 deletions

View file

@ -58,4 +58,4 @@ opt-level = 3
opt-level = 3 opt-level = 3
[profile.dev.package.xtask] [profile.dev.package.xtask]
opt-level = 3 opt-level = 2

View file

@ -1,57 +0,0 @@
[config]
default_to_workspace = false
skip_core_tasks = true
[tasks.build]
run_task = [
{ name = "build-windows", condition = { platforms = ["windows"] } },
{ name = "build-linux", condition = { platforms = ["linux"] } },
]
[tasks.build-windows]
command = "cargo"
args = [
"build",
"-p", "offline_compiler",
"-p", "zluda_dump",
"-p", "zluda_inject",
"-p", "zluda_lib",
"-p", "zluda_ml",
"-p", "zluda_redirect",
]
[tasks.build-linux]
command = "cargo"
args = [
"build",
"-p", "offline_compiler",
"-p", "zluda_blas",
"-p", "zluda_blaslt",
"-p", "zluda_ccl",
"-p", "zluda_dnn",
"-p", "zluda_dump",
"-p", "zluda_fft",
"-p", "zluda_lib",
"-p", "zluda_ml",
"-p", "zluda_sparse",
]
[tasks.build-release]
command = "cargo"
args = [
"build",
"--release",
"-p", "offline_compiler",
"-p", "zluda_blas",
"-p", "zluda_blaslt",
"-p", "zluda_ccl",
"-p", "zluda_dnn",
"-p", "zluda_dump",
"-p", "zluda_fft",
"-p", "zluda_lib",
"-p", "zluda_ml",
"-p", "zluda_sparse",
]
[tasks.default]
alias = "build"

View file

@ -18,3 +18,4 @@ features = [
[package.metadata.zluda] [package.metadata.zluda]
debug_only = true debug_only = true
skip_zip = true

View file

@ -12,3 +12,6 @@ cargo_metadata = "=0.17.0"
cargo-platform = "=0.1.5" cargo-platform = "=0.1.5"
serde = "1.0.193" serde = "1.0.193"
serde_json = "1.0.108" serde_json = "1.0.108"
flate2 = { version = "1.0.28", features = ["cloudflare_zlib"], default-features = false }
zip = { version = "0.6.6", features = ["deflate", "time"], default-features = false }
time = { version = "=0.3.23", features = ["local-offset"] }

View file

@ -1,7 +1,10 @@
use argh::{EarlyExit, FromArgs, TopLevelCommand}; use argh::{EarlyExit, FromArgs, TopLevelCommand};
use cargo_metadata::camino::Utf8PathBuf;
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
convert::TryFrom,
env, env,
fs::File,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
}; };
@ -60,7 +63,7 @@ struct BuildCommand {
} }
#[derive(FromArgs)] #[derive(FromArgs)]
/// Package build artifacts into an archive (.zip or .tar.gz) /// Compile ZLUDA and package binaries into an archive (.zip or .tar.gz)
#[argh(subcommand, name = "zip")] #[argh(subcommand, name = "zip")]
struct ZipCommand { struct ZipCommand {
/// use artifacts from release mode /// use artifacts from release mode
@ -73,10 +76,15 @@ fn main() -> Result<(), DynError> {
let args: Arguments = argh::from_env(); let args: Arguments = argh::from_env();
std::process::exit(match args.command { std::process::exit(match args.command {
Subcommand::Build(BuildCommand { release }) => build(!release)?, Subcommand::Build(BuildCommand { release }) => build(!release)?,
Subcommand::Zip(_) => panic!(), Subcommand::Zip(ZipCommand { release }) => build_and_zip(!release)?,
}) })
} }
fn build_and_zip(is_debug: bool) -> Result<i32, DynError> {
let workspace = build_impl(is_debug)?;
zip(workspace)
}
#[derive(Deserialize)] #[derive(Deserialize)]
struct ZludaMetadata { struct ZludaMetadata {
zluda: Project, zluda: Project,
@ -92,8 +100,6 @@ struct Project {
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
kind: TargetKind, kind: TargetKind,
#[serde(default)] #[serde(default)]
top_level: bool,
#[serde(default)]
windows_only: bool, windows_only: bool,
#[serde(default)] #[serde(default)]
linux_only: bool, linux_only: bool,
@ -104,6 +110,8 @@ struct Project {
#[serde(default)] #[serde(default)]
skip_dump_link: bool, skip_dump_link: bool,
#[serde(default)] #[serde(default)]
skip_zip: bool,
#[serde(default)]
linux_names: Vec<String>, linux_names: Vec<String>,
#[serde(default)] #[serde(default)]
dump_names: Vec<String>, dump_names: Vec<String>,
@ -116,14 +124,56 @@ enum TargetKind {
Cdylib, Cdylib,
} }
struct Workspace {
pub cargo: String,
pub project_root: PathBuf,
pub projects: Vec<Project>,
pub target_directory: Utf8PathBuf,
}
impl Workspace {
fn open(is_debug: bool) -> Result<Self, DynError> {
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
let project_root = Self::project_root()?;
let mut cmd = cargo_metadata::MetadataCommand::new();
cmd.cargo_path(&cargo).current_dir(&project_root).no_deps();
let cargo_metadata = cmd.exec()?;
let projects = cargo_metadata
.packages
.into_iter()
.filter_map(Project::new)
.filter(|p| !p.skip_build(is_debug))
.collect::<Vec<_>>();
let mut target_directory = cargo_metadata.target_directory;
target_directory.push(if is_debug { "debug" } else { "release" });
Ok(Workspace {
cargo,
project_root,
projects,
target_directory,
})
}
fn project_root() -> Result<PathBuf, DynError> {
Ok(Path::new(&env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(1)
.ok_or::<DynError>("CARGO_MANIFEST_DIR".into())?
.to_path_buf())
}
fn cargo_command(&self) -> Command {
let mut command = Command::new(&self.cargo);
command.current_dir(&self.project_root);
command
}
}
impl Project { impl Project {
fn new(json_pkg: cargo_metadata::Package) -> Self { fn new(json_pkg: cargo_metadata::Package) -> Option<Self> {
let mut project = serde_json::from_value::<Option<ZludaMetadata>>(json_pkg.metadata) let project_metadata =
.unwrap() serde_json::from_value::<Option<ZludaMetadata>>(json_pkg.metadata).unwrap()?;
.map_or(Default::default(), |x| x.zluda); let mut project = project_metadata.zluda;
if project != Default::default() {
project.top_level = true;
}
project.name = json_pkg.name; project.name = json_pkg.name;
if let Some((target_name, kind)) = json_pkg.targets.into_iter().find_map(|t| { if let Some((target_name, kind)) = json_pkg.targets.into_iter().find_map(|t| {
match t.kind.first().map(std::ops::Deref::deref) { match t.kind.first().map(std::ops::Deref::deref) {
@ -135,13 +185,10 @@ impl Project {
project.target_name = target_name; project.target_name = target_name;
project.kind = kind; project.kind = kind;
} }
project Some(project)
} }
fn skip_build(&self, is_debug: bool) -> bool { fn skip_build(&self, is_debug: bool) -> bool {
if !self.top_level {
return true;
}
if self.broken { if self.broken {
return true; return true;
} }
@ -159,49 +206,93 @@ impl Project {
} }
fn build(is_debug: bool) -> Result<i32, DynError> { fn build(is_debug: bool) -> Result<i32, DynError> {
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); build_impl(is_debug)?;
let project_root = project_root()?; Ok(0)
let mut cmd = cargo_metadata::MetadataCommand::new(); }
cmd.cargo_path(&cargo).current_dir(&project_root).no_deps();
let metadata = cmd.exec()?; fn build_impl(is_debug: bool) -> Result<Workspace, DynError> {
let projects = metadata let workspace = Workspace::open(is_debug)?;
.packages let mut command = workspace.cargo_command();
.into_iter() command.arg("build");
.map(Project::new) workspace
.filter(|p| !p.skip_build(is_debug)) .projects
.collect::<Vec<_>>(); .iter()
let mut command = Command::new(&cargo); .fold(&mut command, |command, proj| {
command.current_dir(&project_root).arg("build"); command.args(["-p", &proj.name])
projects.iter().fold(&mut command, |command, proj| { });
command.args(["-p", &proj.name])
});
if !is_debug { if !is_debug {
command.arg("--release"); command.arg("--release");
} }
let build_result = command.status()?.code().unwrap(); let build_result = command.status()?.code().unwrap();
if build_result != 0 { if build_result != 0 {
return Ok(build_result); return Err(format!("{command:?} failed with exit code {build_result}").into());
} }
os::create_dump_dir_and_symlinks(is_debug, metadata.target_directory, projects); os::create_dump_dir_and_symlinks(is_debug, &workspace);
Ok(workspace)
}
fn zip(workspace: Workspace) -> Result<i32, DynError> {
fn get_zip_entry_options(
f: &File,
time_offset: time::UtcOffset,
) -> Option<zip::write::FileOptions> {
let time = f.metadata().ok()?.modified().ok()?;
let time = time::OffsetDateTime::from(time).to_offset(time_offset);
Some(
zip::write::FileOptions::default()
.last_modified_time(zip::DateTime::try_from(time).ok()?),
)
}
let mut target_file = workspace.target_directory.clone();
target_file.push("zluda.zip");
let zip_archive = File::create(target_file)?;
let mut zip_writer = zip::write::ZipWriter::new(zip_archive);
let time_offset = time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC);
for p in workspace.projects {
if p.skip_zip {
continue;
}
let mut src_file = File::open(format!(
"{}/{}{}",
&workspace.target_directory,
p.target_name,
p.kind.suffix()
))?;
zip_writer.start_file(
format!("zluda/{}{}", p.target_name, p.kind.suffix()),
get_zip_entry_options(&src_file, time_offset)
.unwrap_or(zip::write::FileOptions::default()),
)?;
std::io::copy(&mut src_file, &mut zip_writer)?;
}
zip_writer.finish()?;
Ok(0) Ok(0)
} }
fn project_root() -> Result<PathBuf, DynError> { impl TargetKind {
Ok(Path::new(&env!("CARGO_MANIFEST_DIR")) #[cfg(unix)]
.ancestors() fn prefix(self) -> &'static str {
.nth(1) match self {
.ok_or::<DynError>("CARGO_MANIFEST_DIR".into())? TargetKind::Binary => "",
.to_path_buf()) TargetKind::Cdylib => "lib",
} }
}
#[cfg(not(unix))] #[cfg(unix)]
mod os { fn suffix(self) -> &'static str {
use super::Project; match self {
use cargo_metadata::camino::Utf8PathBuf; TargetKind::Binary => "",
TargetKind::Cdylib => ".so",
}
}
// This is 100% intentional, we don't want symlinks on Windows since #[cfg(windows)]
// we use completely different scheme for injections here fn suffix(self) -> &'static str {
pub(crate) fn create_dump_dir_and_symlinks(_: bool, _: Utf8PathBuf, _: Vec<Project>) {} match self {
TargetKind::Binary => ".exe",
TargetKind::Cdylib => ".dll",
}
}
} }
#[cfg(unix)] #[cfg(unix)]
@ -211,11 +302,10 @@ mod os {
pub(crate) fn create_dump_dir_and_symlinks( pub(crate) fn create_dump_dir_and_symlinks(
is_debug: bool, is_debug: bool,
mut target_directory: Utf8PathBuf, mut target_directory: &Utf8PathBuf,
projects: Vec<Project>, projects: Vec<Project>,
) { ) {
use std::fs; use std::fs;
target_directory.push(if is_debug { "debug" } else { "release" });
let mut dump_dir = target_directory.clone(); let mut dump_dir = target_directory.clone();
dump_dir.push("dump"); dump_dir.push("dump");
fs::create_dir_all(&dump_dir).unwrap(); fs::create_dir_all(&dump_dir).unwrap();
@ -279,3 +369,12 @@ mod os {
} }
} }
} }
#[cfg(windows)]
mod os {
use crate::Workspace;
// This is 100% intentional, we don't want symlinks on Windows since
// we use a completely different scheme for injections there
pub(crate) fn create_dump_dir_and_symlinks(_: bool, _: &Workspace) {}
}

View file

@ -26,3 +26,4 @@ features = [
[package.metadata.zluda] [package.metadata.zluda]
debug_only = true debug_only = true
windows_only = true windows_only = true
skip_zip = true

View file

@ -15,5 +15,4 @@ atiadlxx-sys = { path = "../atiadlxx-sys" }
rocm_smi-sys = { path = "../rocm_smi-sys" } rocm_smi-sys = { path = "../rocm_smi-sys" }
[package.metadata.zluda] [package.metadata.zluda]
top_level = true
linux_names = ["libnvidia-ml.so", "libnvidia-ml.so.1"] linux_names = ["libnvidia-ml.so", "libnvidia-ml.so.1"]