@@ -158,13 +158,13 @@ impl BuildScriptOutput {
158158 }
159159
160160 /// Convert a vector of [BuildScriptOutput] into a list of environment variables.
161- pub fn outputs_to_env ( outputs : & [ BuildScriptOutput ] , exec_root : & str ) -> String {
161+ pub fn outputs_to_env ( outputs : & [ BuildScriptOutput ] , exec_root : & str , out_dir : & str ) -> String {
162162 outputs
163163 . iter ( )
164164 . filter_map ( |x| {
165165 if let BuildScriptOutput :: Env ( env) = x {
166- Some ( Self :: escape_for_serializing ( Self :: redact_exec_root (
167- env, exec_root,
166+ Some ( Self :: escape_for_serializing ( Self :: redact_paths (
167+ env, exec_root, out_dir ,
168168 ) ) )
169169 } else {
170170 None
@@ -179,6 +179,7 @@ impl BuildScriptOutput {
179179 outputs : & [ BuildScriptOutput ] ,
180180 crate_links : & str ,
181181 exec_root : & str ,
182+ out_dir : & str ,
182183 ) -> String {
183184 let prefix = format ! ( "DEP_{}_" , crate_links. replace( '-' , "_" ) . to_uppercase( ) ) ;
184185 outputs
@@ -188,7 +189,7 @@ impl BuildScriptOutput {
188189 Some ( format ! (
189190 "{}{}" ,
190191 prefix,
191- Self :: escape_for_serializing( Self :: redact_exec_root ( env, exec_root) )
192+ Self :: escape_for_serializing( Self :: redact_paths ( env, exec_root, out_dir ) )
192193 ) )
193194 } else {
194195 None
@@ -199,7 +200,11 @@ impl BuildScriptOutput {
199200 }
200201
201202 /// Convert a vector of [BuildScriptOutput] into a flagfile.
202- pub fn outputs_to_flags ( outputs : & [ BuildScriptOutput ] , exec_root : & str ) -> CompileAndLinkFlags {
203+ pub fn outputs_to_flags (
204+ outputs : & [ BuildScriptOutput ] ,
205+ exec_root : & str ,
206+ out_dir : & str ,
207+ ) -> CompileAndLinkFlags {
203208 let mut compile_flags = Vec :: new ( ) ;
204209 let mut link_flags = Vec :: new ( ) ;
205210 let mut link_search_paths = Vec :: new ( ) ;
@@ -217,13 +222,38 @@ impl BuildScriptOutput {
217222
218223 CompileAndLinkFlags {
219224 compile_flags : compile_flags. join ( "\n " ) ,
220- link_flags : Self :: redact_exec_root ( & link_flags. join ( "\n " ) , exec_root) ,
221- link_search_paths : Self :: redact_exec_root ( & link_search_paths. join ( "\n " ) , exec_root) ,
225+ link_flags : Self :: redact_paths ( & link_flags. join ( "\n " ) , exec_root, out_dir) ,
226+ link_search_paths : Self :: redact_paths (
227+ & link_search_paths. join ( "\n " ) ,
228+ exec_root,
229+ out_dir,
230+ ) ,
222231 }
223232 }
224233
225- fn redact_exec_root ( value : & str , exec_root : & str ) -> String {
226- value. replace ( exec_root, "${pwd}" )
234+ /// Replace the absolute exec-root with `${pwd}` and the relative
235+ /// configuration-dependent `out_dir` path (e.g.
236+ /// `bazel-out/<config>/bin/.../_bs.out_dir`) with `${out_dir}`.
237+ ///
238+ /// Both tokens are substituted by `process_wrapper` at action
239+ /// execution time. Routing the `out_dir` portion through
240+ /// `${out_dir}` lets Bazel's path mapping
241+ /// (`--experimental_output_paths=strip`) rewrite it: the consumer
242+ /// `Rustc` action passes the directory to `process_wrapper` via
243+ /// `--out-dir <File>` from a `File`-typed `Args` entry, so the value
244+ /// is the mapped `bazel-out/cfg/bin/...` path under path mapping and
245+ /// the un-mapped path otherwise. Without this redaction,
246+ /// build-script-emitted env vars (e.g. `cargo::rustc-env=FOO=$OUT_DIR/bar`)
247+ /// would carry the un-mapped path through the `_bs.env` file and
248+ /// cause the path-mapped Rustc action to look in the wrong location
249+ /// at runtime.
250+ fn redact_paths ( value : & str , exec_root : & str , out_dir : & str ) -> String {
251+ let with_pwd = value. replace ( exec_root, "${pwd}" ) ;
252+ if out_dir. is_empty ( ) {
253+ with_pwd
254+ } else {
255+ with_pwd. replace ( out_dir, "${out_dir}" )
256+ }
227257 }
228258
229259 // The process-wrapper treats trailing backslashes as escapes for following newlines.
@@ -283,15 +313,15 @@ mod tests {
283313 BuildScriptOutput :: Env ( "no_trailing_newline=true" . to_owned( ) )
284314 ) ;
285315 assert_eq ! (
286- BuildScriptOutput :: outputs_to_dep_env( & result, "ssh2" , "/some/absolute/path" ) ,
316+ BuildScriptOutput :: outputs_to_dep_env( & result, "ssh2" , "/some/absolute/path" , "" ) ,
287317 "DEP_SSH2_VERSION=123\n DEP_SSH2_VERSION_NUMBER=1010107f\n DEP_SSH2_INCLUDE_PATH=${pwd}/include" . to_owned( )
288318 ) ;
289319 assert_eq ! (
290- BuildScriptOutput :: outputs_to_env( & result, "/some/absolute/path" ) ,
320+ BuildScriptOutput :: outputs_to_env( & result, "/some/absolute/path" , "" ) ,
291321 "FOO=BAR\n BAR=FOO\n SOME_PATH=${pwd}/beep\n no_trailing_newline=true" . to_owned( )
292322 ) ;
293323 assert_eq ! (
294- BuildScriptOutput :: outputs_to_flags( & result, "/some/absolute/path" ) ,
324+ BuildScriptOutput :: outputs_to_flags( & result, "/some/absolute/path" , "" ) ,
295325 CompileAndLinkFlags {
296326 // -Lblah was output as a rustc-flags, so even though it probably _should_ be a link
297327 // flag, we don't treat it like one.
@@ -366,7 +396,7 @@ cargo::rustc-env=valid2=2
366396 let result = BuildScriptOutput :: outputs_from_reader ( reader) ;
367397 assert_eq ! ( result. len( ) , 2 ) ;
368398 assert_eq ! (
369- & BuildScriptOutput :: outputs_to_env( & result, "/some/absolute/path" ) ,
399+ & BuildScriptOutput :: outputs_to_env( & result, "/some/absolute/path" , "" ) ,
370400 "valid1=1\n valid2=2"
371401 ) ;
372402 }
@@ -385,7 +415,7 @@ cargo:rustc-env=valid2=2
385415 let result = BuildScriptOutput :: outputs_from_reader ( reader) ;
386416 assert_eq ! ( result. len( ) , 2 ) ;
387417 assert_eq ! (
388- & BuildScriptOutput :: outputs_to_env( & result, "/some/absolute/path" ) ,
418+ & BuildScriptOutput :: outputs_to_env( & result, "/some/absolute/path" , "" ) ,
389419 "valid1=1\n valid2=2"
390420 ) ;
391421 }
@@ -399,4 +429,32 @@ cargo:rustc-env=valid2=2
399429 vec![ BuildScriptOutput :: DepEnv ( "VERSION_1_10_0=1" . to_owned( ) ) ]
400430 ) ;
401431 }
432+
433+ /// Verify that a build-script-emitted env var that references the
434+ /// `out_dir` (e.g. `cargo::rustc-env=FOO=$OUT_DIR/bar`) is rewritten
435+ /// to use the `${out_dir}` substitution token. `process_wrapper`
436+ /// substitutes the token at action execution time using the value
437+ /// from its `--out-dir` arg, which is sourced from a `File`-typed
438+ /// `Args` entry on the rules_rust side and therefore picks up Bazel
439+ /// path mapping (`--experimental_output_paths=strip`) when the
440+ /// consumer Rustc action advertises `supports-path-mapping`.
441+ #[ test]
442+ fn out_dir_in_env_value_is_redacted_to_substitution_token ( ) {
443+ let buff = Cursor :: new (
444+ "
445+ cargo::rustc-env=FOO=/abs/exec_root/bazel-out/cfg/bin/_bs.out_dir/op.rs
446+ cargo::rustc-env=BAR=/abs/exec_root/elsewhere/file.rs
447+ " ,
448+ ) ;
449+ let reader = BufReader :: new ( buff) ;
450+ let result = BuildScriptOutput :: outputs_from_reader ( reader) ;
451+ assert_eq ! (
452+ BuildScriptOutput :: outputs_to_env(
453+ & result,
454+ "/abs/exec_root" ,
455+ "bazel-out/cfg/bin/_bs.out_dir" ,
456+ ) ,
457+ "FOO=${pwd}/${out_dir}/op.rs\n BAR=${pwd}/elsewhere/file.rs"
458+ ) ;
459+ }
402460}
0 commit comments