Initial xtask check-in

This commit is contained in:
2025-01-01 17:32:11 -05:00
commit 52110f4e29
17 changed files with 439 additions and 0 deletions

10
crates/xtask/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "xtask"
edition.workspace = true
version.workspace = true
publish = false
[dependencies]
anyhow.workspace = true
xflags = "0.3"
xshell = "0.2"

56
crates/xtask/src/flags.rs Normal file
View File

@ -0,0 +1,56 @@
use std::path::PathBuf;
xflags::xflags! {
src "./src/flags.rs"
cmd xtask {
cmd shader-compile {
optional -S, --source source: PathBuf
}
cmd shader-test {
optional -S, --source source: PathBuf
}
}
}
// generated start
// The following code is generated by `xflags` macro.
// Run `env UPDATE_XFLAGS=1 cargo build` to regenerate.
#[derive(Debug)]
pub struct Xtask {
pub subcommand: XtaskCmd,
}
#[derive(Debug)]
pub enum XtaskCmd {
ShaderCompile(ShaderCompile),
ShaderTest(ShaderTest),
}
#[derive(Debug)]
pub struct ShaderCompile {
pub source: Option<PathBuf>,
}
#[derive(Debug)]
pub struct ShaderTest {
pub source: Option<PathBuf>,
}
impl Xtask {
#[allow(dead_code)]
pub fn from_env_or_exit() -> Self {
Self::from_env_or_exit_()
}
#[allow(dead_code)]
pub fn from_env() -> xflags::Result<Self> {
Self::from_env_()
}
#[allow(dead_code)]
pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
Self::from_vec_(args)
}
}
// generated end

21
crates/xtask/src/main.rs Normal file
View File

@ -0,0 +1,21 @@
mod flags;
mod shader_compile;
mod shader_test;
mod slang_build;
use std::path::PathBuf;
use xshell::{Shell};
use crate::flags::XtaskCmd;
fn main() -> anyhow::Result<()> {
let project_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
let sh = Shell::new()?;
sh.change_dir(project_root);
let flags = flags::Xtask::from_env_or_exit();
match flags.subcommand {
XtaskCmd::ShaderCompile(cmd) => cmd.run(&sh),
XtaskCmd::ShaderTest(cmd) => cmd.run(&sh),
}
}

View File

@ -0,0 +1,19 @@
use crate::flags;
use crate::slang_build::SlangBuild;
use xshell::Shell;
impl flags::ShaderCompile {
pub(crate) fn run(&self, sh: &Shell) -> anyhow::Result<()> {
let targets = ["slangc", "slang-glslang"];
let build_path = SlangBuild::new(self.source.as_ref()).build_targets(&sh, &targets)?;
let slangc_path = format!("{}/Release/bin/slangc", build_path.display());
sh.create_dir("target/xtask/shader")?;
sh.cmd(&slangc_path)
.args(["-target", "spirv"])
.args(["-o", "target/xtask/shader/hello-world.spv"])
.arg("shader/hello-world.slang")
.run()?;
Ok(())
}
}

View File

@ -0,0 +1,34 @@
use crate::flags::ShaderTest;
use crate::slang_build::SlangBuild;
use xshell::Shell;
impl ShaderTest {
pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> {
// Pending resolution of https://github.com/shader-slang/slang/issues/5979
/*
sh.cmd(format!("{}/Release/bin/slang-test", build_dir.display()))
.args(["-category", "compute"])
.args(["-test-dir", "shader-tests"])
.run()?;
Ok(())
*/
let targets = ["slang-test"];
let build_dir = SlangBuild::new(self.source.as_ref()).build_targets(&sh, &targets)?;
let build_dir = build_dir.to_str().unwrap();
// slang-test currently relies on a folder named `tests/` in the current directory
sh.cmd("cp")
.arg("-r")
.arg("shader-tests")
.arg(format!("{build_dir}/tests"))
.run()?;
let _dir_guard = sh.push_dir(&build_dir);
sh.cmd(format!("Release/bin/slang-test"))
.args(["-category", "compute"])
.run()?;
Ok(())
}
}

View File

@ -0,0 +1,83 @@
use anyhow::Context;
use std::path::{Path, PathBuf};
use std::thread::available_parallelism;
use xshell::Shell;
#[derive(Debug, Default, Clone)]
pub(crate) struct SlangBuild {
source: Option<PathBuf>,
}
impl SlangBuild {
pub(crate) fn new<P: AsRef<Path>>(source: Option<P>) -> Self {
Self {
source: source.map(|path| path.as_ref().to_path_buf()),
}
}
}
impl SlangBuild {
pub(crate) fn assure_source(self: &Self, sh: &Shell) -> anyhow::Result<PathBuf> {
if self.source.as_ref().is_some_and(|source| source.exists()) {
return Ok(self.source.as_ref().unwrap().clone());
}
let clone_dir = PathBuf::from("target/xtask/slang");
if clone_dir.exists() {
return Ok(clone_dir);
}
let _ = sh
.cmd("git")
.arg("clone")
.args(["--branch", "v2024.17"])
.args(["--depth", "1"])
.arg("--recurse-submodules")
.arg("--shallow-submodules")
.arg(format!("-j{}", available_parallelism()?))
.arg("https://github.com/shader-slang/slang")
.arg(clone_dir.to_str().unwrap())
.run()?;
Ok(clone_dir)
}
pub(crate) fn assure_build(self: &Self, sh: &Shell) -> anyhow::Result<PathBuf> {
let build_dir = sh.create_dir("target/xtask/slang-build")?;
Ok(build_dir)
}
pub(crate) fn configure(self: &Self, sh: &Shell) -> anyhow::Result<PathBuf> {
let source_dir = self.assure_source(&sh)?;
let build_dir = self.assure_build(&sh)?;
sh.cmd("cmake")
.args(["-S", source_dir.to_str().unwrap()])
.args(["-B", build_dir.to_str().unwrap()])
.arg("-DCMAKE_BUILD_TYPE=Release")
// https://github.com/shader-slang/slang/issues/5832#issuecomment-2533324982
.arg("-DCMAKE_SKIP_INSTALL_RULES=ON")
.run()
.context("slang-build configure")?;
Ok(PathBuf::from(build_dir))
}
pub(crate) fn build_targets(
self: &Self,
sh: &Shell,
targets: &[&str],
) -> anyhow::Result<PathBuf> {
let build_dir = self.configure(&sh)?;
let cmd = sh
.cmd("cmake")
.args(["--build", build_dir.to_str().unwrap()])
.arg(format!("-j{}", available_parallelism()?));
targets
.iter()
.fold(cmd, |cmd, target| cmd.args(["--target", target]))
.run()?;
Ok(build_dir)
}
}