Skip to content

Commit 1e9aa1a

Browse files
committed
Fix bug where MSVC linker flags were not handled properly
Clang and GCC's `-Wl` argument can be used to pass arguments to the linker. MSVC, however, does not recognize this option and instead uses a /link flag at the end of all other arguments for this purpose. This commit makes use of "/link" with MSVC when LDFLAGS are used.
1 parent d4e0478 commit 1e9aa1a

File tree

1 file changed

+94
-21
lines changed

1 file changed

+94
-21
lines changed

src/run.rs

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,16 @@ pub fn run(language: Language, program: &str) -> Result<Assert, Box<dyn Error>>
7373
if msvc {
7474
command = compiler.to_command();
7575

76-
command_add_compiler_flags(&mut command, &variables);
76+
command_add_compiler_flags(&mut command, &variables, msvc);
7777
command_add_output_file(&mut command, &output_path, msvc, compiler.is_like_clang());
7878
command.arg(input_path.clone());
79-
command.envs(variables.clone());
79+
command_add_linker_flags_msvc(&mut command, &variables);
8080
} else {
8181
command = Command::new(compiler.path());
8282

8383
command.arg(input_path.clone()); // the input must come first
8484
command.args(compiler.args());
85-
command_add_compiler_flags(&mut command, &variables);
85+
command_add_compiler_flags(&mut command, &variables, msvc);
8686
command_add_output_file(&mut command, &output_path, msvc, compiler.is_like_clang());
8787
}
8888

@@ -159,31 +159,48 @@ fn command_add_output_file(command: &mut Command, output_path: &PathBuf, msvc: b
159159
}
160160
}
161161

162-
fn command_add_compiler_flags(command: &mut Command, variables: &HashMap<String, String>) {
163-
let get_env_flags = |env_name: &str| -> Vec<String> {
164-
variables
165-
.get(env_name)
166-
.map(|e| e.to_string())
167-
.ok_or_else(|| env::var(env_name))
168-
.unwrap_or_default()
169-
.split_ascii_whitespace()
170-
.map(|slice| slice.to_string())
171-
.collect()
172-
};
173-
174-
command.args(get_env_flags("CFLAGS"));
175-
command.args(get_env_flags("CPPFLAGS"));
176-
command.args(get_env_flags("CXXFLAGS"));
177-
178-
for linker_argument in get_env_flags("LDFLAGS") {
179-
command.arg(format!("-Wl,{}", linker_argument));
162+
fn get_env_flags(variables: &HashMap<String, String>, env_name: &str) -> Vec<String> {
163+
variables
164+
.get(env_name)
165+
.map(|e| e.to_string())
166+
.ok_or_else(|| env::var(env_name))
167+
.unwrap_or_default()
168+
.split_ascii_whitespace()
169+
.map(|slice| slice.to_string())
170+
.collect()
171+
}
172+
173+
fn command_add_compiler_flags(
174+
command: &mut Command,
175+
variables: &HashMap<String, String>,
176+
msvc: bool,
177+
) {
178+
command.args(get_env_flags(variables, "CFLAGS"));
179+
command.args(get_env_flags(variables, "CPPFLAGS"));
180+
command.args(get_env_flags(variables, "CXXFLAGS"));
181+
182+
if !msvc {
183+
for linker_argument in get_env_flags(variables, "LDFLAGS") {
184+
command.arg(format!("-Wl,{}", linker_argument));
185+
}
186+
}
187+
}
188+
189+
fn command_add_linker_flags_msvc(command: &mut Command, variables: &HashMap<String, String>) {
190+
let linker_flags = get_env_flags(variables, "LDFLAGS");
191+
if !linker_flags.is_empty() {
192+
command.arg("/link");
193+
for linker_argument in linker_flags {
194+
command.arg(linker_argument);
195+
}
180196
}
181197
}
182198

183199
#[cfg(test)]
184200
mod tests {
185201
use super::*;
186202
use crate::predicates::*;
203+
use std::env::{remove_var, set_var};
187204

188205
#[test]
189206
fn test_run_c() {
@@ -203,6 +220,62 @@ mod tests {
203220
.success()
204221
.stdout(predicate::eq("Hello, World!\n").normalize());
205222
}
223+
#[test]
224+
fn test_run_c_ldflags() {
225+
let host = target_lexicon::HOST.to_string();
226+
let msvc = host.contains("msvc");
227+
if msvc {
228+
// Use a linker flag to set the stack size and run a program that
229+
// outputs its stack size. If the linker flags aren't set
230+
// properly, the program will output the wrong value.
231+
232+
set_var("INLINE_C_RS_LDFLAGS", "/STACK:0x40000");
233+
run(
234+
Language::C,
235+
r#"
236+
#include <Windows.h>
237+
238+
#include <stdio.h>
239+
240+
int main() {
241+
ULONG_PTR stack_low_limit, stack_high_limit;
242+
GetCurrentThreadStackLimits(&stack_low_limit, &stack_high_limit);
243+
printf("%#llx", stack_high_limit-stack_low_limit);
244+
245+
return 0;
246+
}
247+
"#,
248+
)
249+
.unwrap()
250+
.success()
251+
.stdout(predicate::eq("0x40000").normalize());
252+
253+
remove_var("INLINE_C_RS_LDFLAGS");
254+
} else {
255+
// Introduces a symbol using linker flags and builds a program that
256+
// ODR uses that symbol. If the linker flags aren't set properly,
257+
// there will be a linker error.
258+
259+
set_var("INLINE_C_RS_LDFLAGS", "--defsym MY_SYMBOL=0x0");
260+
run(
261+
Language::C,
262+
r#"
263+
#include <stdio.h>
264+
265+
extern void* MY_SYMBOL;
266+
267+
int main() {
268+
printf("%p", MY_SYMBOL);
269+
return 0;
270+
}
271+
"#,
272+
)
273+
.unwrap()
274+
.success();
275+
276+
remove_var("INLINE_C_RS_LDFLAGS");
277+
}
278+
}
206279

207280
#[test]
208281
fn test_run_cxx() {

0 commit comments

Comments
 (0)