Skip to content

Commit 30a9ea2

Browse files
committed
Refactor: Move everything to library
1 parent 49bbcd3 commit 30a9ea2

File tree

8 files changed

+494
-495
lines changed

8 files changed

+494
-495
lines changed

src/builder.rs

Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
use std::{
2+
fmt, io,
3+
path::{Path, PathBuf},
4+
process::{self, Command},
5+
};
6+
7+
pub struct Builder {
8+
kernel_manifest_path: PathBuf,
9+
kernel_metadata: cargo_metadata::Metadata,
10+
}
11+
12+
impl Builder {
13+
pub fn new(manifest_path: Option<PathBuf>) -> Result<Self, BuilderError> {
14+
let kernel_manifest_path =
15+
manifest_path.unwrap_or(locate_cargo_manifest::locate_manifest()?);
16+
let kernel_metadata = cargo_metadata::MetadataCommand::new()
17+
.manifest_path(&kernel_manifest_path)
18+
.exec()?;
19+
Ok(Builder {
20+
kernel_manifest_path,
21+
kernel_metadata,
22+
})
23+
}
24+
25+
pub fn kernel_manifest_path(&self) -> &Path {
26+
&self.kernel_manifest_path
27+
}
28+
29+
pub fn kernel_root(&self) -> &Path {
30+
self.kernel_manifest_path
31+
.parent()
32+
.expect("kernel manifest has no parent directory")
33+
}
34+
35+
pub fn kernel_metadata(&self) -> &cargo_metadata::Metadata {
36+
&self.kernel_metadata
37+
}
38+
39+
pub fn kernel_package(&self) -> Result<&cargo_metadata::Package, String> {
40+
let mut packages = self.kernel_metadata.packages.iter();
41+
let kernel_package = packages.find(|p| &p.manifest_path == &self.kernel_manifest_path);
42+
kernel_package.ok_or(format!(
43+
"packages[manifest_path = `{}`]",
44+
&self.kernel_manifest_path.display()
45+
))
46+
}
47+
48+
pub fn build_kernel(&self, args: &[String], quiet: bool) -> Result<(), BuildKernelError> {
49+
if !quiet {
50+
println!("Building kernel");
51+
}
52+
53+
let cargo = std::env::var("CARGO").unwrap_or("cargo".to_owned());
54+
let mut cmd = process::Command::new(cargo);
55+
cmd.arg("xbuild");
56+
cmd.args(args);
57+
if !quiet {
58+
cmd.stdout(process::Stdio::inherit());
59+
cmd.stderr(process::Stdio::inherit());
60+
}
61+
let output = cmd.output().map_err(|err| BuildKernelError::Io {
62+
message: "failed to execute kernel build",
63+
error: err,
64+
})?;;
65+
if !output.status.success() {
66+
let mut help_command = process::Command::new("cargo");
67+
help_command.arg("xbuild").arg("--help");
68+
help_command.stdout(process::Stdio::null());
69+
help_command.stderr(process::Stdio::null());
70+
if let Ok(help_exit_status) = help_command.status() {
71+
if !help_exit_status.success() {
72+
return Err(BuildKernelError::XbuildNotFound);
73+
}
74+
}
75+
return Err(BuildKernelError::XbuildFailed {
76+
stderr: output.stderr,
77+
});
78+
}
79+
80+
Ok(())
81+
}
82+
83+
pub fn create_bootimage(
84+
&self,
85+
kernel_bin_path: &Path,
86+
output_bin_path: &Path,
87+
quiet: bool,
88+
) -> Result<(), CreateBootimageError> {
89+
let metadata = self.kernel_metadata();
90+
91+
let bootloader_name = {
92+
let kernel_package = self
93+
.kernel_package()
94+
.map_err(|key| CreateBootimageError::CargoMetadataIncomplete { key })?;
95+
let mut dependencies = kernel_package.dependencies.iter();
96+
let bootloader_package = dependencies
97+
.find(|p| p.rename.as_ref().unwrap_or(&p.name) == "bootloader")
98+
.ok_or(CreateBootimageError::BootloaderNotFound)?;
99+
bootloader_package.name.clone()
100+
};
101+
let target_dir = metadata
102+
.target_directory
103+
.join("bootimage")
104+
.join(&bootloader_name);
105+
106+
let bootloader_pkg = metadata
107+
.packages
108+
.iter()
109+
.find(|p| p.name == bootloader_name)
110+
.ok_or(CreateBootimageError::CargoMetadataIncomplete {
111+
key: format!("packages[name = `{}`", &bootloader_name),
112+
})?;
113+
let bootloader_root = bootloader_pkg.manifest_path.parent().ok_or(
114+
CreateBootimageError::BootloaderInvalid(
115+
"bootloader manifest has no target directory".into(),
116+
),
117+
)?;
118+
let bootloader_features =
119+
{
120+
let resolve = metadata.resolve.as_ref().ok_or(
121+
CreateBootimageError::CargoMetadataIncomplete {
122+
key: "resolve".into(),
123+
},
124+
)?;
125+
let bootloader_resolve = resolve
126+
.nodes
127+
.iter()
128+
.find(|n| n.id == bootloader_pkg.id)
129+
.ok_or(CreateBootimageError::CargoMetadataIncomplete {
130+
key: format!("resolve[\"{}\"]", bootloader_name),
131+
})?;
132+
bootloader_resolve.features.clone()
133+
};
134+
let bootloader_target_triple =
135+
crate::cargo_config::default_target_triple(&bootloader_root, false)
136+
.map_err(CreateBootimageError::BootloaderInvalid)?
137+
.ok_or(CreateBootimageError::BootloaderInvalid(format!(
138+
"bootloader must have a default target"
139+
)))?;
140+
141+
// build bootloader
142+
if !quiet {
143+
println!("Building bootloader");
144+
}
145+
146+
let cargo = std::env::var("CARGO").unwrap_or("cargo".to_owned());
147+
let mut cmd = process::Command::new(cargo);
148+
cmd.arg("xbuild");
149+
cmd.arg("--manifest-path");
150+
cmd.arg(&bootloader_pkg.manifest_path);
151+
cmd.arg("--target-dir").arg(&target_dir);
152+
cmd.arg("--features")
153+
.arg(bootloader_features.as_slice().join(" "));
154+
cmd.arg("--release");
155+
cmd.current_dir(bootloader_root);
156+
cmd.env("KERNEL", kernel_bin_path);
157+
cmd.env_remove("RUSTFLAGS");
158+
if !quiet {
159+
cmd.stdout(process::Stdio::inherit());
160+
cmd.stderr(process::Stdio::inherit());
161+
}
162+
let output = cmd.output().map_err(|err| CreateBootimageError::Io {
163+
message: "failed to execute bootloader build command",
164+
error: err,
165+
})?;
166+
if !output.status.success() {
167+
return Err(CreateBootimageError::BootloaderBuildFailed {
168+
stderr: output.stderr,
169+
});
170+
}
171+
172+
let bootloader_elf_path = target_dir
173+
.join(&bootloader_target_triple)
174+
.join("release")
175+
.join(&bootloader_name);
176+
177+
let llvm_tools = llvm_tools::LlvmTools::new()?;
178+
let objcopy = llvm_tools
179+
.tool(&llvm_tools::exe("llvm-objcopy"))
180+
.ok_or(CreateBootimageError::LlvmObjcopyNotFound)?;
181+
182+
// convert bootloader to binary
183+
let mut cmd = Command::new(objcopy);
184+
cmd.arg("-I").arg("elf64-x86-64");
185+
cmd.arg("-O").arg("binary");
186+
cmd.arg("--binary-architecture=i386:x86-64");
187+
cmd.arg(&bootloader_elf_path);
188+
cmd.arg(&output_bin_path);
189+
let output = cmd.output().map_err(|err| CreateBootimageError::Io {
190+
message: "failed to execute llvm-objcopy command",
191+
error: err,
192+
})?;
193+
if !output.status.success() {
194+
return Err(CreateBootimageError::ObjcopyFailed {
195+
stderr: output.stderr,
196+
});
197+
}
198+
199+
Ok(())
200+
}
201+
}
202+
203+
#[derive(Debug)]
204+
pub enum BuilderError {
205+
/// Failed to locate cargo manifest
206+
LocateCargoManifest(locate_cargo_manifest::LocateManifestError),
207+
/// Error while running `cargo metadata`
208+
CargoMetadata(cargo_metadata::Error),
209+
}
210+
211+
impl fmt::Display for BuilderError {
212+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213+
match self {
214+
BuilderError::LocateCargoManifest(err) => writeln!(
215+
f,
216+
"Could not find Cargo.toml file starting from current folder: {:?}",
217+
err
218+
),
219+
BuilderError::CargoMetadata(err) => writeln!(
220+
f,
221+
"Error while running `cargo metadata` for current project: {:?}",
222+
err
223+
),
224+
}
225+
}
226+
}
227+
228+
#[derive(Debug)]
229+
pub enum BuildKernelError {
230+
/// Could not find kernel package in cargo metadata, required for retrieving kernel crate name
231+
KernelPackageNotFound,
232+
/// An unexpected I/O error occurred
233+
Io {
234+
/// Desciption of the failed I/O operation
235+
message: &'static str,
236+
/// The I/O error that occured
237+
error: io::Error,
238+
},
239+
XbuildNotFound,
240+
XbuildFailed {
241+
stderr: Vec<u8>,
242+
},
243+
CargoConfigInvalid {
244+
path: PathBuf,
245+
error: String,
246+
},
247+
}
248+
249+
impl fmt::Display for BuildKernelError {
250+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
251+
match self {
252+
BuildKernelError::KernelPackageNotFound => {
253+
writeln!(f, "Could not find kernel package in cargo metadata, required for retrieving kernel crate name")
254+
}
255+
BuildKernelError::Io {message, error} => {
256+
writeln!(f, "I/O error: {}: {}", message, error)
257+
}
258+
BuildKernelError::XbuildNotFound => {
259+
writeln!(f, "Failed to run `cargo xbuild`. Perhaps it is not installed?\n\
260+
Run `cargo install cargo-xbuild` to install it.")
261+
}
262+
BuildKernelError::XbuildFailed{stderr} => {
263+
writeln!(f, "Kernel build failed: {}", String::from_utf8_lossy(stderr))
264+
}
265+
BuildKernelError::CargoConfigInvalid{path,error} => {
266+
writeln!(f, "Failed to read cargo config at {}: {}", path.display(), error)
267+
},
268+
}
269+
}
270+
}
271+
272+
#[derive(Debug)]
273+
pub enum CreateBootimageError {
274+
/// Could not find some required information in the `cargo metadata` output
275+
CargoMetadataIncomplete {
276+
/// The required key that was not found
277+
key: String,
278+
},
279+
/// Bootloader dependency not found
280+
BootloaderNotFound,
281+
/// Bootloader dependency has not the right format
282+
BootloaderInvalid(String),
283+
BootloaderBuildFailed {
284+
stderr: Vec<u8>,
285+
},
286+
/// An unexpected I/O error occurred
287+
Io {
288+
/// Desciption of the failed I/O operation
289+
message: &'static str,
290+
/// The I/O error that occured
291+
error: io::Error,
292+
},
293+
/// There was a problem retrieving the `llvm-tools-preview` rustup component
294+
LlvmTools(llvm_tools::Error),
295+
/// The llvm-tools component did not contain the required `llvm-objcopy` executable
296+
LlvmObjcopyNotFound,
297+
/// The `llvm-objcopy` command failed
298+
ObjcopyFailed {
299+
stderr: Vec<u8>,
300+
},
301+
}
302+
303+
impl fmt::Display for CreateBootimageError {
304+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
305+
match self {
306+
CreateBootimageError::CargoMetadataIncomplete { key } => writeln!(
307+
f,
308+
"Could not find required key `{}` in cargo metadata output",
309+
key
310+
),
311+
CreateBootimageError::BootloaderNotFound => {
312+
writeln!(f, "Bootloader dependency not found\n\n\
313+
You need to add a dependency on a crate named `bootloader` in your Cargo.toml.")
314+
}
315+
CreateBootimageError::BootloaderInvalid(err) => writeln!(
316+
f,
317+
"The `bootloader` dependency has not the right format: {}",
318+
err
319+
),
320+
CreateBootimageError::BootloaderBuildFailed { stderr } => writeln!(
321+
f,
322+
"Bootloader build failed:\n\n{}",
323+
String::from_utf8_lossy(stderr)
324+
),
325+
CreateBootimageError::Io { message, error } => {
326+
writeln!(f, "I/O error: {}: {}", message, error)
327+
}
328+
CreateBootimageError::LlvmTools(err) => match err {
329+
llvm_tools::Error::NotFound => writeln!(
330+
f,
331+
"Could not find the `llvm-tools-preview` rustup component.\n\n\
332+
You can install by executing `rustup component add llvm-tools-preview`."
333+
),
334+
err => writeln!(
335+
f,
336+
"Failed to locate the `llvm-tools-preview` rustup component: {:?}",
337+
err
338+
),
339+
},
340+
CreateBootimageError::LlvmObjcopyNotFound => writeln!(
341+
f,
342+
"Could not find `llvm-objcopy` in the `llvm-tools-preview` rustup component."
343+
),
344+
CreateBootimageError::ObjcopyFailed { stderr } => writeln!(
345+
f,
346+
"Failed to run `llvm-objcopy`: {}",
347+
String::from_utf8_lossy(stderr)
348+
),
349+
}
350+
}
351+
}
352+
353+
// from implementations
354+
355+
impl From<locate_cargo_manifest::LocateManifestError> for BuilderError {
356+
fn from(err: locate_cargo_manifest::LocateManifestError) -> Self {
357+
BuilderError::LocateCargoManifest(err)
358+
}
359+
}
360+
361+
impl From<cargo_metadata::Error> for BuilderError {
362+
fn from(err: cargo_metadata::Error) -> Self {
363+
BuilderError::CargoMetadata(err)
364+
}
365+
}
366+
367+
impl From<llvm_tools::Error> for CreateBootimageError {
368+
fn from(err: llvm_tools::Error) -> Self {
369+
CreateBootimageError::LlvmTools(err)
370+
}
371+
}

0 commit comments

Comments
 (0)