11pub mod command;
2+ mod graph;
23
34use core:: fmt;
5+ use std:: ffi:: OsStr ;
46use std:: io:: { BufRead , BufReader } ;
57use std:: path:: { Path , PathBuf } ;
68use std:: process:: { Command , ExitStatus , Stdio } ;
@@ -13,6 +15,7 @@ use semver::Version;
1315use tee:: TeeReader ;
1416
1517use crate :: command:: { CargoCmd , Run } ;
18+ use crate :: graph:: UnitGraph ;
1619
1720/// Build a command using [`make_cargo_build_command`] and execute it,
1821/// parsing and returning the messages from the spawned process.
@@ -22,6 +25,23 @@ use crate::command::{CargoCmd, Run};
2225pub fn run_cargo ( input : & Input , message_format : Option < String > ) -> ( ExitStatus , Vec < Message > ) {
2326 let mut command = make_cargo_command ( input, & message_format) ;
2427
28+ let libctru = if should_use_ctru_debuginfo ( & command, input. verbose ) {
29+ "ctrud"
30+ } else {
31+ "ctru"
32+ } ;
33+
34+ let rustflags = command
35+ . get_envs ( )
36+ . find ( |( var, _) | var == & OsStr :: new ( "RUSTFLAGS" ) )
37+ . and_then ( |( _, flags) | flags)
38+ . unwrap_or_default ( )
39+ . to_string_lossy ( ) ;
40+
41+ let rustflags = format ! ( "{rustflags} -l{libctru}" ) ;
42+
43+ command. env ( "RUSTFLAGS" , rustflags) ;
44+
2545 if input. verbose {
2646 print_command ( & command) ;
2747 }
@@ -57,27 +77,51 @@ pub fn run_cargo(input: &Input, message_format: Option<String>) -> (ExitStatus,
5777 ( process. wait ( ) . unwrap ( ) , messages)
5878}
5979
80+ /// Ensure that we use the same `-lctru[d]` flag that `ctru-sys` is using in its build.
81+ fn should_use_ctru_debuginfo ( cargo_cmd : & Command , verbose : bool ) -> bool {
82+ match UnitGraph :: from_cargo ( cargo_cmd, verbose) {
83+ Ok ( unit_graph) => {
84+ let Some ( unit) = unit_graph
85+ . units
86+ . iter ( )
87+ . find ( |unit| unit. target . name == "ctru-sys" )
88+ else {
89+ eprintln ! ( "Warning: unable to check if `ctru` debuginfo should be linked: `ctru-sys` not found" ) ;
90+ return false ;
91+ } ;
92+
93+ let debuginfo = unit. profile . debuginfo . unwrap_or ( 0 ) ;
94+ debuginfo > 0
95+ }
96+ Err ( err) => {
97+ eprintln ! ( "Warning: unable to check if `ctru` debuginfo should be linked: {err}" ) ;
98+ false
99+ }
100+ }
101+ }
102+
60103/// Create a cargo command based on the context.
61104///
62105/// For "build" commands (which compile code, such as `cargo 3ds build` or `cargo 3ds clippy`),
63106/// if there is no pre-built std detected in the sysroot, `build-std` will be used instead.
64107pub fn make_cargo_command ( input : & Input , message_format : & Option < String > ) -> Command {
108+ let devkitpro =
109+ env:: var ( "DEVKITPRO" ) . expect ( "DEVKITPRO is not defined as an environment variable" ) ;
110+ // TODO: should we actually prepend the user's RUSTFLAGS for linking order? not sure
111+ let rustflags =
112+ env:: var ( "RUSTFLAGS" ) . unwrap_or_default ( ) + & format ! ( " -L{devkitpro}/libctru/lib" ) ;
113+
65114 let cargo_cmd = & input. cmd ;
66115
67116 let mut command = cargo ( & input. config ) ;
68- command. arg ( cargo_cmd. subcommand_name ( ) ) ;
117+ command
118+ . arg ( cargo_cmd. subcommand_name ( ) )
119+ . env ( "RUSTFLAGS" , rustflags) ;
69120
70121 // Any command that needs to compile code will run under this environment.
71122 // Even `clippy` and `check` need this kind of context, so we'll just assume any other `Passthrough` command uses it too.
72123 if cargo_cmd. should_compile ( ) {
73- let rust_flags = env:: var ( "RUSTFLAGS" ) . unwrap_or_default ( )
74- + & format ! (
75- " -L{}/libctru/lib -lctru" ,
76- env:: var( "DEVKITPRO" ) . expect( "DEVKITPRO is not defined as an environment variable" )
77- ) ;
78-
79124 command
80- . env ( "RUSTFLAGS" , rust_flags)
81125 . arg ( "--target" )
82126 . arg ( "armv6k-nintendo-3ds" )
83127 . arg ( "--message-format" )
0 commit comments