A repository for a long-term OS project titled DasOS (named after the Greek for "forest")
1use std::{path::{PathBuf, Path}, env, fs::{create_dir_all, remove_file}, io::{self, ErrorKind}, os::unix::fs::symlink, process::{Stdio, Command}};
2
3fn main() {
4 // This is the folder where a build script (this file) should place its output
5 let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
6 // This is the `runner` folder
7 let runner_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
8 // This folder contains Limine files such as `BOOTX64.EFI`
9 let limine_dir = PathBuf::from(env::var("LIMINE_PATH").unwrap());
10 // Cargo passes us the path to the kernel executable because it is an artifact dep
11 let kernel_executable_file = env::var("CARGO_BIN_FILE_KERNEL").unwrap();
12
13 // Symlink the out dir so we get a constant path to it
14 ensure_symlink(&out_dir, runner_dir.join("out_dir")).unwrap();
15
16 // We will create an ISO file for our OS
17 // First we create a folder which will be used to generate the ISO
18 // We will use symlinks instead of copying to avoid unnecessary disk space used
19 let iso_dir = out_dir.join("iso_root");
20 create_dir_all(&iso_dir).unwrap();
21
22 // symlink the kernel binary to `kernel`
23 let kernel_dest = iso_dir.join("kernel");
24 ensure_symlink(&kernel_executable_file, &kernel_dest).unwrap();
25
26 // Limine config will be in `limine.conf`
27 let limine_conf = iso_dir.join("limine.conf");
28 ensure_symlink(runner_dir.join("limine.conf"), limine_conf).unwrap();
29
30 let boot_dir = iso_dir.join("boot");
31 create_dir_all(&boot_dir).unwrap();
32
33 // Copy files from the Limine packaeg into `boot/limine`
34 let out_limine_dir = boot_dir.join("limine");
35 create_dir_all(&out_limine_dir).unwrap();
36
37 for path in [
38 "limine-bios.sys",
39 "limine-bios-cd.bin",
40 "limine-uefi-cd.bin",
41 ] {
42 let from = limine_dir.join(path);
43 let to = out_limine_dir.join(path);
44 ensure_symlink(from, to).unwrap();
45 }
46
47 // EFI/BOOT/BOOTX64.EFI is the executable loaded by UEFI firmware
48 // We will also copy BOOTIA32.EFI because xorisso will complain if it's not there
49 let efi_boot_dir = iso_dir.join("EFI/BOOT");
50 create_dir_all(&efi_boot_dir).unwrap();
51
52 for efi_file in ["BOOTX64.EFI", "BOOTIA32.EFI"] {
53 ensure_symlink(limine_dir.join(efi_file), efi_boot_dir.join(efi_file)).unwrap();
54 }
55
56 // We'll call the output iso `os.iso`
57 let output_iso = out_dir.join("os.iso");
58 // This command creates an ISO file from our `iso_root` folder.
59 // Symlinks will be read (the contents will be copied into the ISO file)
60 let status = Command::new("xorriso")
61 .arg("-as")
62 .arg("mkisofs")
63 .arg("--follow-links")
64 .arg("-b")
65 .arg(
66 out_limine_dir
67 .join("limine-bios-cd.bin")
68 .strip_prefix(&iso_dir)
69 .unwrap(),
70 )
71 .arg("-no-emul-boot")
72 .arg("-boot-load-size")
73 .arg("4")
74 .arg("-boot-info-table")
75 .arg("--efi-boot")
76 .arg(
77 out_limine_dir
78 .join("limine-uefi-cd.bin")
79 .strip_prefix(&iso_dir)
80 .unwrap(),
81 )
82 .arg("-efi-boot-part")
83 .arg("--efi-boot-image")
84 .arg("--protective-msdos-label")
85 .arg(iso_dir)
86 .arg("-o")
87 .arg(&output_iso)
88 .stderr(Stdio::inherit())
89 .stdout(Stdio::inherit())
90 .status()
91 .unwrap();
92
93 assert!(status.success() || status.code().unwrap_or(-1) == 32);
94
95 // This is needed to create a hybrid ISO that boots on both BIOS and UEFI. See https://github.com/limine-bootloader/limine/blob/v9.x/USAGE.md#biosuefi-hybrid-iso-creation
96 let status = Command::new("limine")
97 .arg("bios-install")
98 .arg(&output_iso)
99 .stderr(Stdio::inherit())
100 .stdout(Stdio::inherit())
101 .status()
102 .unwrap();
103
104 assert!(status.success());
105
106 let output_iso = output_iso.display();
107 println!("cargo:rustc-env=ISO={output_iso}");
108}
109
110pub fn ensure_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
111 match remove_file(&link) {
112 Ok(()) => Ok(()),
113 Err(error) => match error.kind() {
114 ErrorKind::NotFound => Ok(()),
115 _ => Err(error),
116 },
117 }?;
118
119 symlink(original, link)?;
120 Ok(())
121}