machine_check_compile/
prepare.rs1use cargo_metadata::{camino::Utf8PathBuf, Message};
2use log::info;
3use std::{collections::BTreeSet, io::Write};
4use std::{
5 fs::File,
6 io::BufWriter,
7 process::{Command, Stdio},
8};
9
10use serde::{Deserialize, Serialize};
11
12use crate::features::add_cargo_features;
13use crate::util::{log_process_error_log, log_process_output};
14use crate::Error;
15
16#[derive(Debug, Serialize, Deserialize)]
17pub(crate) struct Preparation {
18 pub target_build_args: Vec<String>,
19}
20
21pub fn default_preparation_dir() -> Result<Utf8PathBuf, Error> {
22 let mut path = std::env::current_exe().map_err(Error::CurrentExe)?;
24 path.pop();
25 let path = Utf8PathBuf::try_from(path.clone()).map_err(|err| Error::PathToUtf8(path, err))?;
26 Ok(path.join("machine-check-preparation"))
27}
28
29pub struct Config {
30 pub preparation_path: Option<Utf8PathBuf>,
31 pub clean: bool,
32}
33
34pub fn prepare(config: Config) -> Result<(), Error> {
35 let preparation_dir = match config.preparation_path {
36 Some(preparation_path) => preparation_path,
37 None => {
38 default_preparation_dir()?
40 }
41 };
42
43 if config.clean {
44 info!(
45 "Cleaning preparation by removing directory {:?}.",
46 preparation_dir
47 );
48 std::fs::remove_dir_all(preparation_dir.clone())
49 .map_err(|err| Error::RemoveDirAll(preparation_dir, err))?;
50 return Ok(());
51 }
52
53 info!(
54 "Preparing sub-artifacts for machine executable building into {:?}.",
55 preparation_dir
56 );
57
58 let src_dir_path = preparation_dir.join("src");
59 std::fs::create_dir_all(&src_dir_path)
60 .map_err(|err| Error::CreateDir(src_dir_path.clone(), err))?;
61 let lib_path = src_dir_path.join("lib.rs");
62
63 std::fs::write(lib_path.clone(), "").map_err(|err| Error::WriteFile(lib_path, err))?;
64
65 let cargo_toml = include_str!("../resources/Prepare_Cargo.toml");
66 let cargo_toml_path = preparation_dir.join("Cargo.toml");
67 std::fs::write(&cargo_toml_path, cargo_toml)
68 .map_err(|err| Error::WriteFile(cargo_toml_path.clone(), err))?;
69
70 let home_dir = preparation_dir.join("home");
71 std::fs::create_dir_all(&home_dir).map_err(|err| Error::CreateDir(home_dir.clone(), err))?;
72 let target_dir = preparation_dir.join("target");
73 std::fs::create_dir_all(&target_dir)
74 .map_err(|err| Error::CreateDir(target_dir.clone(), err))?;
75 let profile = String::from("release");
76
77 let mut build_command = Command::new("cargo");
79 build_command
80 .arg("build")
81 .arg("--manifest-path")
82 .arg(cargo_toml_path)
83 .arg("--lib")
84 .arg("--profile")
85 .arg(&profile)
86 .arg("--message-format=json-render-diagnostics")
87 .arg("--target-dir")
88 .arg(&target_dir);
89
90 add_cargo_features(&mut build_command);
91
92 let build_output = build_command
93 .stdout(Stdio::piped())
94 .stderr(Stdio::inherit())
95 .env("CARGO_HOME", &home_dir)
96 .output()
97 .map_err(Error::BuildRun)?;
98
99 if !build_output.status.success() {
100 log_process_error_log("Preparation", &build_output.stderr);
101 return Err(Error::BuildStatus(build_output.status));
102 }
103
104 log_process_output("Preparation", &build_output);
105
106 let mut linked_paths = BTreeSet::new();
107
108 let mut target_build_args = vec![
109 String::from("--edition=2021"),
110 String::from("--error-format=json"),
111 String::from("--json=artifacts"),
112 String::from("--crate-type"),
113 String::from("bin"),
114 String::from("-C"),
115 String::from("opt-level=3"),
116 String::from("-C"),
117 String::from("embed-bitcode=no"),
118 String::from("-C"),
119 String::from("strip=symbols"),
120 ];
121
122 let deps_dir = target_dir.join(profile).join("deps");
124
125 target_build_args.push(String::from("-L"));
126 target_build_args.push(format!("dependency={}", deps_dir));
127
128 let bytes: &[u8] = &build_output.stdout;
130 for message in cargo_metadata::Message::parse_stream(bytes) {
131 let message = message.map_err(Error::CargoParse)?;
132 match message {
133 Message::BuildScriptExecuted(build_script) => {
134 linked_paths.extend(build_script.linked_paths);
136 }
137 Message::CompilerArtifact(artifact) => {
138 let target_name = artifact.target.name.replace('-', "_");
141 if matches!(
142 target_name.as_str(),
143 "mck" | "machine_check" | "machine_check_exec"
144 ) {
145 for original_path in artifact.filenames {
146 let extern_target_name = artifact.target.name.replace('-', "_");
150 target_build_args.push(String::from("--extern"));
151 target_build_args.push(format!("{}={}", extern_target_name, original_path));
152 }
153 }
154 }
155 Message::BuildFinished(finished) => {
156 assert!(finished.success);
158 }
159 _ => (),
160 };
161 }
162
163 for linked_path in linked_paths {
165 target_build_args.push(String::from("-L"));
166 target_build_args.push(linked_path.to_string());
167 }
168
169 let preparation = Preparation { target_build_args };
170
171 let preparation_path = preparation_dir.join("preparation.json");
172 let file = File::create(&preparation_path)
173 .map_err(|err| Error::CreateFile(preparation_path.clone(), err))?;
174 let mut writer = BufWriter::new(file);
175 serde_json::to_writer(&mut writer, &preparation)?;
176 writer
177 .flush()
178 .map_err(|err| Error::Flush(preparation_path, err))?;
179
180 info!("Preparation complete.");
181 Ok(())
182}