2019-04-30 11:43:40 +02:00
const std = @import ( " std " ) ;
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 ;
2019-09-03 20:13:26 +02:00
const Builder = std . build . Builder ;
2020-07-25 12:18:19 +02:00
const Step = std . build . Step ;
2020-04-12 23:26:34 +02:00
const Target = std . Target ;
const CrossTarget = std . zig . CrossTarget ;
2019-09-03 20:13:26 +02:00
const fs = std . fs ;
2020-07-25 12:18:19 +02:00
const File = fs . File ;
2019-09-08 21:48:23 +02:00
const Mode = builtin . Mode ;
2020-06-23 13:43:52 +02:00
const TestMode = rt . TestMode ;
const ArrayList = std . ArrayList ;
2019-09-03 20:13:26 +02:00
2020-06-02 19:54:41 +02:00
const x86_i686 = CrossTarget {
. cpu_arch = . i386 ,
. os_tag = . freestanding ,
. cpu_model = . { . explicit = & Target . x86 . cpu . _i686 } ,
} ;
2019-09-08 21:48:23 +02:00
2020-06-02 19:54:41 +02:00
pub fn build ( b : * Builder ) ! void {
2020-06-22 21:18:19 +02:00
const target = b . standardTargetOptions ( . { . whitelist = & [ _ ] CrossTarget { x86_i686 } , . default_target = x86_i686 } ) ;
const arch = switch ( target . getCpuArch ( ) ) {
. i386 = > " x86 " ,
else = > unreachable ,
2019-09-08 21:48:23 +02:00
} ;
2019-09-16 23:19:21 +02:00
2020-01-15 17:30:30 +01:00
const fmt_step = b . addFmt ( & [ _ ] [ ] const u8 {
" build.zig " ,
" src " ,
" test " ,
} ) ;
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 " ;
const constants_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { arch_root , arch , " constants.zig " } ) ;
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 " } ) ;
const output_iso = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . exe_dir , " pluto.iso " } ) ;
const iso_dir_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . exe_dir , " iso " } ) ;
const boot_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . exe_dir , " iso " , " boot " } ) ;
const modules_path = try fs . path . join ( b . allocator , & [ _ ] [ ] const u8 { b . exe_dir , " 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-01-09 14:08:00 +01:00
2019-09-17 20:00:33 +02:00
const build_mode = b . standardReleaseOptions ( ) ;
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 ) ;
2019-09-16 23:19:21 +02:00
exec . addPackagePath ( " constants " , constants_path ) ;
2020-01-09 14:08:00 +01:00
exec . setOutputDir ( b . cache_root ) ;
2020-06-23 13:43:52 +02:00
exec . addBuildOption ( TestMode , " test_mode " , test_mode ) ;
2020-01-09 14:08:00 +01:00
exec . setBuildMode ( build_mode ) ;
2020-06-02 19:41:54 +02:00
exec . setLinkerScriptPath ( 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 ( ) ) {
2020-07-25 12:18:19 +02:00
. i386 = > b . addSystemCommand ( & [ _ ] [ ] const u8 { " ./makeiso.sh " , boot_path , modules_path , iso_dir_path , exec . getOutputPath ( ) , 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-07-25 12:18:19 +02:00
var ramdisk_files_al = ArrayList ( [ ] const u8 ) . init ( b . allocator ) ;
defer ramdisk_files_al . deinit ( ) ;
// Add some test files for the ramdisk runtime tests
if ( test_mode = = . Initialisation ) {
try ramdisk_files_al . append ( " test/ramdisk_test1.txt " ) ;
try ramdisk_files_al . append ( " test/ramdisk_test2.txt " ) ;
}
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-08-06 19:13:53 +02:00
const mock_path = " ../../test/mock/kernel/ " ;
const arch_mock_path = " ../../../../test/mock/kernel/ " ;
2020-06-23 13:43:52 +02:00
const unit_tests = b . addTest ( main_src ) ;
unit_tests . setBuildMode ( build_mode ) ;
unit_tests . setMainPkgPath ( " . " ) ;
unit_tests . addPackagePath ( " constants " , constants_path ) ;
unit_tests . addBuildOption ( TestMode , " test_mode " , test_mode ) ;
unit_tests . addBuildOption ( [ ] const u8 , " mock_path " , mock_path ) ;
unit_tests . addBuildOption ( [ ] const u8 , " arch_mock_path " , arch_mock_path ) ;
if ( builtin . os . tag ! = . windows ) {
unit_tests . enable_qemu = true ;
2020-01-09 14:08:00 +01:00
}
2019-04-17 18:40:26 +02:00
2020-06-23 13:43:52 +02:00
unit_tests . setTarget ( . { . cpu_arch = target . cpu_arch } ) ;
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 " ) ;
const build_mode_str = switch ( build_mode ) {
. Debug = > " " ,
. ReleaseSafe = > " -Drelease-safe " ,
. ReleaseFast = > " -Drelease-fast " ,
. ReleaseSmall = > " -Drelease-small " ,
2019-09-16 23:19:21 +02:00
} ;
2020-06-23 13:43:52 +02:00
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 " ) ;
}
var qemu_args = qemu_args_al . toOwnedSlice ( ) ;
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 " ) ;
2020-01-09 14:08:00 +01:00
const symbol_file_arg = try std . mem . join ( b . allocator , " " , & [ _ ] [ ] const u8 { " symbol-file " , exec . getOutputPath ( ) } ) ;
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
/// 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
builder : * Builder ,
/// 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
const Error = ( error { EndOfStream } | | File . ReadError | | File . GetPosError | | Allocator . Error | | File . WriteError | | File . OpenError ) ;
///
/// 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 {
// 1MB, don't think the ram disk should be very big
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 ) ) ;
try ramdisk . writer ( ) . writeInt ( Usize , @truncate ( Usize , self . files . len ) , endian ) ;
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 ) ) ;
try ramdisk . writer ( ) . writeInt ( Usize , @truncate ( Usize , file_path [ file_name_index . . ] . len ) , endian ) ;
// Name
try ramdisk . writer ( ) . writeAll ( file_path [ file_name_index . . ] ) ;
// Length
std . debug . assert ( file_content . len < std . math . maxInt ( Usize ) ) ;
try ramdisk . writer ( ) . writeInt ( Usize , @truncate ( Usize , file_content . len ) , endian ) ;
// 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 ;
}
}
///
/// The make function that is called by the builder. This will create the qemu process with the
/// stdout as a Pipe. Then create the read thread to read the logs from the qemu stdout. Then
/// will call the test function to test a specifics part of the OS defined by the test mode.
///
/// 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 {
const self = @fieldParentPtr ( RamdiskStep , " step " , step ) ;
switch ( self . target . getCpuArch ( ) ) {
. i386 = > try writeRamdisk ( u32 , self ) ,
else = > unreachable ,
}
}
///
/// Create a ramdisk step.
///
/// Argument:
/// IN builder: *Builder - The build builder.
/// IN target: CrossTarget - The target for the build.
/// 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.
///
pub fn create ( builder : * Builder , target : CrossTarget , files : [ ] const [ ] const u8 , out_file_path : [ ] const u8 ) * RamdiskStep {
const ramdisk_step = builder . allocator . create ( RamdiskStep ) catch unreachable ;
ramdisk_step . * = . {
. step = Step . init ( . Custom , builder . fmt ( " Ramdisk " , . { } ) , builder . allocator , make ) ,
. builder = builder ,
. target = target ,
. files = files ,
. out_file_path = out_file_path ,
} ;
return ramdisk_step ;
}
} ;