diff --git a/vulkano-shaders/src/lib.rs b/vulkano-shaders/src/lib.rs index 63ef83fb..47a32cfd 100644 --- a/vulkano-shaders/src/lib.rs +++ b/vulkano-shaders/src/lib.rs @@ -126,6 +126,20 @@ //! in conjunction with the `src` or `path` field. This allows using shaders compiled through a //! separate build system. //! +//! ## `root_path_env: "..."` +//! +//! Instead of searching relative to your `Cargo.toml`, search relative to some other folder +//! specified by this env variable. The intended use case is using `OUT_DIR` to be able to load +//! shaders generated by your build script. Defaults to `CARGO_MANIFEST_DIR` corresponding to the +//! folder of your `Cargo.toml`. +//! +//! See [`cargo-env-vars`] for a full set of env variables set by cargo. It is also possible to +//! specify env variables from within the build script using the following: +//! ```rust +//! # let shader_out_dir = ""; +//! println!("cargo:rustc-env=SHADER_OUT_DIR={shader_out_dir}"); +//! ``` +//! //! ## `shaders: { first: { src: "...", ty: "..." }, ... }` //! //! With these options the user can compile several shaders in a single macro invocation. Each @@ -200,6 +214,7 @@ //! | `shaderc-build-from-source` | Build the `shaderc` library from source when compiling. | //! | `shaderc-debug` | Compile shaders with debug information included. | //! +//! [`cargo-env-vars`]: https://doc.rust-lang.org/cargo/reference/environment-variables.html //! [cargo-expand]: https://github.com/dtolnay/cargo-expand //! [`ShaderModule::from_words_with_data`]: vulkano::shader::ShaderModule::from_words_with_data //! [`SpecializationConstants`]: vulkano::shader::SpecializationConstants @@ -247,12 +262,34 @@ pub fn shader(input: proc_macro::TokenStream) -> proc_macro::TokenStream { } fn shader_inner(mut input: MacroInput) -> Result { - let root = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()); + let (root, relative_path_error_msg) = match input.root_path_env.as_ref() { + None => ( + env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()), + "to your Cargo.toml".to_owned(), + ), + Some(root_path_env) => { + let root = match env::var(root_path_env.value()) { + Ok(e) => e, + Err(e) => { + bail!( + root_path_env, + "failed to fetch environment variable: {e}; typical parameters are \ + `OUT_DIR` to gather results from your build script, or left default to \ + search relative to your Cargo.toml", + ) + } + }; + let env = root_path_env.value(); + let error = format!("to the path `{root}` specified by the env variable `{env:?}`"); + (root, error) + } + }; + let root_path = Path::new(&root); let shaders = mem::take(&mut input.shaders); // yoink - let mut shaders_code = Vec::with_capacity(input.shaders.len()); - let mut types_code = Vec::with_capacity(input.shaders.len()); + let mut shaders_code = Vec::with_capacity(shaders.len()); + let mut types_code = Vec::with_capacity(shaders.len()); let mut type_registry = TypeRegistry::default(); for (name, (shader_kind, source_kind)) in shaders { @@ -273,7 +310,7 @@ fn shader_inner(mut input: MacroInput) -> Result { bail!( path, "file `{full_path:?}` was not found, note that the path must be relative \ - to your Cargo.toml", + {relative_path_error_msg}", ); } @@ -302,7 +339,7 @@ fn shader_inner(mut input: MacroInput) -> Result { bail!( path, "file `{full_path:?}` was not found, note that the path must be relative \ - to your Cargo.toml", + {relative_path_error_msg}", ); } @@ -350,6 +387,7 @@ enum SourceKind { } struct MacroInput { + root_path_env: Option, include_directories: Vec, macro_defines: Vec<(String, String)>, shared_constants: bool, @@ -365,6 +403,7 @@ impl MacroInput { #[cfg(test)] fn empty() -> Self { MacroInput { + root_path_env: None, include_directories: Vec::new(), macro_defines: Vec::new(), shared_constants: false, @@ -382,6 +421,7 @@ impl Parse for MacroInput { fn parse(input: ParseStream<'_>) -> Result { let root = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()); + let mut root_path_env = None; let mut include_directories = Vec::new(); let mut macro_defines = Vec::new(); let mut shared_constants = None; @@ -583,6 +623,13 @@ impl Parse for MacroInput { } } } + "root_path_env" => { + let lit = input.parse::()?; + if root_path_env.is_some() { + bail!(lit, "field `root_path_env` is already defined"); + } + root_path_env = Some(lit); + } "include" => { let in_brackets; bracketed!(in_brackets in input); @@ -709,6 +756,7 @@ impl Parse for MacroInput { } Ok(MacroInput { + root_path_env, include_directories, macro_defines, shared_constants: shared_constants.unwrap_or(false),