@@ -58,28 +58,32 @@ impl PartialEq for BuildDataConfig {
5858
5959impl Eq for BuildDataConfig { }
6060
61- #[ derive( Debug , Default ) ]
61+ #[ derive( Debug ) ]
6262pub struct BuildDataCollector {
63+ wrap_rustc : bool ,
6364 configs : FxHashMap < AbsPathBuf , BuildDataConfig > ,
6465}
6566
6667impl BuildDataCollector {
68+ pub fn new ( wrap_rustc : bool ) -> Self {
69+ Self { wrap_rustc, configs : FxHashMap :: default ( ) }
70+ }
71+
6772 pub ( crate ) fn add_config ( & mut self , workspace_root : & AbsPath , config : BuildDataConfig ) {
6873 self . configs . insert ( workspace_root. to_path_buf ( ) , config) ;
6974 }
7075
7176 pub fn collect ( & mut self , progress : & dyn Fn ( String ) ) -> Result < BuildDataResult > {
7277 let mut res = BuildDataResult :: default ( ) ;
7378 for ( path, config) in self . configs . iter ( ) {
74- res. per_workspace . insert (
75- path. clone ( ) ,
76- collect_from_workspace (
77- & config. cargo_toml ,
78- & config. cargo_features ,
79- & config. packages ,
80- progress,
81- ) ?,
82- ) ;
79+ let workspace_build_data = WorkspaceBuildData :: collect (
80+ & config. cargo_toml ,
81+ & config. cargo_features ,
82+ & config. packages ,
83+ self . wrap_rustc ,
84+ progress,
85+ ) ?;
86+ res. per_workspace . insert ( path. clone ( ) , workspace_build_data) ;
8387 }
8488 Ok ( res)
8589 }
@@ -120,119 +124,137 @@ impl BuildDataConfig {
120124 }
121125}
122126
123- fn collect_from_workspace (
124- cargo_toml : & AbsPath ,
125- cargo_features : & CargoConfig ,
126- packages : & Vec < cargo_metadata:: Package > ,
127- progress : & dyn Fn ( String ) ,
128- ) -> Result < WorkspaceBuildData > {
129- let mut cmd = Command :: new ( toolchain:: cargo ( ) ) ;
130- cmd. args ( & [ "check" , "--workspace" , "--message-format=json" , "--manifest-path" ] )
131- . arg ( cargo_toml. as_ref ( ) ) ;
132-
133- // --all-targets includes tests, benches and examples in addition to the
134- // default lib and bins. This is an independent concept from the --targets
135- // flag below.
136- cmd. arg ( "--all-targets" ) ;
137-
138- if let Some ( target) = & cargo_features. target {
139- cmd. args ( & [ "--target" , target] ) ;
140- }
127+ impl WorkspaceBuildData {
128+ fn collect (
129+ cargo_toml : & AbsPath ,
130+ cargo_features : & CargoConfig ,
131+ packages : & Vec < cargo_metadata:: Package > ,
132+ wrap_rustc : bool ,
133+ progress : & dyn Fn ( String ) ,
134+ ) -> Result < WorkspaceBuildData > {
135+ let mut cmd = Command :: new ( toolchain:: cargo ( ) ) ;
136+
137+ if wrap_rustc {
138+ // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
139+ // that to compile only proc macros and build scripts during the initial
140+ // `cargo check`.
141+ let myself = std:: env:: current_exe ( ) ?;
142+ cmd. env ( "RUSTC_WRAPPER" , myself) ;
143+ cmd. env ( "RA_RUSTC_WRAPPER" , "1" ) ;
144+ }
145+
146+ cmd. args ( & [ "check" , "--workspace" , "--message-format=json" , "--manifest-path" ] )
147+ . arg ( cargo_toml. as_ref ( ) ) ;
141148
142- if cargo_features . all_features {
143- cmd . arg ( "--all-features" ) ;
144- } else {
145- if cargo_features . no_default_features {
146- // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
147- // https://github.com/oli-obk/cargo_metadata/issues/79
148- cmd. arg ( "--no-default-features" ) ;
149+ // --all-targets includes tests, benches and examples in addition to the
150+ // default lib and bins. This is an independent concept from the --targets
151+ // flag below.
152+ cmd . arg ( "--all-targets" ) ;
153+
154+ if let Some ( target ) = & cargo_features . target {
155+ cmd. args ( & [ "--target" , target ] ) ;
149156 }
150- if !cargo_features. features . is_empty ( ) {
151- cmd. arg ( "--features" ) ;
152- cmd. arg ( cargo_features. features . join ( " " ) ) ;
157+
158+ if cargo_features. all_features {
159+ cmd. arg ( "--all-features" ) ;
160+ } else {
161+ if cargo_features. no_default_features {
162+ // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
163+ // https://github.com/oli-obk/cargo_metadata/issues/79
164+ cmd. arg ( "--no-default-features" ) ;
165+ }
166+ if !cargo_features. features . is_empty ( ) {
167+ cmd. arg ( "--features" ) ;
168+ cmd. arg ( cargo_features. features . join ( " " ) ) ;
169+ }
153170 }
154- }
155171
156- cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) . stdin ( Stdio :: null ( ) ) ;
157-
158- let mut child = cmd. spawn ( ) . map ( JodChild ) ?;
159- let child_stdout = child. stdout . take ( ) . unwrap ( ) ;
160- let stdout = BufReader :: new ( child_stdout) ;
161-
162- let mut res = WorkspaceBuildData :: default ( ) ;
163- for message in cargo_metadata:: Message :: parse_stream ( stdout) . flatten ( ) {
164- match message {
165- Message :: BuildScriptExecuted ( BuildScript {
166- package_id, out_dir, cfgs, env, ..
167- } ) => {
168- let cfgs = {
169- let mut acc = Vec :: new ( ) ;
170- for cfg in cfgs {
171- match cfg. parse :: < CfgFlag > ( ) {
172- Ok ( it) => acc. push ( it) ,
173- Err ( err) => {
174- anyhow:: bail!( "invalid cfg from cargo-metadata: {}" , err)
175- }
176- } ;
172+ cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) . stdin ( Stdio :: null ( ) ) ;
173+
174+ let mut child = cmd. spawn ( ) . map ( JodChild ) ?;
175+ let child_stdout = child. stdout . take ( ) . unwrap ( ) ;
176+ let stdout = BufReader :: new ( child_stdout) ;
177+
178+ let mut res = WorkspaceBuildData :: default ( ) ;
179+ for message in cargo_metadata:: Message :: parse_stream ( stdout) . flatten ( ) {
180+ match message {
181+ Message :: BuildScriptExecuted ( BuildScript {
182+ package_id,
183+ out_dir,
184+ cfgs,
185+ env,
186+ ..
187+ } ) => {
188+ let cfgs = {
189+ let mut acc = Vec :: new ( ) ;
190+ for cfg in cfgs {
191+ match cfg. parse :: < CfgFlag > ( ) {
192+ Ok ( it) => acc. push ( it) ,
193+ Err ( err) => {
194+ anyhow:: bail!( "invalid cfg from cargo-metadata: {}" , err)
195+ }
196+ } ;
197+ }
198+ acc
199+ } ;
200+ let package_build_data =
201+ res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
202+ // cargo_metadata crate returns default (empty) path for
203+ // older cargos, which is not absolute, so work around that.
204+ if !out_dir. as_str ( ) . is_empty ( ) {
205+ let out_dir = AbsPathBuf :: assert ( PathBuf :: from ( out_dir. into_os_string ( ) ) ) ;
206+ package_build_data. out_dir = Some ( out_dir) ;
207+ package_build_data. cfgs = cfgs;
177208 }
178- acc
179- } ;
180- let package_build_data =
181- res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
182- // cargo_metadata crate returns default (empty) path for
183- // older cargos, which is not absolute, so work around that.
184- if !out_dir. as_str ( ) . is_empty ( ) {
185- let out_dir = AbsPathBuf :: assert ( PathBuf :: from ( out_dir. into_os_string ( ) ) ) ;
186- package_build_data. out_dir = Some ( out_dir) ;
187- package_build_data. cfgs = cfgs;
188- }
189209
190- package_build_data. envs = env;
191- }
192- Message :: CompilerArtifact ( message) => {
193- progress ( format ! ( "metadata {}" , message. target. name) ) ;
194-
195- if message. target . kind . contains ( & "proc-macro" . to_string ( ) ) {
196- let package_id = message. package_id ;
197- // Skip rmeta file
198- if let Some ( filename) = message. filenames . iter ( ) . find ( |name| is_dylib ( name) ) {
199- let filename = AbsPathBuf :: assert ( PathBuf :: from ( & filename) ) ;
200- let package_build_data =
201- res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
202- package_build_data. proc_macro_dylib_path = Some ( filename) ;
210+ package_build_data. envs = env;
211+ }
212+ Message :: CompilerArtifact ( message) => {
213+ progress ( format ! ( "metadata {}" , message. target. name) ) ;
214+
215+ if message. target . kind . contains ( & "proc-macro" . to_string ( ) ) {
216+ let package_id = message. package_id ;
217+ // Skip rmeta file
218+ if let Some ( filename) = message. filenames . iter ( ) . find ( |name| is_dylib ( name) )
219+ {
220+ let filename = AbsPathBuf :: assert ( PathBuf :: from ( & filename) ) ;
221+ let package_build_data =
222+ res. per_package . entry ( package_id. repr . clone ( ) ) . or_default ( ) ;
223+ package_build_data. proc_macro_dylib_path = Some ( filename) ;
224+ }
203225 }
204226 }
227+ Message :: CompilerMessage ( message) => {
228+ progress ( message. target . name . clone ( ) ) ;
229+ }
230+ Message :: BuildFinished ( _) => { }
231+ Message :: TextLine ( _) => { }
232+ _ => { }
205233 }
206- Message :: CompilerMessage ( message) => {
207- progress ( message. target . name . clone ( ) ) ;
208- }
209- Message :: BuildFinished ( _) => { }
210- Message :: TextLine ( _) => { }
211- _ => { }
212234 }
213- }
214235
215- for package in packages {
216- let package_build_data = res. per_package . entry ( package. id . repr . clone ( ) ) . or_default ( ) ;
217- inject_cargo_env ( package, package_build_data) ;
218- if let Some ( out_dir) = & package_build_data. out_dir {
219- // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
220- if let Some ( out_dir) = out_dir. to_str ( ) . map ( |s| s. to_owned ( ) ) {
221- package_build_data. envs . push ( ( "OUT_DIR" . to_string ( ) , out_dir) ) ;
236+ for package in packages {
237+ let package_build_data = res. per_package . entry ( package. id . repr . clone ( ) ) . or_default ( ) ;
238+ inject_cargo_env ( package, package_build_data) ;
239+ if let Some ( out_dir) = & package_build_data. out_dir {
240+ // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
241+ if let Some ( out_dir) = out_dir. to_str ( ) . map ( |s| s. to_owned ( ) ) {
242+ package_build_data. envs . push ( ( "OUT_DIR" . to_string ( ) , out_dir) ) ;
243+ }
222244 }
223245 }
224- }
225246
226- let output = child. into_inner ( ) . wait_with_output ( ) ?;
227- if !output. status . success ( ) {
228- let mut stderr = String :: from_utf8 ( output. stderr ) . unwrap_or_default ( ) ;
229- if stderr. is_empty ( ) {
230- stderr = "cargo check failed" . to_string ( ) ;
247+ let output = child. into_inner ( ) . wait_with_output ( ) ?;
248+ if !output. status . success ( ) {
249+ let mut stderr = String :: from_utf8 ( output. stderr ) . unwrap_or_default ( ) ;
250+ if stderr. is_empty ( ) {
251+ stderr = "cargo check failed" . to_string ( ) ;
252+ }
253+ res. error = Some ( stderr)
231254 }
232- res. error = Some ( stderr)
233- }
234255
235- Ok ( res)
256+ Ok ( res)
257+ }
236258}
237259
238260// FIXME: File a better way to know if it is a dylib
0 commit comments