1
1
use color_eyre:: eyre:: { bail, Result } ;
2
2
use std:: {
3
+ collections:: { HashMap , HashSet } ,
3
4
path:: { Path , PathBuf } ,
4
5
process:: Command ,
5
6
} ;
6
7
7
8
use crate :: Config ;
8
9
10
+ #[ derive( Default , Debug ) ]
11
+ pub struct Dependencies {
12
+ /// All paths that must be imported with `-L dependency=`. This is for
13
+ /// finding proc macros run on the host and dependencies for the target.
14
+ pub import_paths : Vec < PathBuf > ,
15
+ /// The name as chosen in the `Cargo.toml` and its corresponding rmeta file.
16
+ pub dependencies : Vec < ( String , PathBuf ) > ,
17
+ }
18
+
9
19
/// Compiles dependencies and returns the crate names and corresponding rmeta files.
10
- pub fn build_dependencies ( config : & Config ) -> Result < Vec < ( String , PathBuf ) > > {
20
+ pub fn build_dependencies ( config : & Config ) -> Result < Dependencies > {
11
21
let manifest_path = match & config. dependencies_crate_manifest_path {
12
22
Some ( path) => path,
13
- None => return Ok ( vec ! [ ] ) ,
23
+ None => return Ok ( Default :: default ( ) ) ,
14
24
} ;
15
25
let ( program, args, envs) : ( & Path , & [ _ ] , & [ _ ] ) = match & config. dependency_builder {
16
26
Some ( db) => ( & db. program , & db. args , & db. envs ) ,
17
27
None => ( Path :: new ( "cargo" ) , & [ ] , & [ ] ) ,
18
28
} ;
19
29
let mut build = Command :: new ( program) ;
20
-
21
- // Avoid poisoning the target directory and causing unnecessary rebuilds.
22
- build. env_remove ( "RUSTFLAGS" ) ;
23
-
24
- build. envs ( envs. iter ( ) . map ( |( k, v) | ( k, v) ) ) ;
25
30
build. args ( args) ;
26
31
build. arg ( "run" ) ;
27
- if let Some ( target) = & config. target {
28
- build. arg ( format ! ( "--target={target}" ) ) ;
29
- }
32
+
33
+ // Reusable closure for setting up the environment both for artifact generation and `cargo_metadata`
34
+ let setup_command = |cmd : & mut Command | {
35
+ // Avoid poisoning the target directory and causing unnecessary rebuilds.
36
+ cmd. env_remove ( "RUSTFLAGS" ) ;
37
+
38
+ cmd. envs ( envs. iter ( ) . map ( |( k, v) | ( k, v) ) ) ;
39
+ if let Some ( target) = & config. target {
40
+ cmd. arg ( format ! ( "--target={target}" ) ) ;
41
+ }
42
+ cmd. arg ( format ! ( "--manifest-path={}" , manifest_path. display( ) ) ) ;
43
+ } ;
44
+
45
+ setup_command ( & mut build) ;
30
46
build
31
- . arg ( format ! ( "--manifest-path={}" , manifest_path. display( ) ) )
32
47
. arg ( "--target-dir=target/test_dependencies" )
33
48
. arg ( "--message-format=json" )
34
49
. arg ( "-Zunstable-options" ) ;
@@ -41,24 +56,79 @@ pub fn build_dependencies(config: &Config) -> Result<Vec<(String, PathBuf)>> {
41
56
bail ! ( "failed to compile dependencies:\n stderr:\n {stderr}\n \n stdout:{stdout}" ) ;
42
57
}
43
58
59
+ // Collect all artifacts generated
44
60
let output = output. stdout ;
45
61
let output = String :: from_utf8 ( output) ?;
46
- Ok ( output
62
+ let mut import_paths: HashSet < PathBuf > = HashSet :: new ( ) ;
63
+ let mut artifacts: HashMap < _ , _ > = output
47
64
. lines ( )
48
65
. filter_map ( |line| {
49
66
let message = serde_json:: from_str :: < cargo_metadata:: Message > ( line) . ok ( ) ?;
50
67
if let cargo_metadata:: Message :: CompilerArtifact ( artifact) = message {
68
+ for filename in & artifact. filenames {
69
+ import_paths. insert ( filename. parent ( ) . unwrap ( ) . into ( ) ) ;
70
+ }
51
71
let filename = artifact
52
72
. filenames
53
73
. into_iter ( )
54
74
. find ( |filename| filename. extension ( ) == Some ( "rmeta" ) ) ?;
55
- Some ( (
56
- artifact. package_id . repr . split_once ( ' ' ) . unwrap ( ) . 0 . to_string ( ) ,
57
- filename. into_std_path_buf ( ) ,
58
- ) )
75
+ Some ( ( artifact. package_id , filename. into_std_path_buf ( ) ) )
59
76
} else {
60
77
None
61
78
}
62
79
} )
63
- . collect ( ) )
80
+ . collect ( ) ;
81
+
82
+ // Check which crates are mentioned in the crate itself
83
+ let mut metadata = cargo_metadata:: MetadataCommand :: new ( ) . cargo_command ( ) ;
84
+ setup_command ( & mut metadata) ;
85
+ let output = metadata. output ( ) ?;
86
+
87
+ if !output. status . success ( ) {
88
+ let stdout = String :: from_utf8 ( output. stdout ) ?;
89
+ let stderr = String :: from_utf8 ( output. stderr ) ?;
90
+ bail ! ( "failed to run cargo-metadata:\n stderr:\n {stderr}\n \n stdout:{stdout}" ) ;
91
+ }
92
+
93
+ let output = output. stdout ;
94
+ let output = String :: from_utf8 ( output) ?;
95
+
96
+ for line in output. lines ( ) {
97
+ if !line. starts_with ( '{' ) {
98
+ continue ;
99
+ }
100
+ let metadata: cargo_metadata:: Metadata = serde_json:: from_str ( line) ?;
101
+ // Only take artifacts that are defined in the Cargo.toml
102
+
103
+ // First, find the root artifact
104
+ let root = metadata
105
+ . packages
106
+ . iter ( )
107
+ . find ( |package| package. manifest_path . as_std_path ( ) == manifest_path)
108
+ . unwrap ( ) ;
109
+
110
+ // Then go over all of its dependencies
111
+ let dependencies = root
112
+ . dependencies
113
+ . iter ( )
114
+ . map ( |package| {
115
+ // Get the id for the package matching the version requirement of the dep
116
+ let id = & metadata
117
+ . packages
118
+ . iter ( )
119
+ . find ( |& dep| dep. name == package. name && package. req . matches ( & dep. version ) )
120
+ . expect ( "dependency does not exist" )
121
+ . id ;
122
+ // Return the name chosen in `Cargo.toml` and the path to the corresponding artifact
123
+ (
124
+ package. rename . clone ( ) . unwrap_or_else ( || package. name . clone ( ) ) ,
125
+ artifacts. remove ( id) . expect ( "package without artifact" ) ,
126
+ )
127
+ } )
128
+ . collect ( ) ;
129
+ let import_paths = import_paths. into_iter ( ) . collect ( ) ;
130
+ return Ok ( Dependencies { dependencies, import_paths } ) ;
131
+ }
132
+
133
+ bail ! ( "no json found in cargo-metadata output" )
64
134
}
0 commit comments