diff --git a/.gitignore b/.gitignore index e6df7cb..5a2e6ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,113 @@ +/build/check_makefile +/build/check_makefile_targets +/build/check_makefile_w_arnings +/build/iso_files/boot/iso_boot.sys +/build/iso_files/os.zip +/build/makefile_built_directories +/build/makefile_built_files +/build/makefile_database +/build/makefile_database_files +/build/makefile_file_targets +/build/makefile_non_file_targets +/build/makefile_phony +/build/makefile_targets +/build/makefile_w_arnings +/build/offsets/bytes_fat12_end.dec +/build/offsets/bytes_fat12_end.hex +/build/offsets/bytes_fat12_size.dec +/build/offsets/bytes_fat12_size.hex +/build/offsets/bytes_fat12_start.dec +/build/offsets/bytes_fat12_start.hex +/build/offsets/bytes_gpt_mirror_end.dec +/build/offsets/bytes_gpt_mirror_end.hex +/build/offsets/bytes_gpt_mirror_size.dec +/build/offsets/bytes_gpt_mirror_size.hex +/build/offsets/bytes_gpt_mirror_start.dec +/build/offsets/bytes_gpt_mirror_start.hex +/build/offsets/bytes_header_32k_end.dec +/build/offsets/bytes_header_32k_end.hex +/build/offsets/bytes_header_32k_size.dec +/build/offsets/bytes_header_32k_size.hex +/build/offsets/bytes_header_32k_start.dec +/build/offsets/bytes_header_32k_start.hex +/build/offsets/bytes_iso_end.dec +/build/offsets/bytes_iso_end.hex +/build/offsets/bytes_iso_size.dec +/build/offsets/bytes_iso_size.hex +/build/offsets/bytes_iso_start.dec +/build/offsets/bytes_iso_start.hex +/build/offsets/bytes_mbr_end.dec +/build/offsets/bytes_mbr_end.hex +/build/offsets/bytes_mbr_start.dec +/build/offsets/bytes_mbr_start.hex +/build/offsets/bytes_os_size.dec +/build/offsets/bytes_os_size.hex +/build/offsets/bytes_zip_end.dec +/build/offsets/bytes_zip_end.hex +/build/offsets/bytes_zip_size.dec +/build/offsets/bytes_zip_size.hex +/build/offsets/bytes_zip_start.dec +/build/offsets/bytes_zip_start.hex +/build/offsets/sectors_fat12_size.dec +/build/offsets/sectors_fat12_size.hex +/build/offsets/sectors_fat12_start.dec +/build/offsets/sectors_fat12_start.hex +/build/offsets/sectors_gpt_mirror_size.dec +/build/offsets/sectors_gpt_mirror_size.hex +/build/offsets/sectors_iso_size.dec +/build/offsets/sectors_iso_size.hex +/build/offsets/sectors_os_size.dec +/build/offsets/sectors_os_size.hex +/build/offsets/sectors_zip_size.dec +/build/offsets/sectors_zip_size.hex +/build/offsets/tracks_fat12_size.dec +/build/offsets/tracks_fat12_size.hex +/build/offsets/tracks_gpt_mirror_size.dec +/build/offsets/tracks_gpt_mirror_size.hex +/build/offsets/tracks_iso_size.dec +/build/offsets/tracks_iso_size.hex +/build/offsets/tracks_os_size.dec +/build/offsets/tracks_os_size.hex +/build/offsets/tracks_zip_size.dec +/build/offsets/tracks_zip_size.hex +/build/os.32k +/build/os.fat12 +/build/os.fdisk +/build/os.file +/build/os.hex_with_offsets +/build/os.iso +/build/os.ndisasm.disasm +/build/os.offsets +/build/os.reasm +/build/os.reasm.asm +/build/os.zip +/build/os.zip.adjusted +/build/test_pass/emu_bochs +/build/test_pass/emu_dosbox +/build/test_pass/emu_gui-sh +/build/test_pass/emu_qemu-system-arm +/build/test_pass/emu_qemu-system-i386-cdrom +/build/test_pass/emu_qemu-system-i386-floppy +/build/test_pass/emu_virtualbox +/build/test_pass/noemu_fat12_contents +/build/test_pass/noemu_os.reasm +/build/test_pass/noemu_sizes +/build/test_pass/noemu_zip +/build/test_pass/sudo_fat12_mount +/build/test_pass/sudo_iso_mount +/deploy-screenshots +/deploy-screenshots/bochs-anim.gif +/deploy-screenshots/bochs.png +/deploy-screenshots/dosbox-anim.gif +/deploy-screenshots/dosbox.png +/deploy-screenshots/gui-sh-anim.gif +/deploy-screenshots/gui-sh.png +/deploy-screenshots/qemu-system-arm-anim.gif +/deploy-screenshots/qemu-system-arm.png +/deploy-screenshots/qemu-system-i386-cdrom-anim.gif +/deploy-screenshots/qemu-system-i386-cdrom.png +/deploy-screenshots/qemu-system-i386-floppy-anim.gif +/deploy-screenshots/qemu-system-i386-floppy.png +/deploy-screenshots/virtualbox-anim.gif +/deploy-screenshots/virtualbox.png /os.bat -/os.ndisasm.disasm -/os.reasm.asm -/os.reasm -/os.reasm.disasm -/os.file -/os.fdisk -/os.arm.disasm -/os.zip -/os.zip.adjusted -/os.iso -/os.32k -/os.fat12 -/os.offsets -/os.hex_with_offsets -/iso_files/os.zip -/iso_files/boot/iso_boot.sys -/build/offsets/fat12_start.hex -/build/offsets/fat12_start.dec diff --git a/.travis.yml b/.travis.yml index a634f5e..2468061 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ addons: matrix: include: - env: MODE=self-test # Ensure that the Makefile works, especially with parallel builds. - script: (cd example-os && make -j 10 test) + script: make -j 10 test sudo: true addons: apt: @@ -95,7 +95,7 @@ install: - if test "$MODE" = virtualbox -o "$MODE" = self-test; then ./utils/install-virtualbox.sh; fi script: - - (cd example-os && make test/${MODE}) + - make test/${MODE} - | ./utils/auto-push.sh "https://github.com/jsmaniac/travis-os.git" \ "git@github.com:jsmaniac/travis-os-deploy-artifacts.git" \ diff --git a/Makefile b/Makefile index 26c12d2..c67d32b 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,86 @@ +MAKEFLAGS = --warn-undefined-variables +SHELL = bash -euET -o pipefail -c +.SECONDEXPANSION: + os_filename = os.bat tests_emu = test/qemu-system-i386-floppy test/qemu-system-i386-cdrom test/qemu-system-arm test/virtualbox test/bochs test/gui-sh test/dosbox tests_requiring_sudo = test/fat12_mount test/iso_mount -tests_noemu = test/zip os.reasm test/sizes test/fat12_contents -built_files = $(os_filename) os.ndisasm.disasm os.reasm.asm os.reasm os.reasm.disasm os.file os.fdisk os.arm.disasm os.zip os.zip.adjusted os.iso os.32k os.fat12 os.offsets os.hex_with_offsets iso_files/os.zip iso_files/boot/iso_boot.sys build/offsets/fat12_start.hex build/offsets/fat12_start.dec -# TODO: auto-add the .dec files for each .hex offset. -built_directories = iso_files/boot iso_files build/offsets build/mnt_fat12 build/mnt_iso build +tests_noemu = test/zip test/os.reasm test/sizes test/fat12_contents + +offset_names = bytes_os_size \ + bytes_mbr_start \ + bytes_mbr_end \ + bytes_header_32k_start \ + bytes_header_32k_end \ + bytes_iso_start \ + bytes_iso_end \ + bytes_fat12_start \ + bytes_fat12_end \ + bytes_gpt_mirror_start \ + bytes_gpt_mirror_end \ + bytes_zip_start \ + bytes_zip_end + +more_offset_names = ${offset_names} \ + bytes_fat12_size \ + bytes_gpt_mirror_size \ + bytes_header_32k_size \ + bytes_iso_size \ + bytes_zip_size \ + sectors_fat12_size \ + sectors_fat12_start \ + sectors_gpt_mirror_size \ + sectors_iso_size \ + sectors_os_size \ + sectors_zip_size \ + tracks_fat12_size \ + tracks_gpt_mirror_size \ + tracks_iso_size \ + tracks_os_size \ + tracks_zip_size + +more_offset_dec = ${more_offset_names:%=build/offsets/%.dec} +more_offset_hex = ${more_offset_names:%=build/offsets/%.hex} + +# + os.arm.disasm +# + os.reasm.disasm +built_files = ${os_filename} \ + build/check_makefile \ + build/check_makefile_targets \ + build/check_makefile_w_arnings \ + build/makefile_built_directories \ + build/makefile_built_files \ + build/makefile_database \ + build/makefile_database_files \ + build/makefile_file_targets \ + build/makefile_non_file_targets \ + build/makefile_phony \ + build/makefile_targets \ + build/makefile_w_arnings \ + build/os.ndisasm.disasm \ + build/os.reasm.asm \ + build/os.reasm \ + build/os.file \ + build/os.fdisk \ + build/os.zip \ + build/os.zip.adjusted \ + build/os.iso \ + build/os.32k \ + build/os.fat12 \ + build/os.offsets \ + build/os.hex_with_offsets \ + build/iso_files/os.zip \ + build/iso_files/boot/iso_boot.sys \ + ${more_offset_dec} \ + ${more_offset_hex} \ + ${tests_emu:test/%=build/test_pass/emu_%} \ + ${tests_noemu:test/%=build/test_pass/noemu_%} \ + ${tests_requiring_sudo:test/%=build/test_pass/sudo_%} \ + ${tests_emu:test/%=deploy-screenshots/%.png} \ + ${tests_emu:test/%=deploy-screenshots/%-anim.gif} + +built_directories = build/iso_files/boot build/iso_files build/offsets build/mnt_fat12 build/mnt_iso build/test_pass deploy-screenshots +more_built_directories = ${built_directories} build os_image_size_kb = 1440 os_partition_start_sectors = 3 @@ -15,19 +91,58 @@ os_floppy_chs_s = 9 .PHONY: all # all: os.arm.disasm -all: $(os_filename) os.ndisasm.disasm os.reasm.asm os.file os.fdisk os.offsets os.hex_with_offsets .gitignore Makefile +all: ${os_filename} build/os.ndisasm.disasm build/os.reasm.asm build/os.file build/os.fdisk build/os.offsets build/os.hex_with_offsets .gitignore build/check_makefile ${more_offset_dec} ${more_offset_hex} -../deploy-screenshots $(built_directories): Makefile - mkdir -p $@ - touch $@ +build/makefile_w_arnings: | $${@D} +${built_files}: | $${@D} -iso_files/boot: iso_files +build/makefile_w_arnings: Makefile + @make -n --warn-undefined-variables test 2>$@ 1>/dev/null || make -n --warn-undefined-variables test + +# Check that the file build/makefile_w_arnings is present, and that it does not contain the string "warn". +build/check_makefile_w_arnings: build/makefile_w_arnings + @cat build/makefile_w_arnings > /dev/null && (! grep -i warn $<) && touch $@ + +# Check that the declared list of built files matches the list of targets extracted from the Makefile. +build/check_makefile_targets: build/makefile_built_files build/makefile_file_targets build/check_makefile_w_arnings + @diff build/makefile_built_files build/makefile_file_targets && touch $@ + +build/check_makefile: build/check_makefile_w_arnings build/check_makefile_targets + @touch $@ + +build/makefile_database: Makefile build/check_makefile_w_arnings + @make -rpn | sed -n -e '/^# Make data base,/,$$p' > $@ + +build/makefile_database_files: build/makefile_database build/check_makefile_w_arnings + @sed -n -e '/^# Files$$/,/^# files hash-table stats:$$/p' $< > $@ + +build/makefile_built_directories: build/check_makefile_w_arnings + @echo ${more_built_directories} | tr ' ' '\n' | grep -v '^\s*$$' | sort > $@ + +build/makefile_built_files: build/check_makefile_w_arnings + @echo ${built_files} | tr ' ' '\n' | grep -v '^\s*$$' | sort > $@ + +build/makefile_phony: build/makefile_database_files build/check_makefile_w_arnings + @sed -r -n -e 's/^.PHONY: (.*)$$/\1/p' $< | tr ' ' '\n' | grep -v '^\s*$$' | sort > $@ + +build/makefile_targets: build/makefile_database_files build/check_makefile_w_arnings + @grep -E -v '^(\s|#|\.|$$|^[^:]*:$$)' $< | grep '^[^ :]*:' | sed -r -e 's|^([^:]*):.*$$|\1|' | sort > $@ + +build/makefile_non_file_targets: build/makefile_phony build/makefile_built_directories build/check_makefile_w_arnings + @cat build/makefile_phony build/makefile_built_directories | sort > $@ + +build/makefile_file_targets: build/makefile_non_file_targets build/makefile_targets build/check_makefile_w_arnings + @comm -23 build/makefile_targets build/makefile_non_file_targets > $@ + +${built_directories}: build/check_makefile +${more_built_directories}: Makefile + mkdir -p $@ && touch $@ # 32k header of the ISO9660 image -os.32k: os.asm Makefile +build/os.32k: example-os/os.asm build/check_makefile nasm -o $@ $< -os.iso: iso_files/os.zip iso_files/boot/iso_boot.sys Makefile +build/os.iso: build/iso_files/os.zip build/iso_files/boot/iso_boot.sys build/check_makefile mkisofs \ --input-charset utf-8 \ -rock \ @@ -37,57 +152,79 @@ os.iso: iso_files/os.zip iso_files/boot/iso_boot.sys Makefile -no-emul-boot \ -boot-load-size 4 \ -pad \ - -output os.iso \ - ./iso_files/ + -output $@ \ + ./build/iso_files/ # Layout: # MBR; GPT; UNIX sh & MS-DOS batch scripts; ISO9660; FAT12; GPT mirror; ZIP +define offset +tmp_${1} = ${3} +build/offsets/${1}.dec: $${tmp_${1}:%=build/offsets/%.dec} build/check_makefile + echo $$$$(( ${2} )) > $$@ +${1} = $$$$(cat build/offsets/${1}.dec) +dep_${1} = build/offsets/${1}.dec +endef + +define div_round_up +( ( ( ${1} ) + ( ${2} ) - 1 ) / ( ${2} ) ) +endef + sector_size = 512 # should be exact (TODO: make a check) -os_total_size_sectors = ( $(os_image_size_kb)*1024 / $(sector_size) ) -os_total_size_tracks = ( $(os_total_size_sectors) / $(os_floppy_chs_s) ) +${eval ${call offset,bytes_os_size, $${os_image_size_kb} * 1024,, }} +${eval ${call offset,sectors_os_size, $${bytes_os_size} / $${sector_size}, bytes_os_size,}} +${eval ${call offset,tracks_os_size, $${sectors_os_size} / $${os_floppy_chs_s}, sectors_os_size,}} + # round up -iso_size_sectors = ( ( $$(wc -c os.iso | cut -d ' ' -f 1) + $(sector_size) - 1 ) / $(sector_size) ) -iso_size_tracks = ( ( $(iso_size_sectors) + $(os_floppy_chs_s) - 1 ) / $(os_floppy_chs_s) ) +${eval ${call offset,bytes_iso_size, $$$$(wc -c build/os.iso | cut -d ' ' -f 1), ,build/os.iso}} +${eval ${call offset,sectors_iso_size, ${call div_round_up,$${bytes_iso_size},$${sector_size}}, bytes_iso_size,}} +${eval ${call offset,tracks_iso_size, ${call div_round_up,$${sectors_iso_size},$${os_floppy_chs_s}}, sectors_iso_size,}} + # round up -zip_size_sectors = ( ( $$(wc -c os.zip | cut -d ' ' -f 1) + $(sector_size) - 1 ) / $(sector_size) ) -zip_size_tracks = ( ( $(zip_size_sectors) + $(os_floppy_chs_s) - 1 ) / $(os_floppy_chs_s) ) -gpt_mirror_size_sectors = 33 -gpt_mirror_size_tracks = ( ( $(gpt_mirror_size_sectors) + $(os_floppy_chs_s) - 1 ) / $(os_floppy_chs_s) ) -# allocate the remaining sectors, aligned on tracks -fat12_size_tracks = ( ( $(os_total_size_tracks) - $(iso_size_tracks) - $(gpt_mirror_size_tracks) - $(zip_size_tracks) ) ) -fat12_size_sectors = ( $(fat12_size_tracks) * $(os_floppy_chs_s) ) +${eval ${call offset,bytes_zip_size, $$$$(wc -c build/os.zip | cut -d ' ' -f 1), ,build/os.zip}} +${eval ${call offset,sectors_zip_size, ${call div_round_up,$${bytes_zip_size},$${sector_size}}, bytes_zip_size,}} +${eval ${call offset,tracks_zip_size, ${call div_round_up,$${sectors_zip_size},$${os_floppy_chs_s}}, sectors_zip_size,}} + +# round up +${eval ${call offset,sectors_gpt_mirror_size, 33,, }} +${eval ${call offset,tracks_gpt_mirror_size, ${call div_round_up,$${sectors_gpt_mirror_size},$${os_floppy_chs_s}}, sectors_gpt_mirror_size,}} + +# allocate the remaining sectors to the FAT, aligned on tracks +${eval ${call offset,tracks_fat12_size, $${tracks_os_size} - $${tracks_iso_size} - $${tracks_gpt_mirror_size} - $${tracks_zip_size}, tracks_os_size tracks_iso_size tracks_gpt_mirror_size tracks_zip_size,}} +${eval ${call offset,sectors_fat12_size,$${tracks_fat12_size} * $${os_floppy_chs_s}, tracks_fat12_size,}} + # zip should probably have its end aligned, not its start -space_before_zip_bytes = ( $(os_image_size_kb)*1024 - $$(wc -c os.zip | cut -d ' ' -f 1) ) +${eval ${call offset,bytes_zip_start, $${bytes_os_size} - $${bytes_zip_size}, bytes_os_size bytes_zip_size,}} -mbr_start = 0 -mbr_end = 512 -header_32k_start = 0 -header_32k_end = ( 32 * 1024 ) -header_32k_size = ( $(header_32k_end) - $(header_32k_start) ) -iso_start = ( 32 * 1024 ) -iso_end = ( $(iso_size_sectors) * $(sector_size) ) -fat12_start = ( $(iso_size_tracks) * $(os_floppy_chs_s) * $(sector_size) ) -fat12_size = ( $(fat12_size_sectors) * $(sector_size) ) -fat12_end = ( $(fat12_start) + $(fat12_size) ) +${eval ${call offset,bytes_mbr_start, 0,,}} +${eval ${call offset,bytes_mbr_end, 512,,}} +${eval ${call offset,bytes_header_32k_start, 0,,}} +${eval ${call offset,bytes_header_32k_end, 32 * 1024,,}} +${eval ${call offset,bytes_header_32k_size, $${bytes_header_32k_end} - $${bytes_header_32k_start}, bytes_header_32k_end bytes_header_32k_start,}} +${eval ${call offset,bytes_iso_start, 32 * 1024,,}} +${eval ${call offset,bytes_iso_end, $${sectors_iso_size} * $${sector_size}, sectors_iso_size,}} +${eval ${call offset,bytes_fat12_start, $${tracks_iso_size} * $${os_floppy_chs_s} * $${sector_size}, tracks_iso_size,}} +${eval ${call offset,sectors_fat12_start, $${bytes_fat12_start} / $${sector_size}, bytes_fat12_start,}} +${eval ${call offset,bytes_fat12_size, $${sectors_fat12_size} * $${sector_size}, sectors_fat12_size,}} +${eval ${call offset,bytes_fat12_end, $${bytes_fat12_start} + $${bytes_fat12_size}, bytes_fat12_start bytes_fat12_size,}} # It is probably not necessary to align the GPT mirror end on a track boundary. -gpt_mirror_end = ( $(fat12_end) + ( $(gpt_mirror_size_tracks) * $(os_floppy_chs_s) * $(sector_size) ) ) -gpt_mirror_start = ( $(gpt_mirror_end) - ( $(gpt_mirror_size_sectors) * $(sector_size) ) ) -zip_start = $(space_before_zip_bytes) -zip_end = ( $(os_total_size_sectors) * $(sector_size) ) +${eval ${call offset,bytes_gpt_mirror_size, $${sectors_gpt_mirror_size} + $${sector_size}, sectors_gpt_mirror_size,}} +${eval ${call offset,bytes_gpt_mirror_end, $${bytes_fat12_end} + $${bytes_gpt_mirror_size}, bytes_fat12_end bytes_gpt_mirror_size,}} +${eval ${call offset,bytes_gpt_mirror_start, $${bytes_gpt_mirror_end} - $${bytes_gpt_mirror_size}, bytes_gpt_mirror_end bytes_gpt_mirror_size,}} +${eval ${call offset,bytes_zip_end, $${bytes_os_size},, }} -os_fat12_partition = "$@@@$$(( $(fat12_start) ))" -os.fat12: os.zip os.iso Makefile - set -x; dd if=/dev/zero bs=$(sector_size) count=$$(( $(os_total_size_sectors) )) of=$@ +os_fat12_partition = "$@@@${bytes_fat12_start}" +build/os.fat12: build/os.zip ${dep_bytes_fat12_size} ${dep_bytes_fat12_start} ${dep_sectors_os_size} build/check_makefile + set -x; dd if=/dev/zero bs=${sector_size} count=${sectors_os_size} of=$@ set -x; mformat -v "Example OS" \ - -T $$(( $(fat12_size_sectors) )) \ - -h $(os_floppy_chs_h) \ - -s $(os_floppy_chs_s) \ - -i $(os_fat12_partition) - set -x; mcopy -i $(os_fat12_partition) os.zip "::os.zip" + -T ${sectors_fat12_size} \ + -h ${os_floppy_chs_h} \ + -s ${os_floppy_chs_s} \ + -i ${os_fat12_partition} + set -x; mcopy -i ${os_fat12_partition} build/os.zip "::os.zip" -iso_files/os.zip: os.zip iso_files Makefile +build/iso_files/os.zip: build/os.zip build/check_makefile # TODO: make it so that the various file formats are mutual quines: # * the ISO should contain the original file # * the ZIP should contain the original file @@ -95,134 +232,146 @@ iso_files/os.zip: os.zip iso_files Makefile cp $< $@ # 4 sectors loaded when booting from optical media (CD-ROM, …): -iso_files/boot/iso_boot.sys: os.32k iso_files/boot Makefile +build/iso_files/boot/iso_boot.sys: build/os.32k build/check_makefile +# TODO: this copy of the (or alternate) bootsector should contain a Boot Information Table, +# see https://wiki.osdev.org/El-Torito#A_BareBones_Boot_Image_with_Boot_Information_Table dd if=$< bs=512 count=4 of=$@ -os.zip: os.32k Makefile +build/os.zip: build/os.32k build/check_makefile zip $@ $< -os.zip.adjusted: os.zip Makefile - set -x; dd if=/dev/zero bs=1 count=$$(( $(space_before_zip_bytes) )) of=$@ +build/os.zip.adjusted: build/os.zip ${dep_bytes_zip_start} build/check_makefile +# TODO: the ZIP file can end with a variable-length comment, this would allow us to hide the GPT mirrors. + set -x; dd if=/dev/zero bs=1 count=${bytes_zip_start} of=$@ cat $< >> $@ zip --adjust-sfx $@ -$(os_filename): os.32k os.iso os.fat12 os.zip os.zip.adjusted ../deploy-screenshots Makefile +${os_filename}: build/os.32k build/os.iso build/os.fat12 build/os.zip.adjusted \ + ${dep_bytes_header_32k_start} \ + ${dep_bytes_header_32k_size} \ + ${dep_bytes_fat12_start} \ + ${dep_bytes_fat12_size} \ + ${dep_bytes_gpt_mirror_end} \ + ${dep_sectors_fat12_start} \ + ${dep_sectors_fat12_size} \ + ${dep_bytes_zip_start} \ + build/check_makefile rm -f $@ # start with the .iso - cp os.iso $@ + cp build/os.iso $@ # splice in the first 32k (bootsector and partition table) - set -x; dd skip=$$(( $(header_32k_start) )) seek=$$(( $(header_32k_start) )) bs=1 count=$$(( $(header_32k_size) )) conv=notrunc if=os.32k of=$@ + set -x; dd skip=${bytes_header_32k_start} seek=${bytes_header_32k_start} bs=1 count=${bytes_header_32k_size} conv=notrunc if=build/os.32k of=$@ # splice in fat12 - set -x; dd skip=$$(( $(fat12_start) )) seek=$$(( $(fat12_start) )) bs=1 count=$$(( $(fat12_size) )) conv=notrunc if=os.fat12 of=$@ + set -x; dd skip=${bytes_fat12_start} seek=${bytes_fat12_start} bs=1 count=${bytes_fat12_size} conv=notrunc if=build/os.fat12 of=$@ # pad with zeroes to prepare for GPT table - set -x; dd if=/dev/zero seek=$$(( $(gpt_mirror_end) - 1 )) bs=1 count=1 conv=notrunc of=$@ + set -x; dd if=/dev/zero seek=$$((${bytes_gpt_mirror_end} - 1 )) bs=1 count=1 conv=notrunc of=$@ # patch the partition table - printf "p\nd\nn\np\n1\n$$(( $(fat12_start) / $(sector_size) ))\n$$(( $(fat12_size_sectors) ))\nt\n01\na\n1\np\nw\nq\n" | fdisk $@ + printf "p\nd\nn\np\n1\n${sectors_fat12_start}\n${sectors_fat12_size}\nt\n01\na\n1\np\nw\nq\n" | fdisk $@ # Thanks to https://wiki.gentoo.org/wiki/Hybrid_partition_table for showing that gdisk can be used to make a hybrid MBR / GPT. # gdisk commands: recovery, make hybrid, add GPT partition #1 to the hybrid MBR, don't put the EFI partition first, # partition type=0x01, bootable=Y, don't add extra partitions, print GPT, print MBR, write, proceed, quit. printf "r\nh\n1\nN\n01\nY\nN\np\no\nw\nY\nq\n" | gdisk $@ # splice in zip at the end - set -x; dd skip=$$(( $(space_before_zip_bytes) )) seek=$$(( $(space_before_zip_bytes) )) bs=1 conv=notrunc if=os.zip.adjusted of=$@ + set -x; dd skip=${bytes_zip_start} seek=${bytes_zip_start} bs=1 conv=notrunc if=build/os.zip.adjusted of=$@ chmod a+x-w $@ -os.file: $(os_filename) Makefile +build/os.file: ${os_filename} build/check_makefile file -kr $< > $@ -os.fdisk: $(os_filename) Makefile +build/os.fdisk: ${os_filename} build/check_makefile fdisk -l $< > $@ -os.offsets: $(os_filename) os.32k os.iso os.fat12 os.zip Makefile - printf 'total_size = 0x%08x\n' $$(( $(os_total_size_sectors) * $(sector_size) )) > $@ - printf 'mbr_start = 0x%08x\n' $$(( $(mbr_start) )) >> $@ - printf 'mbr_end = 0x%08x\n' $$(( $(mbr_end) )) >> $@ - printf '32k_start = 0x%08x\n' $$(( $(header_32k_start) )) >> $@ - printf '32k_end = 0x%08x\n' $$(( $(header_32k_end) )) >> $@ - printf 'iso_start = 0x%08x\n' $$(( $(iso_start) )) >> $@ - printf 'iso_end = 0x%08x\n' $$(( $(iso_end) )) >> $@ - printf 'fat12_start = 0x%08x\n' $$(( $(fat12_start) )) >> $@ - printf 'fat12_end = 0x%08x\n' $$(( $(fat12_end) )) >> $@ - printf 'gpt_mirror_start = 0x%08x\n' $$(( $(gpt_mirror_start) )) >> $@ - printf 'gpt_mirror_end = 0x%08x\n' $$(( $(gpt_mirror_end) )) >> $@ - printf 'zip_start = 0x%08x\n' $$(( $(zip_start) )) >> $@ - printf 'zip_end = 0x%08x\n' $$(( $(zip_end) )) >> $@ +build/os.offsets: ${offset_names:%=build/offsets/%.hex} build/check_makefile + cat ${offset_names:%=build/offsets/%.hex} > $@ -# TODO: make each offset a separate file, dependencies will be simpler. -build/offsets/fat12_start.hex: os.zip os.iso build/offsets Makefile - printf '0x%08x' $$(( $(fat12_start) )) > $@ +build/offsets/%.hex: build/offsets/%.dec + printf '%x\n' $$(cat $<) > $@ -build/offsets/%.dec: build/offsets/%.hex - printf '%d' $$(cat $<) > $@ - -os.hex_with_offsets: $(os_filename) os.offsets +build/os.hex_with_offsets: ${os_filename} build/os.offsets hexdump -C $< \ - | grep -E -e "($$(cat os.offsets | cut -d '=' -f 2 | sed -r -e 's/^\s*0x(.*).$$/^\10/' | tr '\n' '|')^)" --color=yes > $@ + | grep -E -e "($$(cat build/os.offsets | cut -d '=' -f 2 | sed -r -e 's/^\s*0x(.*).$$/^\10/' | tr '\n' '|')^)" --color=yes > $@ -os.ndisasm.disasm: $(os_filename) Makefile ../utils/compact-ndisasm.sh - ../utils/compact-ndisasm.sh $< $@ +build/os.ndisasm.disasm: ${os_filename} utils/compact-ndisasm.sh build/check_makefile + ./utils/compact-ndisasm.sh $< $@ -os.reasm.asm: os.ndisasm.disasm Makefile +build/os.reasm.asm: build/os.ndisasm.disasm build/check_makefile sed -r -e 's/^[^ ]+ +[^ ]+ +//' $< > $@ -os.reasm: os.reasm.asm $(os_filename) Makefile +build/test_pass/noemu_%.reasm build/%.reasm: build/%.reasm.asm ${os_filename} utils/compact-ndisasm.sh build/check_makefile # For now ignore this test, since we cannot have a reliable re-assembly of arbitrary data. - true + touch build/test_pass/noemu_$*.reasm build/$*.reasm # nasm $< -o $@ -# @echo "diff $@ $(os_filename)" -# @diff $@ $(os_filename) \ -# && echo "Re-assembled file is identical to $(os_filename)" \ -# || (../utils/compact-ndisasm.sh $@ os.reasm.disasm; \ -# echo "Re-assembled file is different from $(os_filename). Use meld os.ndisasm.disasm os.reasm.disasm to see differences."; \ +# @echo "diff $@ ${os_filename}" +# @diff $@ ${os_filename} \ +# && echo "Re-assembled file is identical to ${os_filename}" \ +# || (./utils/compact-ndisasm.sh $@ build/os.reasm.disasm; \ +# echo "Re-assembled file is different from ${os_filename}. Use meld build/os.ndisasm.disasm build/os.reasm.disasm to see differences."; \ # exit 0) -#os.arm.disasm: $(os_filename) Makefile +#os.arm.disasm: ${os_filename} build/check_makefile # arm-none-eabi-objdump --endian=little -marm -b binary -D --adjust-vma=0x8000 $< > $@ .PHONY: clean -clean: Makefile - rm -f $(built_files) - for d in $(built_directories); do if test -e "$$d"; then rmdir "$$d"; fi; done +clean: build/check_makefile + rm -f ${built_files} + for d in $$(echo ${more_built_directories} | tr ' ' '\n' | sort --reverse); do \ + if test -e "$$d"; then \ + rmdir "$$d"; \ + fi; \ + done -.gitignore: Makefile - for f in $(built_files); do echo "/$$f"; done > $@ +.gitignore: build/check_makefile + for f in ${built_files}; do echo "/$$f"; done | sort > $@ .PHONY: test -test: $(tests_emu) $(tests_noemu) $(tests_requiring_sudo) all Makefile +test: ${tests_emu:test/%=build/test_pass/emu_%} \ + ${tests_noemu:test/%=build/test_pass/noemu_%} \ + ${tests_requiring_sudo:test/%=build/test_pass/sudo_%} \ + all \ + build/check_makefile -.PHONY: $(tests_emu) -$(tests_emu): $(os_filename) ../deploy-screenshots Makefile - cd .. && ./utils/gui-wrapper.sh 800x600x24 ./$@.sh example-os/$< +.PHONY: ${tests_emu} +${tests_emu}: build/test_pass/emu_$$(@F) + +build/test_pass/emu_% deploy-screenshots/%.png deploy-screenshots/%-anim.gif: \ + ${os_filename} utils/gui-wrapper.sh test/%.sh build/check_makefile \ + | build/test_pass deploy-screenshots + ./utils/gui-wrapper.sh 800x600x24 ./test/$*.sh $< + touch build/test_pass/emu_$* .PHONY: test/noemu -test/noemu: $(tests_noemu) Makefile +test/noemu: ${tests_noemu:test/%=build/test_pass/noemu_%} build/check_makefile -.PHONY: test/zip -test/zip: $(os_filename) Makefile - unzip -t $(os_filename) +build/test_pass/noemu_zip: ${os_filename} build/check_makefile + unzip -t ${os_filename} + touch $@ -.PHONY: test/sizes -test/sizes: os.32k $(os_filename) Makefile - test "$$(wc -c os.32k)" = "$$((32*1024)) os.32k" - test "$$(wc -c $(os_filename))" = "$$((1440*1024)) $(os_filename)" +build/test_pass/noemu_sizes: build/os.32k ${os_filename} build/check_makefile + test "$$(wc -c build/os.32k)" = "$$((32*1024)) build/os.32k" + test "$$(wc -c ${os_filename})" = "$$((1440*1024)) ${os_filename}" + touch $@ # check that the fat filesystem has the correct contents -test/fat12_contents: $(os_filename) - mdir -i "$<@@$$(( $(fat12_start) ))" :: | grep -E "^os\s+zip\s+" +build/test_pass/noemu_fat12_contents: ${os_filename} ${dep_bytes_fat12_start} build/check_makefile + mdir -i "$<@@${bytes_fat12_start}" :: | grep -E "^os\s+zip\s+" + touch $@ .PHONY: test/requiring_sudo -test/requiring_sudo: $(tests_requiring_sudo) Makefile +test/requiring_sudo: ${tests_requiring_sudo:test/%=build/test_pass/sudo_%} build/check_makefile # check that the fat filesystem can be mounted and has the correct contents -.PHONY: test/fat12_mount -test/fat12_mount: $(os_filename) build/mnt_fat12 build/offsets/fat12_start.dec Makefile +build/test_pass/sudo_fat12_mount: ${os_filename} ${dep_bytes_fat12_start} build/check_makefile | build/mnt_fat12 sudo umount build/mnt_fat12 || true - sudo mount -o loop,ro,offset=$$(cat build/offsets/fat12_start.dec) $< build/mnt_fat12 + sudo mount -o loop,ro,offset=${bytes_fat12_start} $< build/mnt_fat12 ls -l build/mnt_fat12 | grep os.zip sudo umount build/mnt_fat12 + touch $@ -.PHONY: test/iso_mount -test/iso_mount: $(os_filename) build/mnt_iso Makefile +build/test_pass/sudo_iso_mount: ${os_filename} build/check_makefile | build/mnt_iso sudo umount build/mnt_iso || true sudo mount -o loop,ro $< build/mnt_iso ls -l build/mnt_iso | grep os.zip sudo umount build/mnt_iso + touch $@ + +# See https://wiki.osdev.org/EFI#Emulation to emulate an UEFI system with qemu, to test the EFI boot from hdd / cd / fd (?). diff --git a/test/gui-sh.sh b/test/gui-sh.sh index a928da0..8326c52 100755 --- a/test/gui-sh.sh +++ b/test/gui-sh.sh @@ -4,7 +4,10 @@ set -e if test $# -ne 1 || test "$1" = '-h' -o "$1" = '--help'; then echo "Usage: $0 operating_system_file" fi -os_filename="$1" +# Force the path to be relative or absolute, but with at least one / +# Otherwise, the command will be searched in the $PATH, instead of using the +# given file. +os_filename="$(dirname "$1")/$(basename "$1")" xterm -e ${os_filename} & pid=$! diff --git a/test/qemu-system-arm.sh b/test/qemu-system-arm.sh index 147d5c2..757c95f 100755 --- a/test/qemu-system-arm.sh +++ b/test/qemu-system-arm.sh @@ -1,4 +1,5 @@ #!/bin/sh set -e -qemu-system-arm -M help \ No newline at end of file +qemu-system-arm -M help +sleep 5