@@ -12,12 +12,14 @@ mod compile;
1212mod types;
1313
1414use std:: env;
15- use std:: fmt:: Write ;
15+ use std:: fmt:: { self , Write as _ } ;
1616use std:: path:: Path ;
1717
1818use compile:: ShaderInfo ;
1919
2020fn main ( ) {
21+ log:: set_logger ( & BUILD_SCRIPT_LOGGER ) . unwrap ( ) ;
22+ log:: set_max_level ( log:: LevelFilter :: Info ) ;
2123 let out_dir = env:: var_os ( "OUT_DIR" ) . unwrap ( ) ;
2224 let dest_path = Path :: new ( & out_dir) . join ( "shaders.rs" ) ;
2325
@@ -26,10 +28,11 @@ fn main() {
2628 let mut shaders = match ShaderInfo :: from_default ( ) {
2729 Ok ( s) => s,
2830 Err ( err) => {
29- let formatted = err. to_string ( ) ;
30- for line in formatted. lines ( ) {
31- println ! ( "cargo:warning={line}" ) ;
32- }
31+ let mut target = String :: new ( ) ;
32+ // Ideally, we'd write into stdout directly here, but the duck-typing of `write!`
33+ // makes that a bit annoying - we'd have to implement `CargoWarningAdapter` for both `io::Write` and `fmt::Write`
34+ writeln ! ( CargoWarningAdapter :: new( & mut target) , "{err}" ) . unwrap ( ) ;
35+ print ! ( "{target}" ) ;
3336 return ;
3437 }
3538 } ;
@@ -43,7 +46,7 @@ fn main() {
4346 std:: fs:: write ( dest_path, & buf) . unwrap ( ) ;
4447}
4548
46- fn write_types ( buf : & mut String , shaders : & [ ( String , ShaderInfo ) ] ) -> Result < ( ) , std :: fmt:: Error > {
49+ fn write_types ( buf : & mut String , shaders : & [ ( String , ShaderInfo ) ] ) -> Result < ( ) , fmt:: Error > {
4750 writeln ! ( buf, "pub struct Shaders<'a> {{" ) ?;
4851 for ( name, _) in shaders {
4952 writeln ! ( buf, " pub {name}: ComputeShader<'a>," ) ?;
@@ -52,10 +55,7 @@ fn write_types(buf: &mut String, shaders: &[(String, ShaderInfo)]) -> Result<(),
5255 Ok ( ( ) )
5356}
5457
55- fn write_shaders (
56- buf : & mut String ,
57- shaders : & [ ( String , ShaderInfo ) ] ,
58- ) -> Result < ( ) , std:: fmt:: Error > {
58+ fn write_shaders ( buf : & mut String , shaders : & [ ( String , ShaderInfo ) ] ) -> Result < ( ) , fmt:: Error > {
5959 writeln ! ( buf, "mod generated {{" ) ?;
6060 writeln ! ( buf, " use super::*;" ) ?;
6161 writeln ! ( buf, " use BindType::*;" ) ?;
@@ -110,12 +110,12 @@ fn write_shaders(
110110}
111111
112112#[ cfg( not( feature = "msl" ) ) ]
113- fn write_msl ( _: & mut String , _: & ShaderInfo ) -> Result < ( ) , std :: fmt:: Error > {
113+ fn write_msl ( _: & mut String , _: & ShaderInfo ) -> Result < ( ) , fmt:: Error > {
114114 Ok ( ( ) )
115115}
116116
117117#[ cfg( feature = "msl" ) ]
118- fn write_msl ( buf : & mut String , info : & ShaderInfo ) -> Result < ( ) , std :: fmt:: Error > {
118+ fn write_msl ( buf : & mut String , info : & ShaderInfo ) -> Result < ( ) , fmt:: Error > {
119119 let mut index_iter = compile:: msl:: BindingIndexIterator :: default ( ) ;
120120 let indices = info
121121 . bindings
@@ -136,3 +136,74 @@ fn write_msl(buf: &mut String, info: &ShaderInfo) -> Result<(), std::fmt::Error>
136136 writeln ! ( buf, " }}," ) ?;
137137 Ok ( ( ) )
138138}
139+
140+ /// A very simple logger for build scripts, which ensures that warnings and above
141+ /// are visible to the user.
142+ ///
143+ /// We don't use an external crate here to keep build times down.
144+ struct BuildScriptLog ;
145+
146+ static BUILD_SCRIPT_LOGGER : BuildScriptLog = BuildScriptLog ;
147+
148+ impl log:: Log for BuildScriptLog {
149+ fn enabled ( & self , _: & log:: Metadata < ' _ > ) -> bool {
150+ true
151+ }
152+
153+ fn log ( & self , record : & log:: Record < ' _ > ) {
154+ // "more serious" levels are lower
155+ if record. level ( ) <= log:: Level :: Warn {
156+ let mut target = String :: new ( ) ;
157+ write ! (
158+ CargoWarningAdapter :: new( & mut target) ,
159+ "{}: {}" ,
160+ record. level( ) ,
161+ record. args( )
162+ )
163+ . unwrap ( ) ;
164+ println ! ( "{target}" ) ;
165+ } else {
166+ // If the user wants more verbose output from the build script, they would pass
167+ // `-vv` to `cargo build`. In that case, we should provide all of the logs that
168+ // people chose to provide.
169+ // TODO: Maybe this should fall back to `env_logger`?
170+ eprintln ! ( "{}: {}" , record. level( ) , record. args( ) ) ;
171+ }
172+ }
173+
174+ fn flush ( & self ) {
175+ // Nothing to do; we use `println` which is "self-flushing"
176+ }
177+ }
178+
179+ /// An adapter for `fmt::Write` which prepends `cargo:warning=` to each line, ensuring that every
180+ /// output line is shown to build script users.
181+ struct CargoWarningAdapter < W : fmt:: Write > {
182+ writer : W ,
183+ needs_warning : bool ,
184+ }
185+
186+ impl < W : fmt:: Write > CargoWarningAdapter < W > {
187+ fn new ( writer : W ) -> Self {
188+ Self {
189+ writer,
190+ needs_warning : true ,
191+ }
192+ }
193+ }
194+
195+ impl < W : fmt:: Write > fmt:: Write for CargoWarningAdapter < W > {
196+ fn write_str ( & mut self , s : & str ) -> fmt:: Result {
197+ for line in s. split_inclusive ( '\n' ) {
198+ if self . needs_warning {
199+ write ! ( & mut self . writer, "cargo:warning=" ) ?;
200+ self . needs_warning = false ;
201+ }
202+ write ! ( & mut self . writer, "{line}" ) ?;
203+ if line. ends_with ( '\n' ) {
204+ self . needs_warning = true ;
205+ }
206+ }
207+ Ok ( ( ) )
208+ }
209+ }
0 commit comments