2019-04-30 11:43:40 +02:00
const std = @import ( " std " ) ;
2020-11-13 20:16:25 +01:00
const log = std . log . scoped ( . builder ) ;
2019-09-03 20:13:26 +02:00
const builtin = @import ( " builtin " ) ;
2020-06-23 13:43:52 +02:00
const rt = @import ( " test/runtime_test.zig " ) ;
const RuntimeStep = rt . RuntimeStep ;
2020-07-25 12:18:19 +02:00
const Allocator = std . mem . Allocator ;
2024-06-24 21:10:31 +02:00
// const Builder = std.build.Builder;
2024-08-08 15:45:52 +02:00
const Step = std . Build . Step ;
2020-04-12 23:26:34 +02:00
const Target = std . Target ;
2024-06-24 21:10:31 +02:00
const CrossTarget = std . Target . Query ;
2019-09-03 20:13:26 +02:00
const fs = std . fs ;
2020-07-25 12:18:19 +02:00
const File = fs . File ;
2021-06-08 00:00:33 +02:00
const Mode = std . builtin . Mode ;
2020-06-23 13:43:52 +02:00
const TestMode = rt . TestMode ;
const ArrayList = std . ArrayList ;
2020-09-27 21:53:11 +02:00
const Fat32 = @import ( " mkfat32.zig " ) . Fat32 ;
2019-09-03 20:13:26 +02:00
2020-06-02 19:54:41 +02:00
const x86_i686 = CrossTarget {
2024-06-24 21:10:31 +02:00
. cpu_arch = . x86 ,
2020-06-02 19:54:41 +02:00
. os_tag = . freestanding ,
2024-06-24 20:47:27 +02:00
. cpu_model = . { . explicit = & Target . x86 . cpu . i686 } ,
2020-06-02 19:54:41 +02:00
} ;
2019-09-08 21:48:23 +02:00
2024-06-24 21:10:31 +02:00
pub fn build ( b : * std . Build ) ! void {
2020-06-22 21:18:19 +02:00
const target = b . standardTargetOptions ( . { . whitelist = & [ _ ] CrossTarget { x86_i686 } , . default_target = x86_i686 } ) ;
2024-08-08 15:45:52 +02:00
const arch = switch ( target . query . cpu_arch . ? ) {
. x86 = > " x86 " ,
2020-06-22 21:18:19 +02:00
else = > unreachable ,
2019-09-08 21:48:23 +02:00
} ;
2019-09-16 23:19:21 +02:00
2024-08-08 15:45:52 +02:00
const fmt_step = b . addFmt ( Step . Fmt . Options {
. paths = & [ _ ] [ ] const u8 { " build.zig " , " mkfat32.zig " , " src " , " test " } ,
2020-01-15 17:30:30 +01:00
} ) ;
b . default_step . dependOn ( & fmt_step . step ) ;
2020-01-09 14:08:00 +01:00
const main_src = " src/kernel/kmain.zig " ;
2020-06-02 19:41:54 +02:00
const arch_root = " src/kernel/arch " ;
2020-06-23 13:43:52 +02:00
const linker_script_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { arch_root , arch , " link.ld " } ) ;
2020-11-23 21:02:21 +01:00
const output_iso = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . install_path , " pluto.iso " } ) ;
const iso_dir_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . install_path , " iso " } ) ;
const boot_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . install_path , " iso " , " boot " } ) ;
const modules_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . install_path , " iso " , " modules " } ) ;
2020-07-25 12:18:19 +02:00
const ramdisk_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . install_path , " initrd.ramdisk " } ) ;
2020-09-27 21:53:11 +02:00
const fat32_image_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . install_path , " fat32.img " } ) ;
2020-12-12 23:31:42 +01:00
const test_fat32_image_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { " test " , " fat32 " , " test_fat32.img " } ) ;
2020-01-09 14:08:00 +01:00
2024-08-08 15:45:52 +02:00
const build_mode = b . standardOptimizeOption ( . { } ) ;
2020-06-23 13:43:52 +02:00
comptime var test_mode_desc : [ ] const u8 = " \n " ;
inline for ( @typeInfo ( TestMode ) . Enum . fields ) | field | {
const tm = @field ( TestMode , field . name ) ;
test_mode_desc = test_mode_desc + + field . name + + " ( " + + TestMode . getDescription ( tm ) + + " ) " ;
test_mode_desc = test_mode_desc + + " \n " ;
}
const test_mode = b . option ( TestMode , " test-mode " , " Run a specific runtime test. This option is for the rt-test step. Available options: " + + test_mode_desc ) orelse . None ;
const disable_display = b . option ( bool , " disable-display " , " Disable the qemu window " ) orelse false ;
2019-04-17 18:40:26 +02:00
2020-06-02 19:54:41 +02:00
const exec = b . addExecutable ( " pluto.elf " , main_src ) ;
2021-06-08 00:00:33 +02:00
const exec_output_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . install_path , " pluto.elf " } ) ;
2021-05-05 22:30:08 +02:00
exec . setOutputDir ( b . install_path ) ;
2021-06-08 00:00:33 +02:00
const exec_options = b . addOptions ( ) ;
exec . addOptions ( " build_options " , exec_options ) ;
exec_options . addOption ( TestMode , " test_mode " , test_mode ) ;
2020-01-09 14:08:00 +01:00
exec . setBuildMode ( build_mode ) ;
2021-06-08 00:00:33 +02:00
exec . setLinkerScriptPath ( std . build . FileSource { . path = linker_script_path } ) ;
2020-04-12 23:26:34 +02:00
exec . setTarget ( target ) ;
2019-05-31 08:41:28 +02:00
2020-06-02 19:54:41 +02:00
const make_iso = switch ( target . getCpuArch ( ) ) {
2021-06-08 00:00:33 +02:00
. i386 = > b . addSystemCommand ( & [ _ ] [ ] const u8 { " ./makeiso.sh " , boot_path , modules_path , iso_dir_path , exec_output_path , ramdisk_path , output_iso } ) ,
2020-06-02 19:54:41 +02:00
else = > unreachable ,
} ;
2020-01-09 14:08:00 +01:00
make_iso . step . dependOn ( & exec . step ) ;
2020-06-23 13:43:52 +02:00
2020-09-27 21:53:11 +02:00
var fat32_builder_step = Fat32BuilderStep . create ( b , . { } , fat32_image_path ) ;
make_iso . step . dependOn ( & fat32_builder_step . step ) ;
2020-07-25 12:18:19 +02:00
var ramdisk_files_al = ArrayList ( [ ] const u8 ) . init ( b . allocator ) ;
defer ramdisk_files_al . deinit ( ) ;
if ( test_mode = = . Initialisation ) {
2020-07-24 01:18:56 +02:00
// Add some test files for the ramdisk runtime tests
2020-07-25 12:18:19 +02:00
try ramdisk_files_al . append ( " test/ramdisk_test1.txt " ) ;
try ramdisk_files_al . append ( " test/ramdisk_test2.txt " ) ;
2020-07-24 01:18:56 +02:00
} else if ( test_mode = = . Scheduler ) {
2020-11-23 21:02:21 +01:00
inline for ( & [ _ ] [ ] const u8 { " user_program_data " , " user_program " } ) | user_program | {
// Add some test files for the user mode runtime tests
const user_program_step = b . addExecutable ( user_program + + " .elf " , null ) ;
2021-06-08 00:00:33 +02:00
user_program_step . setLinkerScriptPath ( . { . path = " test/user_program.ld " } ) ;
2020-11-23 21:02:21 +01:00
user_program_step . addAssemblyFile ( " test/ " + + user_program + + " .s " ) ;
user_program_step . setOutputDir ( b . install_path ) ;
user_program_step . setTarget ( target ) ;
user_program_step . setBuildMode ( build_mode ) ;
user_program_step . strip = true ;
exec . step . dependOn ( & user_program_step . step ) ;
const user_program_path = try std . mem . join ( b . allocator , " / " , & [ _ ] [ ] const u8 { b . install_path , user_program + + " .elf " } ) ;
try ramdisk_files_al . append ( user_program_path ) ;
}
2020-07-25 12:18:19 +02:00
}
const ramdisk_step = RamdiskStep . create ( b , target , ramdisk_files_al . toOwnedSlice ( ) , ramdisk_path ) ;
make_iso . step . dependOn ( & ramdisk_step . step ) ;
2020-01-09 14:08:00 +01:00
b . default_step . dependOn ( & make_iso . step ) ;
2019-04-17 18:40:26 +02:00
2020-01-09 14:08:00 +01:00
const test_step = b . step ( " test " , " Run tests " ) ;
2020-06-23 13:43:52 +02:00
const unit_tests = b . addTest ( main_src ) ;
unit_tests . setBuildMode ( build_mode ) ;
unit_tests . setMainPkgPath ( " . " ) ;
2021-06-08 00:00:33 +02:00
const unit_test_options = b . addOptions ( ) ;
unit_tests . addOptions ( " build_options " , unit_test_options ) ;
unit_test_options . addOption ( TestMode , " test_mode " , test_mode ) ;
2020-10-10 01:25:09 +02:00
unit_tests . setTarget ( . { . cpu_arch = target . cpu_arch } ) ;
2020-06-23 13:43:52 +02:00
if ( builtin . os . tag ! = . windows ) {
2021-06-08 00:00:33 +02:00
b . enable_qemu = true ;
2020-01-09 14:08:00 +01:00
}
2019-04-17 18:40:26 +02:00
2020-10-10 01:25:09 +02:00
// Run the mock gen
const mock_gen = b . addExecutable ( " mock_gen " , " test/gen_types.zig " ) ;
mock_gen . setMainPkgPath ( " . " ) ;
const mock_gen_run = mock_gen . run ( ) ;
unit_tests . step . dependOn ( & mock_gen_run . step ) ;
2020-12-12 23:31:42 +01:00
// Create test FAT32 image
const test_fat32_img_step = Fat32BuilderStep . create ( b , . { } , test_fat32_image_path ) ;
const copy_test_files_step = b . addSystemCommand ( & [ _ ] [ ] const u8 { " ./fat32_cp.sh " , test_fat32_image_path } ) ;
copy_test_files_step . step . dependOn ( & test_fat32_img_step . step ) ;
unit_tests . step . dependOn ( & copy_test_files_step . step ) ;
2020-06-23 13:43:52 +02:00
test_step . dependOn ( & unit_tests . step ) ;
2019-10-01 18:35:15 +02:00
2020-06-23 13:43:52 +02:00
const rt_test_step = b . step ( " rt-test " , " Run runtime tests " ) ;
var qemu_args_al = ArrayList ( [ ] const u8 ) . init ( b . allocator ) ;
defer qemu_args_al . deinit ( ) ;
switch ( target . getCpuArch ( ) ) {
. i386 = > try qemu_args_al . append ( " qemu-system-i386 " ) ,
else = > unreachable ,
}
try qemu_args_al . append ( " -serial " ) ;
try qemu_args_al . append ( " stdio " ) ;
switch ( target . getCpuArch ( ) ) {
. i386 = > {
try qemu_args_al . append ( " -boot " ) ;
try qemu_args_al . append ( " d " ) ;
try qemu_args_al . append ( " -cdrom " ) ;
try qemu_args_al . append ( output_iso ) ;
2020-06-02 19:54:41 +02:00
} ,
2020-06-23 13:43:52 +02:00
else = > unreachable ,
}
if ( disable_display ) {
try qemu_args_al . append ( " -display " ) ;
try qemu_args_al . append ( " none " ) ;
}
2024-06-24 20:47:27 +02:00
const qemu_args = qemu_args_al . toOwnedSlice ( ) ;
2020-06-23 13:43:52 +02:00
const rt_step = RuntimeStep . create ( b , test_mode , qemu_args ) ;
rt_step . step . dependOn ( & make_iso . step ) ;
rt_test_step . dependOn ( & rt_step . step ) ;
const run_step = b . step ( " run " , " Run with qemu " ) ;
const run_debug_step = b . step ( " debug-run " , " Run with qemu and wait for a gdb connection " ) ;
2019-10-01 18:35:15 +02:00
const qemu_cmd = b . addSystemCommand ( qemu_args ) ;
const qemu_debug_cmd = b . addSystemCommand ( qemu_args ) ;
2020-01-01 20:12:36 +01:00
qemu_debug_cmd . addArgs ( & [ _ ] [ ] const u8 { " -s " , " -S " } ) ;
2019-09-16 23:19:21 +02:00
2020-01-09 14:08:00 +01:00
qemu_cmd . step . dependOn ( & make_iso . step ) ;
qemu_debug_cmd . step . dependOn ( & make_iso . step ) ;
2019-10-01 18:35:15 +02:00
run_step . dependOn ( & qemu_cmd . step ) ;
run_debug_step . dependOn ( & qemu_debug_cmd . step ) ;
2019-04-30 11:43:40 +02:00
2019-10-01 18:35:15 +02:00
const debug_step = b . step ( " debug " , " Debug with gdb and connect to a running qemu instance " ) ;
2021-06-08 00:00:33 +02:00
const symbol_file_arg = try std . mem . join ( b . allocator , " " , & [ _ ] [ ] const u8 { " symbol-file " , exec_output_path } ) ;
2020-01-01 20:12:36 +01:00
const debug_cmd = b . addSystemCommand ( & [ _ ] [ ] const u8 {
2020-06-02 19:54:41 +02:00
" gdb-multiarch " ,
2019-09-03 20:13:26 +02:00
" -ex " ,
2019-09-12 00:25:34 +02:00
symbol_file_arg ,
2020-06-02 19:54:41 +02:00
" -ex " ,
" set architecture auto " ,
2019-09-03 20:13:26 +02:00
} ) ;
2020-01-01 20:12:36 +01:00
debug_cmd . addArgs ( & [ _ ] [ ] const u8 {
2019-09-03 20:13:26 +02:00
" -ex " ,
" target remote localhost:1234 " ,
} ) ;
debug_step . dependOn ( & debug_cmd . step ) ;
2019-04-30 11:43:40 +02:00
}
2020-07-25 12:18:19 +02:00
2020-09-27 21:53:11 +02:00
/// The FAT32 step for creating a FAT32 image.
const Fat32BuilderStep = struct {
/// The Step, that is all you need to know
step : Step ,
/// The builder pointer, also all you need to know
2024-06-24 21:10:31 +02:00
builder : * std . Build ,
2020-09-27 21:53:11 +02:00
/// The path to where the ramdisk will be written to.
out_file_path : [ ] const u8 ,
/// Options for creating the FAT32 image.
options : Fat32 . Options ,
///
/// The make function that is called by the builder.
///
/// Arguments:
/// IN step: *Step - The step of this step.
///
/// Error: error{EndOfStream} || File.OpenError || File.ReadError || File.WriteError || File.SeekError || Allocator.Error || Fat32.Error || Error
/// error{EndOfStream} || File.OpenError || File.ReadError || File.WriteError || File.SeekError - Error related to file operations. See std.fs.File.
/// Allocator.Error - If there isn't enough memory to allocate for the make step.
/// Fat32.Error - If there was an error creating the FAT image. This will be invalid options.
///
fn make ( step : * Step ) ( error { EndOfStream } | | File . OpenError | | File . ReadError | | File . WriteError | | File . SeekError | | Fat32 . Error ) ! void {
2024-06-24 20:47:27 +02:00
const self : * Fat32BuilderStep = @fieldParentPtr ( " step " , step ) ;
2020-11-09 20:06:31 +01:00
// Open the out file
const image = try std . fs . cwd ( ) . createFile ( self . out_file_path , . { . read = true } ) ;
// If there was an error, delete the image as this will be invalid
errdefer ( std . fs . cwd ( ) . deleteFile ( self . out_file_path ) catch unreachable ) ;
defer image . close ( ) ;
2020-12-07 19:29:16 +01:00
try Fat32 . make ( self . options , image , false ) ;
2020-09-27 21:53:11 +02:00
}
///
/// Create a FAT32 builder step.
///
/// Argument:
/// IN builder: *Builder - The build builder.
/// IN options: Options - Options for creating FAT32 image.
///
/// Return: *Fat32BuilderStep
/// The FAT32 builder step pointer to add to the build process.
///
2024-06-24 21:10:31 +02:00
pub fn create ( builder : * std . Build , options : Fat32 . Options , out_file_path : [ ] const u8 ) * Fat32BuilderStep {
2020-09-27 21:53:11 +02:00
const fat32_builder_step = builder . allocator . create ( Fat32BuilderStep ) catch unreachable ;
fat32_builder_step . * = . {
2021-06-08 00:00:33 +02:00
. step = Step . init ( . custom , builder . fmt ( " Fat32BuilderStep " , . { } ) , builder . allocator , make ) ,
2020-09-27 21:53:11 +02:00
. builder = builder ,
. options = options ,
. out_file_path = out_file_path ,
} ;
return fat32_builder_step ;
}
} ;
2020-07-25 12:18:19 +02:00
/// The ramdisk make step for creating the initial ramdisk.
const RamdiskStep = struct {
/// The Step, that is all you need to know
step : Step ,
/// The builder pointer, also all you need to know
2024-06-24 21:10:31 +02:00
builder : * std . Build ,
2020-07-25 12:18:19 +02:00
/// The target for the build
target : CrossTarget ,
/// The list of files to be added to the ramdisk
files : [ ] const [ ] const u8 ,
/// The path to where the ramdisk will be written to.
out_file_path : [ ] const u8 ,
/// The possible errors for creating a ramdisk
2021-04-09 21:30:05 +02:00
const Error = ( error { EndOfStream } | | File . ReadError | | File . SeekError | | Allocator . Error | | File . WriteError | | File . OpenError ) ;
2020-07-25 12:18:19 +02:00
///
/// Create and write the files to a raw ramdisk in the format:
/// (NumOfFiles:usize)[(name_length:usize)(name:u8[name_length])(content_length:usize)(content:u8[content_length])]*
///
/// Argument:
/// IN comptime Usize: type - The usize type for the architecture.
/// IN self: *RamdiskStep - Self.
///
/// Error: Error
/// Errors for opening, reading and writing to and from files and for allocating memory.
///
fn writeRamdisk ( comptime Usize : type , self : * RamdiskStep ) Error ! void {
2020-09-27 21:53:11 +02:00
// 1GB, don't think the ram disk should be very big
2020-07-25 12:18:19 +02:00
const max_file_size = 1024 * 1024 * 1024 ;
// Open the out file
var ramdisk = try fs . cwd ( ) . createFile ( self . out_file_path , . { } ) ;
defer ramdisk . close ( ) ;
// Get the targets endian
const endian = self . target . getCpuArch ( ) . endian ( ) ;
// First write the number of files/headers
std . debug . assert ( self . files . len < std . math . maxInt ( Usize ) ) ;
2024-06-24 20:47:27 +02:00
try ramdisk . writer ( ) . writeInt ( Usize , @truncate ( self . files . len ) , endian ) ;
2020-07-25 12:18:19 +02:00
var current_offset : usize = 0 ;
for ( self . files ) | file_path | {
// Open, and read the file. Can get the size from this as well
const file_content = try fs . cwd ( ) . readFileAlloc ( self . builder . allocator , file_path , max_file_size ) ;
// Get the last occurrence of / for the file name, if there isn't one, then the file_path is the name
const file_name_index = if ( std . mem . lastIndexOf ( u8 , file_path , " / " ) ) | index | index + 1 else 0 ;
// Write the header and file content to the ramdisk
// Name length
std . debug . assert ( file_path [ file_name_index . . ] . len < std . math . maxInt ( Usize ) ) ;
2024-06-24 20:47:27 +02:00
try ramdisk . writer ( ) . writeInt ( Usize , @truncate ( file_path [ file_name_index . . ] . len ) , endian ) ;
2020-07-25 12:18:19 +02:00
// Name
try ramdisk . writer ( ) . writeAll ( file_path [ file_name_index . . ] ) ;
// Length
std . debug . assert ( file_content . len < std . math . maxInt ( Usize ) ) ;
2024-06-24 20:47:27 +02:00
try ramdisk . writer ( ) . writeInt ( Usize , @truncate ( file_content . len ) , endian ) ;
2020-07-25 12:18:19 +02:00
// File contest
try ramdisk . writer ( ) . writeAll ( file_content ) ;
// Increment the offset to the new location
current_offset + = @sizeOf ( Usize ) * 3 + file_path [ file_name_index . . ] . len + file_content . len ;
}
}
///
2020-09-27 21:53:11 +02:00
/// The make function that is called by the builder. This will switch on the target to get the
/// correct usize length for the target.
2020-07-25 12:18:19 +02:00
///
/// Arguments:
/// IN step: *Step - The step of this step.
///
/// Error: Error
/// Errors for opening, reading and writing to and from files and for allocating memory.
///
fn make ( step : * Step ) Error ! void {
2024-06-24 20:47:27 +02:00
const self : * RamdiskStep = @fieldParentPtr ( " step " , step ) ;
2020-07-25 12:18:19 +02:00
switch ( self . target . getCpuArch ( ) ) {
. i386 = > try writeRamdisk ( u32 , self ) ,
else = > unreachable ,
}
}
///
/// Create a ramdisk step.
///
/// Argument:
2020-09-27 21:53:11 +02:00
/// IN builder: *Builder - The build builder.
/// IN target: CrossTarget - The target for the build.
2020-07-25 12:18:19 +02:00
/// IN files: []const []const u8 - The file names to be added to the ramdisk.
/// IN out_file_path: []const u8 - The output file path.
///
/// Return: *RamdiskStep
/// The ramdisk step pointer to add to the build process.
///
2024-06-24 21:10:31 +02:00
pub fn create ( builder : * std . Build , target : CrossTarget , files : [ ] const [ ] const u8 , out_file_path : [ ] const u8 ) * RamdiskStep {
2020-07-25 12:18:19 +02:00
const ramdisk_step = builder . allocator . create ( RamdiskStep ) catch unreachable ;
ramdisk_step . * = . {
2021-06-08 00:00:33 +02:00
. step = Step . init ( . custom , builder . fmt ( " Ramdisk " , . { } ) , builder . allocator , make ) ,
2020-07-25 12:18:19 +02:00
. builder = builder ,
. target = target ,
. files = files ,
. out_file_path = out_file_path ,
} ;
return ramdisk_step ;
}
} ;