@@ -1109,6 +1109,45 @@ pub fn strip_basedirs<'a>(preprocessor_output: &'a [u8], basedirs: &[Vec<u8>]) -
11091109 Cow :: Owned ( result)
11101110}
11111111
1112+ const PREFIX_MAP_FLAGS : & [ & [ u8 ] ] = & [
1113+ b"-fdebug-prefix-map=" ,
1114+ b"-fmacro-prefix-map=" ,
1115+ b"-ffile-prefix-map=" ,
1116+ ] ;
1117+
1118+ /// Normalize a `-f{debug,macro,file}-prefix-map=OLD=NEW` compiler argument for cache key
1119+ /// computation by stripping basedir prefixes from the OLD path component.
1120+ ///
1121+ /// These flags embed absolute source or object directory paths. Because they are compound
1122+ /// arguments (not standalone paths), `strip_basedirs` cannot reach them via preprocessor-output
1123+ /// normalization. This function handles them explicitly so builds from different directories
1124+ /// can share cache entries when `SCCACHE_BASEDIRS` is configured.
1125+ ///
1126+ /// Returns `Cow::Owned` with the normalized argument if a basedir was stripped, or
1127+ /// `Cow::Borrowed` of the original if the argument was not a prefix-map flag or no basedir matched.
1128+ pub fn normalize_prefix_map_arg < ' a > ( arg : & ' a OsStr , basedirs : & [ Vec < u8 > ] ) -> Cow < ' a , OsStr > {
1129+ fn try_normalize ( bytes : & [ u8 ] , basedirs : & [ Vec < u8 > ] ) -> Option < Vec < u8 > > {
1130+ let ( flag, rest) = PREFIX_MAP_FLAGS
1131+ . iter ( )
1132+ . find_map ( |& flag| Some ( ( flag, bytes. strip_prefix ( flag) ?) ) ) ?;
1133+ // Format is OLD=NEW; split on the first '='
1134+ let sep = rest. iter ( ) . position ( |& b| b == b'=' ) ?;
1135+ let old = & rest[ ..sep] ;
1136+ // Strip the longest matching basedir prefix from OLD
1137+ let stripped_old = basedirs
1138+ . iter ( )
1139+ . filter ( |dir| old. starts_with ( dir. as_slice ( ) ) )
1140+ . max_by_key ( |dir| dir. len ( ) )
1141+ . map ( |dir| & old[ dir. len ( ) ..] ) ?;
1142+ Some ( [ flag, stripped_old, & rest[ sep..] ] . concat ( ) )
1143+ }
1144+
1145+ // SAFETY: result bytes originate from `arg.as_encoded_bytes()`, preserving its encoding
1146+ try_normalize ( arg. as_encoded_bytes ( ) , basedirs)
1147+ . map ( |b| Cow :: Owned ( unsafe { OsString :: from_encoded_bytes_unchecked ( b) } ) )
1148+ . unwrap_or ( Cow :: Borrowed ( arg) )
1149+ }
1150+
11121151/// Normalize path for case-insensitive comparison.
11131152/// On Windows: converts all backslashes to forward slashes;
11141153/// lowercases characters for consistency.
@@ -1249,6 +1288,7 @@ pub fn resolve_compiler_avoiding_ccache(
12491288#[ cfg( test) ]
12501289mod tests {
12511290 use super :: { OsStrExt , TimeMacroFinder , resolve_compiler_avoiding_ccache} ;
1291+ use std:: borrow:: Cow ;
12521292 use std:: ffi:: { OsStr , OsString } ;
12531293 use std:: path:: Path ;
12541294
@@ -1625,6 +1665,41 @@ mod tests {
16251665 ) ;
16261666 }
16271667
1668+ #[ test]
1669+ fn test_normalize_prefix_map_arg ( ) {
1670+ let basedirs = vec ! [ b"/home/user/project/" . to_vec( ) ] ;
1671+
1672+ // Each flag variant with matching basedir — OLD is fully stripped, leaving empty OLD
1673+ for flag in & [
1674+ "-fdebug-prefix-map" ,
1675+ "-fmacro-prefix-map" ,
1676+ "-ffile-prefix-map" ,
1677+ ] {
1678+ let arg = OsString :: from ( format ! ( "{flag}=/home/user/project/=/topsrcdir/" ) ) ;
1679+ let result = super :: normalize_prefix_map_arg ( & arg, & basedirs) ;
1680+ assert_eq ! (
1681+ & * result,
1682+ OsStr :: new( & format!( "{flag}==/topsrcdir/" ) ) ,
1683+ "{flag}"
1684+ ) ;
1685+ }
1686+
1687+ // Non-matching basedir returns the original (Borrowed)
1688+ let arg = OsString :: from ( "-fdebug-prefix-map=/other/path/=/topsrcdir/" ) ;
1689+ let result = super :: normalize_prefix_map_arg ( & arg, & basedirs) ;
1690+ assert ! ( matches!( result, Cow :: Borrowed ( _) ) ) ;
1691+
1692+ // Non-prefix-map arg returns the original (Borrowed)
1693+ let arg = OsString :: from ( "-I/home/user/project/include" ) ;
1694+ let result = super :: normalize_prefix_map_arg ( & arg, & basedirs) ;
1695+ assert ! ( matches!( result, Cow :: Borrowed ( _) ) ) ;
1696+
1697+ // Empty basedirs returns the original (Borrowed)
1698+ let arg = OsString :: from ( "-fdebug-prefix-map=/home/user/project/=/topsrcdir/" ) ;
1699+ let result = super :: normalize_prefix_map_arg ( & arg, & [ ] ) ;
1700+ assert ! ( matches!( result, Cow :: Borrowed ( _) ) ) ;
1701+ }
1702+
16281703 #[ test]
16291704 fn test_normalize_win_path_ascii ( ) {
16301705 // Test basic ASCII normalization
0 commit comments