@@ -14,7 +14,7 @@ pub const DX_RUSTC_WRAPPER_ENV_VAR: &str = "DX_RUSTC";
14
14
/// This is primarily used to intercept cargo, enabling fast hot-patching by caching the environment
15
15
/// cargo setups up for the user's current project.
16
16
///
17
- /// In a differenet world we could simply rely on cargo printing link args and the rustc command, but
17
+ /// In a different world we could simply rely on cargo printing link args and the rustc command, but
18
18
/// it doesn't seem to output that in a reliable, parseable, cross-platform format (ie using command
19
19
/// files on windows...), so we're forced to do this interception nonsense.
20
20
pub fn is_wrapping_rustc ( ) -> bool {
@@ -25,31 +25,65 @@ pub fn is_wrapping_rustc() -> bool {
25
25
pub struct RustcArgs {
26
26
pub args : Vec < String > ,
27
27
pub envs : Vec < ( String , String ) > ,
28
- pub link_args : Vec < String > ,
28
+ pub link_args : Vec < String > , // I don't believe this is used anymore
29
+ }
30
+
31
+ /// Check if the arguments indicate a linking step, including those in command files.
32
+ fn has_linking_args ( ) -> bool {
33
+ for arg in std:: env:: args ( ) {
34
+ // Direct check for linker-like arguments
35
+ if arg. ends_with ( ".o" ) || arg == "-flavor" {
36
+ return true ;
37
+ }
38
+
39
+ // Check inside command files
40
+ if let Some ( path_str) = arg. strip_prefix ( '@' ) {
41
+ if let Ok ( file_binary) = std:: fs:: read ( path_str) {
42
+ // Handle both UTF-8 and UTF-16LE encodings for response files.
43
+ let content = String :: from_utf8 ( file_binary. clone ( ) ) . unwrap_or_else ( |_| {
44
+ let binary_u16le: Vec < u16 > = file_binary
45
+ . chunks_exact ( 2 )
46
+ . map ( |a| u16:: from_le_bytes ( [ a[ 0 ] , a[ 1 ] ] ) )
47
+ . collect ( ) ;
48
+ String :: from_utf16_lossy ( & binary_u16le)
49
+ } ) ;
50
+
51
+ // Check if any line in the command file contains linking indicators.
52
+ if content. lines ( ) . any ( |line| {
53
+ let trimmed_line = line. trim ( ) . trim_matches ( '"' ) ;
54
+ trimmed_line. ends_with ( ".o" ) || trimmed_line == "-flavor"
55
+ } ) {
56
+ return true ;
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ false
29
63
}
30
64
31
65
/// Run rustc directly, but output the result to a file.
32
66
///
33
67
/// <https://doc.rust-lang.org/cargo/reference/config.html#buildrustc>
34
68
pub async fn run_rustc ( ) {
35
- // if we happen to be both a rustc wrapper and a linker, we want to run the linker if the arguments seem linker-y
36
- // this is a stupid hack
37
- if std:: env:: args ( )
38
- . take ( 5 )
39
- . any ( |arg| arg. ends_with ( ".o" ) || arg == "-flavor" || arg. starts_with ( "@" ) )
40
- {
69
+ // If we are being asked to link, delegate to the linker action.
70
+ if has_linking_args ( ) {
41
71
return crate :: link:: LinkAction :: from_env ( )
42
72
. expect ( "Linker action not found" )
43
73
. run_link ( )
44
74
. await ;
45
75
}
46
76
47
77
let var_file: PathBuf = std:: env:: var ( DX_RUSTC_WRAPPER_ENV_VAR )
48
- . expect ( "DX_RUSTC not set" )
78
+ . expect ( "DX_RUSTC env var must be set" )
49
79
. into ( ) ;
50
80
81
+ // Cargo invokes a wrapper like: `wrapper-name rustc [args...]`
82
+ // We skip our own executable name (`wrapper-name`) to get the args passed to us.
83
+ let captured_args = args ( ) . skip ( 1 ) . collect :: < Vec < _ > > ( ) ;
84
+
51
85
let rustc_args = RustcArgs {
52
- args : args ( ) . skip ( 1 ) . collect :: < Vec < _ > > ( ) ,
86
+ args : captured_args . clone ( ) ,
53
87
envs : vars ( ) . collect :: < _ > ( ) ,
54
88
link_args : Default :: default ( ) ,
55
89
} ;
@@ -63,27 +97,31 @@ pub async fn run_rustc() {
63
97
. nth ( 1 )
64
98
. is_some_and ( |name| name != "___" )
65
99
{
66
- std:: fs:: create_dir_all ( var_file. parent ( ) . expect ( "Failed to get parent dir" ) )
67
- . expect ( "Failed to create parent dir" ) ;
68
- std:: fs:: write (
69
- & var_file,
70
- serde_json:: to_string ( & rustc_args) . expect ( "Failed to serialize rustc args" ) ,
71
- )
72
- . expect ( "Failed to write rustc args to file" ) ;
100
+ let parent_dir = var_file
101
+ . parent ( )
102
+ . expect ( "Args file path has no parent directory" ) ;
103
+ std:: fs:: create_dir_all ( parent_dir)
104
+ . expect ( "Failed to create parent directory for args file" ) ;
105
+
106
+ let serialized_args =
107
+ serde_json:: to_string ( & rustc_args) . expect ( "Failed to serialize rustc args" ) ;
108
+
109
+ std:: fs:: write ( & var_file, serialized_args) . expect ( "Failed to write rustc args to file" ) ;
73
110
}
74
111
75
- // Run the actual rustc command
76
- // We want all stdout/stderr to be inherited, so the running process can see the output
77
- //
78
- // Note that the args format we get from the wrapper includes the `rustc` command itself, so we
79
- // need to skip that - we already skipped the first arg when we created the args struct.
112
+ // Run the actual rustc command.
113
+ // We want all stdout/stderr to be inherited, so the user sees the compiler output.
80
114
let mut cmd = std:: process:: Command :: new ( "rustc" ) ;
81
- cmd. args ( rustc_args. args . iter ( ) . skip ( 1 ) ) ;
115
+
116
+ // The first argument in `captured_args` is "rustc", which we need to skip
117
+ // when passing arguments to the `rustc` command we are spawning.
118
+ cmd. args ( captured_args. iter ( ) . skip ( 1 ) ) ;
82
119
cmd. envs ( rustc_args. envs ) ;
83
120
cmd. stdout ( std:: process:: Stdio :: inherit ( ) ) ;
84
121
cmd. stderr ( std:: process:: Stdio :: inherit ( ) ) ;
85
122
cmd. current_dir ( std:: env:: current_dir ( ) . expect ( "Failed to get current dir" ) ) ;
86
123
87
- // Propagate the exit code
88
- std:: process:: exit ( cmd. status ( ) . unwrap ( ) . code ( ) . unwrap ( ) )
124
+ // Spawn the process and propagate its exit code.
125
+ let status = cmd. status ( ) . expect ( "Failed to execute rustc command" ) ;
126
+ std:: process:: exit ( status. code ( ) . unwrap_or ( 1 ) ) ; // Exit with 1 if process was killed by signal
89
127
}
0 commit comments