@@ -158,13 +158,17 @@ 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 (
162+ outputs : & [ BuildScriptOutput ] ,
163+ exec_root : & str ,
164+ out_dir : & str ,
165+ ) -> String {
162166 outputs
163167 . iter ( )
164168 . filter_map ( |x| {
165169 if let BuildScriptOutput :: Env ( env) = x {
166- Some ( Self :: escape_for_serializing ( Self :: redact_exec_root (
167- env, exec_root,
170+ Some ( Self :: escape_for_serializing ( Self :: redact_paths (
171+ env, exec_root, out_dir ,
168172 ) ) )
169173 } else {
170174 None
@@ -179,6 +183,7 @@ impl BuildScriptOutput {
179183 outputs : & [ BuildScriptOutput ] ,
180184 crate_links : & str ,
181185 exec_root : & str ,
186+ out_dir : & str ,
182187 ) -> String {
183188 let prefix = format ! ( "DEP_{}_" , crate_links. replace( '-' , "_" ) . to_uppercase( ) ) ;
184189 outputs
@@ -188,7 +193,7 @@ impl BuildScriptOutput {
188193 Some ( format ! (
189194 "{}{}" ,
190195 prefix,
191- Self :: escape_for_serializing( Self :: redact_exec_root ( env, exec_root) )
196+ Self :: escape_for_serializing( Self :: redact_paths ( env, exec_root, out_dir ) )
192197 ) )
193198 } else {
194199 None
@@ -199,7 +204,11 @@ impl BuildScriptOutput {
199204 }
200205
201206 /// Convert a vector of [BuildScriptOutput] into a flagfile.
202- pub fn outputs_to_flags ( outputs : & [ BuildScriptOutput ] , exec_root : & str ) -> CompileAndLinkFlags {
207+ pub fn outputs_to_flags (
208+ outputs : & [ BuildScriptOutput ] ,
209+ exec_root : & str ,
210+ out_dir : & str ,
211+ ) -> CompileAndLinkFlags {
203212 let mut compile_flags = Vec :: new ( ) ;
204213 let mut link_flags = Vec :: new ( ) ;
205214 let mut link_search_paths = Vec :: new ( ) ;
@@ -217,13 +226,34 @@ impl BuildScriptOutput {
217226
218227 CompileAndLinkFlags {
219228 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) ,
229+ link_flags : Self :: redact_paths ( & link_flags. join ( "\n " ) , exec_root, out_dir ) ,
230+ link_search_paths : Self :: redact_paths ( & link_search_paths. join ( "\n " ) , exec_root, out_dir ) ,
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