os-test-framework/Makefile
2018-07-28 23:41:03 +02:00

444 lines
20 KiB
Makefile
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 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/checkerboard_800x600.xbm \
build/checkerboard_1024x768.png \
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.gdisk \
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 \
build/bochsrc \
build/bochscontinue \
build/twm_cfg \
build/virtualbox.img \
${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
os_partition_size_sectors = 717 # 720 - start
# CHS parameters for 1.44 MB floppy disk
os_floppy_chs_h = 2
os_floppy_chs_s = 9
.PHONY: all
# all: os.arm.disasm
all: ${os_filename} build/os.ndisasm.disasm build/os.reasm.asm build/os.file build/os.gdisk build/os.offsets build/os.hex_with_offsets .gitignore build/check_makefile ${more_offset_dec} ${more_offset_hex}
build/makefile_w_arnings: | $${@D}
${built_files}: | $${@D}
build/makefile_w_arnings: Makefile
@unset MAKEFLAGS MAKELEVEL MAKE_TERMERR MFLAGS; 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
@unset MAKEFLAGS MAKELEVEL MAKE_TERMERR MFLAGS; 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 '^[[:space:]]*$$' | sort > $@
build/makefile_built_files: build/check_makefile_w_arnings
@echo ${built_files} | tr ' ' '\n' | grep -v '^[[:space:]]*$$' | sort > $@
build/makefile_phony: build/makefile_database_files build/check_makefile_w_arnings
@sed -n -e 's/^\.PHONY: \(.*\)$$/\1/p' $< | tr ' ' '\n' | grep -v '^[[:space:]]*$$' | sort > $@
build/makefile_targets: build/makefile_database_files build/check_makefile_w_arnings
@grep -E -v '^([[:space:]]|#|\.|$$|^[^:]*:$$)' $< | grep '^[^ :]*:' | sed -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
build/os.32k: example-os/os.asm build/check_makefile
nasm -w+macro-params -w+macro-selfref -w+orphan-labels -w+gnu-elf-extensions -o $@ $<
build/os.iso: build/iso_files/os.zip build/iso_files/boot/iso_boot.sys build/check_makefile
mkisofs \
--input-charset utf-8 \
-rock \
-joliet \
-eltorito-catalog boot/boot.cat \
-eltorito-boot boot/iso_boot.sys \
-no-emul-boot \
-boot-load-size 4 \
-pad \
-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} ${4} build/check_makefile
echo $$$$(( ${2} )) | tee $$@
${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)
${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
${eval ${call offset,bytes_iso_size, $$$$(utils/file-length.sh -c build/os.iso), ,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
${eval ${call offset,bytes_zip_size, $$$$(utils/file-length.sh -c build/os.zip), ,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
${eval ${call offset,bytes_zip_start, $${bytes_os_size} - $${bytes_zip_size}, bytes_os_size bytes_zip_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.
${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 = "$@@@${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 ${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"
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
# * the FAT12 should contain the original file
cp $< $@
# 4 sectors loaded when booting from optical media (CD-ROM, …):
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=$@
build/os.zip: build/os.32k build/check_makefile
zip $@ $<
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}: 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 build/os.iso $@
# splice in the first 32k (bootsector and partition table)
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=${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=$$((${bytes_gpt_mirror_end} - 1 )) bs=1 count=1 conv=notrunc of=$@
# patch the partition table
# 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:
# * Delete (the only) partition, eXpert mode, sector aLignment = 1, back to Main menu,
# * New partition (number = 1, start sector, end sector, type 0700)
# * Recovery and transformation options, make Hybrid,
# * add GPT partition #1 to the hybrid MBR, do Not put the EFI partition first,
# * MBR partition type=0x01, bootable=Yes, do Not add extra partitions,
# * Print GPT, print MBR, Write, Proceed.
(printf "d\nx\nl\n1\nm\nn\n1\n${sectors_fat12_start}\n${sectors_fat12_size}\n0700\n"; \
printf "r\nh\n"; \
printf "1\nN\n"; \
printf "01\nY\nN\n"; \
printf "p\no\nw\nY\n") | while read str; do echo "$$str"; printf "\033[1;33m%s\033[m\n" "$$str" >&2; sleep 0.01; done | gdisk $@
# splice in zip at the end
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 $@
build/os.file: ${os_filename} build/check_makefile
file -kr $< > $@
build/os.gdisk: ${os_filename} build/check_makefile
# gdisk commands:
# * Print partition table
# * Recovery and transformation options
# * print prOtective MBR table
# * Quit
printf 'p\nr\no\nq\n' | gdisk $< | tee $@
build/os.offsets: ${offset_names:%=build/offsets/%.hex} build/check_makefile
grep '^' ${offset_names:%=build/offsets/%.hex} | sed -e 's/:/: 0x/' > $@
build/offsets/%.hex: build/offsets/%.dec
printf '%x\n' $$(cat $<) > $@
build/os.hex_with_offsets: ${os_filename} build/os.offsets
hexdump -C $< \
| grep -E -e "($$(cat build/os.offsets | cut -d '=' -f 2 | sed -e 's/^[[:space:]]*0x\(.*\).$$/^\10/' | tr '\n' '|')^)" --color=yes > $@
build/os.ndisasm.disasm: ${os_filename} utils/compact-ndisasm.sh build/check_makefile
./utils/compact-ndisasm.sh $< $@
build/os.reasm.asm: build/os.ndisasm.disasm build/check_makefile
sed -e 's/^[^ ]\+ \+[^ ]\+ \+//' $< > $@
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.
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 $@ 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} build/check_makefile
# arm-none-eabi-objdump --endian=little -marm -b binary -D --adjust-vma=0x8000 $< > $@
.PHONY: clean
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: build/check_makefile
for f in ${built_files}; do echo "/$$f"; done | sort > $@
.PHONY: test
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}: build/test_pass/emu_$$(@F)
build/test_pass/emu_% deploy-screenshots/%.png deploy-screenshots/%-anim.gif: \
${os_filename} \
build/checkerboard_800x600.xbm \
utils/gui-wrapper.sh utils/ansi-screenshots/ansi_screenshot.sh utils/ansi-screenshots/to_ansi.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:test/%=build/test_pass/noemu_%} build/check_makefile
build/test_pass/noemu_zip: ${os_filename} build/check_makefile
unzip -t ${os_filename}
touch $@
build/test_pass/noemu_sizes: build/os.32k ${os_filename} build/check_makefile
test "$$(utils/file-length.sh -c build/os.32k)" = "$$((32*1024))"
test "$$(utils/file-length.sh -c ${os_filename})" = "$$((1440*1024))"
touch $@
# check that the fat filesystem has the correct contents
build/test_pass/noemu_fat12_contents: ${os_filename} ${dep_bytes_fat12_start} build/check_makefile
mdir -i "$<@@${bytes_fat12_start}" :: | grep -E "^os[[:space:]]+zip[[:space:]]+"
touch $@
.PHONY: test/requiring_sudo
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
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=${bytes_fat12_start} $< build/mnt_fat12
ls -l build/mnt_fat12 | grep os.zip
sudo umount build/mnt_fat12
touch $@
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 $@
.PHONY: test/macos
test/macos: all test/noemu test/macos-sh test/macos-sh-x11
.PHONY: test/macos-sh-x11
test/macos-sh-x11:
sudo mkdir -p /tmp/.X11-unix
sudo chmod a+rwxt /tmp/.X11-unix
xvfb :42 & \
sleep 5; \
DISPLAY=:42 xterm -e ./os.bat & \
sleep 5; \
# DISPLAY=:42 import -window root deploy-screenshots/macos-sh-x11.png
screencapture deploy-screenshots/macos-sh-x11-screencapture.png
.PHONY: test/macos-sh
test/macos-sh: build/check_makefile \
build/checkerboard_1024x768.png \
| deploy-screenshots
osascript -e 'tell app "Terminal" to do script "'"$$PWD"'/os.bat"'
sleep 2
osascript -e 'tell app "Terminal" to activate'
sleep 5
(date +%n && sleep 0.2 && date +%n) || true
screencapture deploy-screenshots/screencapture-os-bat.png
./utils/gui-wrapper-mac.sh 1024x768x24 ./test/gui-sh-mac.sh ${os_filename}
# See https://wiki.osdev.org/EFI#Emulation to emulate an UEFI system with qemu, to test the EFI boot from hdd / cd / fd (?).
# Create checkerboard background
build/checkerboard_%.png: build/check_makefile
convert -size "$*" \
tile:pattern:checkerboard \
-auto-level +level-colors 'gray(192),gray(128)' \
$@
build/checkerboard_%.xbm: build/check_makefile
convert -size "$*" \
tile:pattern:checkerboard \
-auto-level \
$@
# Temporary files
build/bochsrc build/bochscontinue build/twm_cfg build/virtualbox.img:
touch $@