44//! They also attempt a C compile step if `cc` is available on PATH.
55
66use std:: path:: Path ;
7- use std:: process:: Command ;
87
98use tempfile:: TempDir ;
109
@@ -16,61 +15,60 @@ fn gloam() -> assert_cmd::Command {
1615 assert_cmd:: Command :: cargo_bin ( "gloam" ) . expect ( "gloam binary not found" )
1716}
1817
19- /// Attempt to compile a generated C source with the system C compiler.
20- /// Silently skips if `cc` is not on PATH (expected in CI, optional locally).
18+ /// Attempt to compile generated C sources with the system C compiler.
19+ /// Uses the `cc` crate for compiler detection (handles MSVC, GCC, Clang,
20+ /// cross-compilation toolchains, CC env override, etc.).
21+ /// Silently skips if no compiler is available.
2122fn try_compile_c ( out : & Path ) {
22- // Find a .c file in out/src/.
2323 let src_dir = out. join ( "src" ) ;
24- let c_file = match std:: fs:: read_dir ( & src_dir) . ok ( ) . and_then ( |mut d| {
25- d. find ( |e| {
26- e. as_ref ( )
27- . is_ok_and ( |e| e. path ( ) . extension ( ) == Some ( "c" . as_ref ( ) ) )
28- } )
29- } ) {
30- Some ( Ok ( entry) ) => entry. path ( ) ,
31- _ => return , // nothing to compile
32- } ;
33-
34- let cc = match find_cc ( ) {
35- Some ( c) => c,
36- None => {
37- eprintln ! ( "compile check skipped: no C compiler on PATH" ) ;
38- return ;
24+ let c_files: Vec < _ > = std:: fs:: read_dir ( & src_dir)
25+ . ok ( )
26+ . into_iter ( )
27+ . flatten ( )
28+ . filter_map ( |e| e. ok ( ) )
29+ . map ( |e| e. path ( ) )
30+ . filter ( |p| p. extension ( ) == Some ( "c" . as_ref ( ) ) )
31+ . collect ( ) ;
32+
33+ if c_files. is_empty ( ) {
34+ return ;
35+ }
36+
37+ // The cc crate expects TARGET/HOST env vars (normally set by Cargo during
38+ // build.rs). In test context they're absent, so provide them.
39+ let target = env ! ( "TARGET" ) ;
40+ let mut build = cc:: Build :: new ( ) ;
41+ build
42+ . target ( target)
43+ . host ( target)
44+ . opt_level ( 0 )
45+ . out_dir ( & src_dir)
46+ . include ( out. join ( "include" ) )
47+ . warnings ( true )
48+ . cargo_warnings ( false )
49+ . std ( "c11" )
50+ . flag_if_supported ( "-Wno-unused-function" ) ;
51+
52+ for f in & c_files {
53+ build. file ( f) ;
54+ }
55+
56+ if let Err ( e) = build. try_compile ( "gloam_test" ) {
57+ // Distinguish "no compiler" from "compilation failed".
58+ let msg = e. to_string ( ) ;
59+ if msg. contains ( "Failed to find tool" )
60+ || msg. contains ( "not found" )
61+ || msg. contains ( "couldn't find" )
62+ {
63+ eprintln ! ( "compile check skipped: no C compiler found" ) ;
64+ } else {
65+ panic ! (
66+ "generated C files in {} failed to compile: {}" ,
67+ src_dir. display( ) ,
68+ e
69+ ) ;
3970 }
40- } ;
41-
42- let status = Command :: new ( cc)
43- . args ( [
44- "-c" ,
45- "-std=c11" ,
46- "-Wall" ,
47- "-Wno-unused-function" ,
48- "-o" ,
49- "/dev/null" ,
50- & format ! ( "-I{}" , out. join( "include" ) . display( ) ) ,
51- c_file. to_str ( ) . unwrap ( ) ,
52- ] )
53- . status ( )
54- . expect ( "failed to spawn C compiler" ) ;
55-
56- assert ! (
57- status. success( ) ,
58- "generated C file {} failed to compile" ,
59- c_file. display( )
60- ) ;
61- }
62-
63- fn find_cc ( ) -> Option < & ' static str > {
64- [ "cc" , "gcc" , "clang" ]
65- . iter ( )
66- . find ( |& candidate| {
67- Command :: new ( candidate)
68- . arg ( "--version" )
69- . output ( )
70- . map ( |o| o. status . success ( ) )
71- . unwrap_or ( false )
72- } )
73- . map ( |v| v as _ )
71+ }
7472}
7573
7674fn assert_c_output_exists ( out : & Path , stem : & str ) {
0 commit comments