| 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 | |