A repository for a long-term OS project titled DasOS (named after the Greek for "forest")
at main 121 lines 4.4 kB view raw
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}