1 | //! Everything needed to build auxilary files with rustc |
2 | // lol we can't name this file `aux.rs` on windows |
3 | |
4 | use crate::{ |
5 | build_manager::{Build, BuildManager}, |
6 | custom_flags::Flag, |
7 | default_per_file_config, display, |
8 | per_test_config::{Comments, TestConfig}, |
9 | status_emitter::SilentStatus, |
10 | CrateType, Error, Errored, |
11 | }; |
12 | use bstr::ByteSlice; |
13 | use spanned::Spanned; |
14 | use std::{ffi::OsString, path::PathBuf, process::Command, sync::Arc}; |
15 | |
16 | impl Flag for AuxBuilder { |
17 | fn must_be_unique(&self) -> bool { |
18 | false |
19 | } |
20 | fn clone_inner(&self) -> Box<dyn Flag> { |
21 | Box::new(self.clone()) |
22 | } |
23 | fn apply( |
24 | &self, |
25 | cmd: &mut Command, |
26 | config: &TestConfig, |
27 | build_manager: &BuildManager, |
28 | ) -> Result<(), Errored> { |
29 | let aux = &self.aux_file; |
30 | let aux_dir = config.aux_dir.clone(); |
31 | let aux_file = if aux.starts_with(".." ) { |
32 | aux_dir.parent().unwrap().join(&aux.content) |
33 | } else { |
34 | aux_dir.join(&aux.content) |
35 | }; |
36 | let extra_args = build_manager |
37 | .build( |
38 | AuxBuilder { |
39 | aux_file: Spanned::new( |
40 | crate::core::strip_path_prefix( |
41 | &aux_file.canonicalize().map_err(|err| Errored { |
42 | command: format!("canonicalizing path ` {}`" , display(&aux_file)), |
43 | errors: vec![], |
44 | stderr: err.to_string().into_bytes(), |
45 | stdout: vec![], |
46 | })?, |
47 | &std::env::current_dir().unwrap(), |
48 | ) |
49 | .collect(), |
50 | aux.span(), |
51 | ), |
52 | }, |
53 | &config.status, |
54 | ) |
55 | .map_err( |
56 | |Errored { |
57 | command, |
58 | errors, |
59 | stderr, |
60 | stdout, |
61 | }| Errored { |
62 | command, |
63 | errors: vec![Error::Aux { |
64 | path: Spanned::new(aux_file.to_path_buf(), aux.span()), |
65 | errors, |
66 | }], |
67 | stderr, |
68 | stdout, |
69 | }, |
70 | )?; |
71 | cmd.args(extra_args); |
72 | Ok(()) |
73 | } |
74 | } |
75 | |
76 | /// Build an aux-build. |
77 | /// Custom `//@aux-build` flag handler. |
78 | #[derive (Clone, Debug)] |
79 | pub struct AuxBuilder { |
80 | /// Full path to the file (including `auxiliary` folder prefix) |
81 | pub aux_file: Spanned<PathBuf>, |
82 | } |
83 | |
84 | impl Build for AuxBuilder { |
85 | fn build(&self, build_manager: &BuildManager) -> Result<Vec<OsString>, Errored> { |
86 | let mut config = build_manager.config().clone(); |
87 | let file_contents = |
88 | Spanned::read_from_file(&self.aux_file.content).map_err(|err| Errored { |
89 | command: format!("reading aux file ` {}`" , display(&self.aux_file)), |
90 | errors: vec![], |
91 | stderr: err.to_string().into_bytes(), |
92 | stdout: vec![], |
93 | })?; |
94 | let comments = Comments::parse(file_contents.as_ref(), &config) |
95 | .map_err(|errors| Errored::new(errors, "parse aux comments" ))?; |
96 | assert_eq!( |
97 | comments.revisions, None, |
98 | "aux builds cannot specify revisions" |
99 | ); |
100 | |
101 | default_per_file_config(&mut config, &file_contents); |
102 | |
103 | match CrateType::from_file_contents(&file_contents) { |
104 | // Proc macros must be run on the host |
105 | CrateType::ProcMacro => config.target.clone_from(&config.host), |
106 | CrateType::Test | CrateType::Bin | CrateType::Lib => {} |
107 | } |
108 | |
109 | let mut config = TestConfig { |
110 | config, |
111 | comments: Arc::new(comments), |
112 | aux_dir: self.aux_file.parent().unwrap().to_owned(), |
113 | status: Box::new(SilentStatus { |
114 | revision: String::new(), |
115 | path: self.aux_file.content.clone(), |
116 | }), |
117 | }; |
118 | |
119 | config.patch_out_dir(); |
120 | |
121 | let mut aux_cmd = config.build_command(build_manager)?; |
122 | |
123 | aux_cmd.arg("--emit=link" ); |
124 | let filename = self.aux_file.file_stem().unwrap().to_str().unwrap(); |
125 | let output = config.config.run_command(&mut aux_cmd)?; |
126 | if !output.status.success() { |
127 | let error = Error::Command { |
128 | kind: "compilation of aux build failed" .to_string(), |
129 | status: output.status, |
130 | }; |
131 | return Err(Errored { |
132 | command: format!(" {aux_cmd:?}" ), |
133 | errors: vec![error], |
134 | stderr: config.process(&output.stderr).rendered, |
135 | stdout: output.stdout, |
136 | }); |
137 | } |
138 | |
139 | // Now run the command again to fetch the output filenames |
140 | aux_cmd.arg("--print" ).arg("file-names" ); |
141 | let output = config.config.run_command(&mut aux_cmd)?; |
142 | |
143 | assert!(output.status.success()); |
144 | |
145 | let mut extra_args = vec![]; |
146 | for file in output.stdout.lines() { |
147 | let file = std::str::from_utf8(file).unwrap(); |
148 | let crate_name = filename.replace('-' , "_" ); |
149 | let path = config.config.out_dir.join(file); |
150 | extra_args.push("--extern" .into()); |
151 | let mut cname = OsString::from(&crate_name); |
152 | cname.push("=" ); |
153 | cname.push(path); |
154 | extra_args.push(cname); |
155 | // Help cargo find the crates added with `--extern`. |
156 | extra_args.push("-L" .into()); |
157 | extra_args.push(config.config.out_dir.as_os_str().to_os_string()); |
158 | } |
159 | Ok(extra_args) |
160 | } |
161 | |
162 | fn description(&self) -> String { |
163 | format!("Building aux file {}" , display(&self.aux_file)) |
164 | } |
165 | } |
166 | |