@@ -45,7 +45,6 @@ fn write_crate_root(ir: &RulesIR, out_dir: &Path) -> PathBuf {
4545
4646fn build_rustc_args ( crate_root : & Path ) -> Vec < String > {
4747 let sysroot = get_sysroot ( ) ;
48- let build_dir = find_build_dir ( ) ;
4948
5049 let mut args = vec ! [
5150 "rustc" . to_string( ) ,
@@ -59,19 +58,19 @@ fn build_rustc_args(crate_root: &Path) -> Vec<String> {
5958 format!( "--sysroot={}" , sysroot. display( ) ) ,
6059 ] ;
6160
62- // Add -L for all out/ directories within the build dir so transitive deps
63- // are discoverable. Also support legacy flat deps/ layout.
64- let legacy_deps = build_dir
65- . parent ( )
66- . map ( |p| p . join ( "deps" ) )
67- . filter ( |p| p . is_dir ( ) ) ;
68- if let Some ( ref deps ) = legacy_deps {
69- args . push ( "-L" . to_string ( ) ) ;
70- args . push ( format ! ( "dependency={}" , deps . display ( ) ) ) ;
71- }
72- for out_dir in find_all_out_dirs ( & build_dir ) {
61+ let artifacts = read_cargo_artifacts ( ) ;
62+
63+ // Make transitive deps discoverable, as cargo does.
64+ let mut lib_dirs : Vec < & Path > = artifacts
65+ . values ( )
66+ . flatten ( )
67+ . filter_map ( |lib| lib . parent ( ) )
68+ . collect ( ) ;
69+ lib_dirs . sort ( ) ;
70+ lib_dirs . dedup ( ) ;
71+ for dir in lib_dirs {
7372 args. push ( "-L" . to_string ( ) ) ;
74- args. push ( format ! ( "dependency={}" , out_dir . display( ) ) ) ;
73+ args. push ( format ! ( "dependency={}" , dir . display( ) ) ) ;
7574 }
7675
7776 for dep in & [
@@ -84,15 +83,13 @@ fn build_rustc_args(crate_root: &Path) -> Vec<String> {
8483 "jiff" ,
8584 "xattr" ,
8685 ] {
87- if let Some ( lib) = find_artifact ( & build_dir, dep) {
88- args. push ( "--extern" . to_string ( ) ) ;
89- args. push ( format ! ( "{}={}" , dep, lib. display( ) ) ) ;
90- } else if let Some ( ref deps) = legacy_deps
91- && let Some ( lib) = find_artifact_in_dir ( deps, dep)
92- {
93- args. push ( "--extern" . to_string ( ) ) ;
94- args. push ( format ! ( "{}={}" , dep, lib. display( ) ) ) ;
95- }
86+ let lib = match artifacts. get ( * dep) . map ( Vec :: as_slice) {
87+ Some ( [ lib] ) => lib,
88+ Some ( [ ] ) | None => panic ! ( "no artifact for crate `{dep}` in cargo's build output" ) ,
89+ Some ( libs) => panic ! ( "multiple artifacts for crate `{dep}`: {libs:?}" ) ,
90+ } ;
91+ args. push ( "--extern" . to_string ( ) ) ;
92+ args. push ( format ! ( "{}={}" , dep, lib. display( ) ) ) ;
9693 }
9794
9895 args
@@ -106,78 +103,52 @@ fn get_sysroot() -> PathBuf {
106103 PathBuf :: from ( String :: from_utf8 ( output. stdout ) . unwrap ( ) . trim ( ) )
107104}
108105
109- fn find_build_dir ( ) -> PathBuf {
110- let target_dir = std:: env:: var ( "CARGO_TARGET_DIR" ) . expect ( "CARGO_TARGET_DIR must be set" ) ;
111- let profile = if cfg ! ( debug_assertions) {
112- "debug"
113- } else {
114- "release"
115- } ;
116- PathBuf :: from ( target_dir) . join ( profile) . join ( "build" )
117- }
118-
119- /// Collect all `<pkg>/<hash>/out/` directories within the build dir.
120- fn find_all_out_dirs ( build_dir : & Path ) -> Vec < PathBuf > {
121- let mut out_dirs = Vec :: new ( ) ;
122- if let Ok ( pkgs) = std:: fs:: read_dir ( build_dir) {
123- for pkg in pkgs. flatten ( ) {
124- if !pkg. file_type ( ) . map ( |t| t. is_dir ( ) ) . unwrap_or ( false ) {
125- continue ;
126- }
127- if let Ok ( hashes) = std:: fs:: read_dir ( pkg. path ( ) ) {
128- for hash_dir in hashes. flatten ( ) {
129- let out = hash_dir. path ( ) . join ( "out" ) ;
130- if out. is_dir ( ) {
131- out_dirs. push ( out) ;
132- }
133- }
134- }
135- }
136- }
137- out_dirs
138- }
139-
140- /// Find an artifact in the new Cargo build layout: build/<pkg>/<hash>/out/
141- fn find_artifact ( build_dir : & Path , crate_name : & str ) -> Option < PathBuf > {
142- // Package directory name uses hyphens where crate name uses underscores
143- let pkg_name = crate_name. replace ( '_' , "-" ) ;
144- let pkg_dir = build_dir. join ( & pkg_name) ;
145- if let Ok ( entries) = std:: fs:: read_dir ( & pkg_dir) {
146- for entry in entries. flatten ( ) {
147- let out_dir = entry. path ( ) . join ( "out" ) ;
148- if out_dir. is_dir ( )
149- && let Some ( path) = find_artifact_in_dir ( & out_dir, crate_name)
150- {
151- return Some ( path) ;
152- }
106+ /// Read the output of `cargo build --message-format=json` for this crate and
107+ /// map each library crate name to the artifacts that build produced. The
108+ /// target dir may also hold stale artifacts of the same crates (e.g., from an
109+ /// older toolchain or from `cargo clippy`), so it cannot simply be scanned.
110+ fn read_cargo_artifacts ( ) -> HashMap < String , Vec < PathBuf > > {
111+ let path = std:: env:: var ( "RULE_PREPROCESSOR_ARTIFACTS" )
112+ . expect ( "RULE_PREPROCESSOR_ARTIFACTS must be set" ) ;
113+ let contents =
114+ std:: fs:: read_to_string ( & path) . unwrap_or_else ( |e| panic ! ( "failed to read {path}: {e}" ) ) ;
115+
116+ let mut artifacts: HashMap < String , Vec < PathBuf > > = HashMap :: new ( ) ;
117+ for line in contents. lines ( ) {
118+ let Ok ( msg) = serde_json:: from_str :: < serde_json:: Value > ( line) else {
119+ continue ;
120+ } ;
121+ if msg[ "reason" ] != "compiler-artifact" {
122+ continue ;
153123 }
154- }
155- None
156- }
157-
158- /// Find an artifact by crate name in a specific directory.
159- fn find_artifact_in_dir ( dir : & Path , crate_name : & str ) -> Option < PathBuf > {
160- let prefixes = [ format ! ( "{}-" , crate_name) , format ! ( "lib{}-" , crate_name) ] ;
161- let mut fallback = None ;
162- if let Ok ( entries) = std:: fs:: read_dir ( dir) {
163- for entry in entries. flatten ( ) {
164- let name = entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
165- if !prefixes. iter ( ) . any ( |p| name. starts_with ( p) ) {
166- continue ;
167- }
168- if name. ends_with ( ".rmeta" )
169- || name. ends_with ( ".so" )
170- || name. ends_with ( ".dylib" )
171- || name. ends_with ( ".dll" )
172- {
173- return Some ( entry. path ( ) ) ;
174- }
175- if name. ends_with ( ".rlib" ) {
176- fallback = Some ( entry. path ( ) ) ;
177- }
124+ let Some ( name) = msg[ "target" ] [ "name" ] . as_str ( ) else {
125+ continue ;
126+ } ;
127+ let Some ( filenames) = msg[ "filenames" ] . as_array ( ) else {
128+ continue ;
129+ } ;
130+ let filenames: Vec < PathBuf > = filenames
131+ . iter ( )
132+ . filter_map ( |f| f. as_str ( ) )
133+ . map ( PathBuf :: from)
134+ . collect ( ) ;
135+ let find = |ext : & str | {
136+ filenames
137+ . iter ( )
138+ . find ( |f| f. extension ( ) . is_some_and ( |e| e == ext) )
139+ } ;
140+ let Some ( lib) = find ( "rmeta" )
141+ . or_else ( || find ( "rlib" ) )
142+ . or_else ( || find ( std:: env:: consts:: DLL_EXTENSION ) )
143+ else {
144+ continue ;
145+ } ;
146+ let libs = artifacts. entry ( name. to_string ( ) ) . or_default ( ) ;
147+ if !libs. contains ( lib) {
148+ libs. push ( lib. clone ( ) ) ;
178149 }
179150 }
180- fallback
151+ artifacts
181152}
182153
183154struct FnDecl < ' tcx > {
0 commit comments