stereokit_rust/tools/
build_tools.rs1use std::{
2 env,
3 ffi::OsStr,
4 fs::{self, File, create_dir},
5 io::{self, BufRead, Error},
6 path::{Path, PathBuf},
7 process::Command,
8};
9
10use crate::tools::os_api::{get_assets_dir, get_shaders_sks_dir, get_shaders_source_dir};
11
12pub fn get_skshaderc(bin_dir: PathBuf, with_wine: bool) -> Result<PathBuf, io::Error> {
31 let mut skshaderc = bin_dir.clone();
32 let target_dir = env::var("CARGO_TARGET_DIR").unwrap_or("target".into());
33 skshaderc.push(target_dir);
34
35 if !skshaderc.exists() {
36 return Err(io::Error::new(
37 io::ErrorKind::NotFound,
38 format!("{} not found. Please run 'cargo build' first.", skshaderc.display()),
39 ));
40 }
41
42 let target_os = if with_wine {
43 "win32"
44 } else if cfg!(target_os = "linux") {
45 "linux"
46 } else if cfg!(target_os = "windows") {
47 "win32"
48 } else if cfg!(target_os = "macos") {
49 "mac"
50 } else {
51 ""
52 };
53 let target_arch = if cfg!(target_arch = "x86_64") {
54 "x64"
55 } else if cfg!(target_arch = "aarch64") {
56 "arm64"
57 } else {
58 ""
59 };
60 let exe_type = target_os.to_string() + "_" + target_arch;
61
62 skshaderc.push(r"tools");
63 skshaderc.push(exe_type);
64 if cfg!(windows) || with_wine {
65 skshaderc.push("skshaderc.exe");
66 } else {
67 skshaderc.push("skshaderc");
68 }
69 Ok(skshaderc)
70}
71
72pub fn compile_hlsl(
84 project_dir: PathBuf,
85 target_dir: Option<PathBuf>,
86 options: &[&str],
87 with_wine: bool,
88) -> Result<bool, io::Error> {
89 let bin_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
91
92 let skshaderc = get_skshaderc(project_dir.clone(), with_wine)?;
93
94 let mut shaders_source_path = project_dir.clone();
95
96 shaders_source_path.push(get_shaders_source_dir());
97
98 if !shaders_source_path.exists() || !shaders_source_path.is_dir() {
99 println!(
100 "No shaders to compile. Current directory does not see {shaders_source_path:?} directory. \n---The name of the directory may be change with SK_RUST_SHADERS_SOURCE_DIR"
101 );
102 return Ok(false);
103 }
104
105 let shaders_path = match target_dir {
106 Some(path) => String::from(path.to_str().expect("shader_path can't be a &str!")) + "/",
107 None => {
108 let mut shaders_path = project_dir.clone();
109 shaders_path.push(get_assets_dir());
110 if !shaders_path.exists() || !shaders_path.is_dir() {
111 return Err(Error::other(format!("Current directory do not see {shaders_path:?} directory")));
112 }
113
114 shaders_path.push(get_shaders_sks_dir());
115 if !shaders_path.exists() || !shaders_path.is_dir() {
116 create_dir(&shaders_path)?
117 }
118 String::from(shaders_path.to_str().expect("shader_path can't be a &str!")) + "/"
119 }
120 };
121
122 let mut shaders_include = bin_dir.clone();
123 shaders_include.push("StereoKit");
124 shaders_include.push("tools");
125 shaders_include.push("include");
126
127 println!("skshaderc executable used : {:?}", &skshaderc);
128 println!("Shaders sources are here : {:?}", &shaders_source_path);
129 println!("Shaders compiled there : {:?}", &shaders_path);
130
131 let excluded_extensions = [OsStr::new("hlsli"), OsStr::new("sks"), OsStr::new("txt"), OsStr::new("md")];
132 if let Ok(entries) = shaders_source_path.read_dir() {
133 for entry in entries {
134 let file = entry?.path();
135 println!("Compiling file : {:?}", &file);
136 if file.is_file()
137 && let Some(extension) = file.extension()
138 && !excluded_extensions.contains(&extension)
139 {
140 let mut cmd = if with_wine {
141 let mut c = Command::new("wine");
142 c.arg(skshaderc.clone());
143 c
144 } else {
145 Command::new(OsStr::new(skshaderc.to_str().unwrap_or("NOPE")))
146 };
147 cmd.arg("-f").arg("-e").arg("-i").arg(&shaders_include).arg("-o").arg(&shaders_path);
148 for arg in options {
149 cmd.arg(arg);
150 }
151 let output = cmd.arg(file).output().expect("failed to run shader compiler");
152 let out = String::from_utf8(output.clone().stdout).unwrap_or(format!("{output:#?}"));
153 if !out.is_empty() {
154 println!("{out}")
155 }
156 let err = String::from_utf8(output.clone().stderr).unwrap_or(format!("{output:#?}"));
157 if !err.is_empty() {
158 println!("{err}")
159 }
160 }
161 }
162 }
163 Ok(true)
164}
165
166pub fn copy_tree(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
170 if let Err(_err) = fs::create_dir(&dst) {}
171 for entry in fs::read_dir(src)?.flatten() {
172 let path_type = entry.file_type()?;
173 if path_type.is_dir() {
174 copy_tree(entry.path(), dst.as_ref().join(entry.file_name()))?;
175 } else {
176 fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
177 }
178 }
179 Ok(())
180}
181
182pub fn get_cargo_name() -> Result<String, Error> {
193 let lines = {
195 let file = File::open("./Cargo.toml")?;
196 io::BufReader::new(file).lines()
197 };
198 let mut in_package = false;
199 for line in lines.map_while(Result::ok) {
201 let line = line.trim();
202 if in_package {
203 if line.starts_with("name=") || line.starts_with("name") {
204 return Ok(line.split("=").last().unwrap().trim().replace("\"", ""));
205 }
206 } else if line.contains("[package]") {
207 in_package = true;
208 }
209 }
210 if in_package {
211 Err(Error::other("Cargo.toml do not have a [package]/name field"))
212 } else {
213 Err(Error::other("Cargo.toml do not have a [package] section"))
214 }
215}