
This allows us to have a deterministic build (given a fixed environment) without having to rely on GIT (the .git is not copied in the Guix build environment), and without having to rely on Guix (which previously had to inject the input hash into the source tree). Guix is therefore "only" responsible for ensuring that the build tools and environment are fixed, but is not strictly necessary to have reproducible builds.
342 lines
16 KiB
Makefile
342 lines
16 KiB
Makefile
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:%=${bld}/offsets/%.dec}
|
||
more_offset_hex = ${more_offset_names:%=${bld}/offsets/%.hex}
|
||
|
||
# + os.arm.disasm
|
||
# + os.reasm.disasm
|
||
built_files += ${os_filename} \
|
||
${bld}/os.ndisasm.disasm \
|
||
${bld}/os.reasm.asm \
|
||
${bld}/os.reasm \
|
||
${bld}/os.file \
|
||
${bld}/os.gdisk \
|
||
${bld}/os.zip \
|
||
${bld}/os.zip.adjusted \
|
||
${bld}/os.iso \
|
||
${bld}/os.32k \
|
||
${bld}/os.fat12 \
|
||
${bld}/os.offsets.hex \
|
||
${bld}/os.offsets.dec \
|
||
${bld}/os.hex_with_offsets \
|
||
${bld}/iso_files/os.zip \
|
||
${bld}/iso_files/boot/iso_boot.sys \
|
||
${bld}/gpt_guid1 \
|
||
${bld}/gpt_guid2 \
|
||
${bld}/os_with_guid_0 \
|
||
${bld}/os_with_guid_hash \
|
||
${more_offset_dec} \
|
||
${more_offset_hex} \
|
||
|
||
# Temporary copies used to adjust timestamps for reproducible builds.
|
||
# These are normally created and deleted within a single target, but
|
||
# could remain if make is interrupted during the build.
|
||
temp_files = ${bld}/iso_files.tmp/os.zip \
|
||
${bld}/iso_files.tmp/boot/iso_boot.sys
|
||
temp_directories = ${bld}/iso_files.tmp \
|
||
${bld}/iso_files.tmp/boot/
|
||
|
||
built_directories += ${bld}/iso_files/boot \
|
||
${bld}/iso_files \
|
||
${bld}/offsets
|
||
|
||
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
|
||
|
||
in-guix: ${os_filename} \
|
||
${bld}/os.ndisasm.disasm \
|
||
${bld}/os.reasm.asm \
|
||
${bld}/os.file \
|
||
${bld}/os.gdisk \
|
||
${bld}/os.offsets.hex \
|
||
${bld}/os.offsets.dec \
|
||
${bld}/os.hex_with_offsets \
|
||
${more_offset_dec} \
|
||
${more_offset_hex} \
|
||
${bld}/check_makefile
|
||
|
||
# 32k header of the ISO9660 image
|
||
${bld}/os.32k: example-os/os.asm ${bld}/check_makefile
|
||
nasm -w+macro-params -w+macro-selfref -w+orphan-labels -w+gnu-elf-extensions -o $@ $<
|
||
|
||
# Circumvent the fact that faketime does not work on system binaries in macos
|
||
./utils/mformat ./utils/mcopy: ${bld}/check_makefile # TODO: depend on the relevant binary
|
||
cp $$(which $$(basename $@)) $@
|
||
chmod u+x $@
|
||
|
||
./utils/mkisofs: ${bld}/check_makefile # TODO: depend on the mkisofs binary
|
||
cp $$(which $$(basename $@) || which xorriso) $@
|
||
chmod u+x $@
|
||
|
||
cp_T_option = $$(if test "$$(uname -s)" = Darwin; then echo ''; else echo '-T'; fi)
|
||
${bld}/os.iso: ${bld}/iso_files/os.zip ${bld}/iso_files/boot/iso_boot.sys ./utils/mkisofs ${bld}/check_makefile
|
||
! test -d ${bld}/iso_files.tmp
|
||
cp -a ${cp_T_option} -- ${bld}/iso_files ${bld}/iso_files.tmp
|
||
find ${bld}/iso_files.tmp -depth -exec touch -t ${commit_timestamp} '{}' ';'
|
||
UTILS="$$PWD/utils" ./utils/faketime.sh ${real_commit_timestamp_iso_8601} \
|
||
sh -c '(cd ./${bld}/iso_files.tmp/ && "$$UTILS/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 ../os.iso \
|
||
.)'
|
||
rm -- ${bld}/iso_files.tmp/os.zip \
|
||
${bld}/iso_files.tmp/boot/iso_boot.sys
|
||
rmdir ${bld}/iso_files.tmp/boot/
|
||
rmdir ${bld}/iso_files.tmp/
|
||
|
||
# Layout:
|
||
# MBR; GPT; UNIX sh & MS-DOS batch scripts; ISO9660; FAT12; GPT mirror; ZIP
|
||
|
||
define offset
|
||
tmp_${1} = ${3}
|
||
${bld}/offsets/${1}.dec: $${tmp_${1}:%=${bld}/offsets/%.dec} ${4} ${bld}/check_makefile
|
||
echo $$$$(( ${2} )) | tee $$@
|
||
${1} = $$$$(cat ${bld}/offsets/${1}.dec)
|
||
dep_${1} = ${bld}/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 ${bld}/os.iso), ,${bld}/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 ${bld}/os.zip), ,${bld}/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}, bytes_os_size,}}
|
||
|
||
os_fat12_partition = "$@@@${bytes_fat12_start}"
|
||
${bld}/os.fat12: ${bld}/os.zip ${dep_bytes_fat12_size} ${dep_bytes_fat12_start} ${dep_sectors_os_size} \
|
||
./utils/mformat ./utils/mcopy ${bld}/check_makefile
|
||
set -x; dd if=/dev/zero bs=${sector_size} count=${sectors_os_size} of=$@
|
||
./utils/faketime.sh ${real_commit_timestamp_iso_8601} ./utils/mformat -v "Example OS" \
|
||
-T ${sectors_fat12_size} \
|
||
-h ${os_floppy_chs_h} \
|
||
-s ${os_floppy_chs_s} \
|
||
-i ${os_fat12_partition}
|
||
./utils/faketime.sh ${real_commit_timestamp_iso_8601} ./utils/mcopy -i ${os_fat12_partition} $< "::os.zip"
|
||
|
||
${bld}/iso_files/os.zip: ${bld}/os.zip ${bld}/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, …):
|
||
${bld}/iso_files/boot/iso_boot.sys: ${bld}/os.32k ${bld}/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=$@
|
||
|
||
${bld}/os.zip: ${bld}/os.32k ${bld}/check_makefile
|
||
# We copy os.32k and alter its timestamp to ensure reproducible
|
||
# builds.
|
||
mkdir -p ${bld}/os.32k.tmp
|
||
cp -a $< ${bld}/os.32k.tmp/os.32k
|
||
touch -t ${commit_timestamp} ${bld}/os.32k.tmp/os.32k
|
||
(cd ${bld}/os.32k.tmp/ && zip -X ../os.zip os.32k)
|
||
rm ${bld}/os.32k.tmp/os.32k
|
||
rmdir ${bld}/os.32k.tmp
|
||
|
||
${bld}/os.zip.adjusted: ${bld}/os.zip ${dep_bytes_zip_start} ${bld}/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 $@
|
||
|
||
gdisk_pipe_commands_slowly=while read str; do echo "$$str"; printf "\033[1;33m%s\033[m\n" "$$str" >&2; sleep 0.01; done
|
||
|
||
# The last digit of guid1 is 1, for guid2 it's 2, and so on.
|
||
# This allows us to have a disk GUID which is different from the partition GUID
|
||
${bld}/gpt_guid%: ${bld}/os_with_guid_0 ${bld}/check_makefile
|
||
sha256sum $< \
|
||
| sed -e 's/^\(.\{8\}\)\(.\{4\}\)\(.\{4\}\)\(.\{4\}\)\(.\{11\}\).*$$/\1-\2-\3-\4-\5$*/' \
|
||
| tr '[:lower:]' '[:upper:]' \
|
||
> $@
|
||
|
||
gpt_disk_guid=${bld}/gpt_guid1
|
||
gpt_partition_guid=${bld}/gpt_guid2
|
||
|
||
${bld}/os_with_guid_0: ${bld}/os.32k ${bld}/os.iso ${bld}/os.fat12 \
|
||
${dep_bytes_header_32k_start} \
|
||
${dep_bytes_header_32k_size} \
|
||
${dep_bytes_fat12_start} \
|
||
${dep_bytes_fat12_size} \
|
||
${dep_bytes_gpt_mirror_start} \
|
||
${dep_bytes_gpt_mirror_end} \
|
||
${dep_sectors_fat12_start} \
|
||
${dep_sectors_fat12_size} \
|
||
${bld}/check_makefile
|
||
rm -f $@
|
||
# start with the .iso
|
||
cp ${bld}/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=${bld}/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=${bld}/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,
|
||
# * back to Main menu,
|
||
# * eXpert mode,
|
||
# * Change partition GUID, the GUID itself,
|
||
# * change disk GUID, the GUID itself,
|
||
# * Print GPT, print prOtective MBR, Write, Proceed.
|
||
(if test "$$(uname -o)" = "Cygwin"; then printf "Y\n"; fi; \
|
||
printf "d\nx\nl\n1\nm\n"; \
|
||
printf "n\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 "x\n"; \
|
||
printf "g\n00000000-0000-0000-0000-000000000000\n"; \
|
||
printf "c\n00000000-0000-0000-0000-000000000000\n"; \
|
||
printf "p\no\nw\nY\n") | ${gdisk_pipe_commands_slowly} | gdisk $@
|
||
|
||
${bld}/os_with_guid_hash: ${bld}/os_with_guid_0 \
|
||
${gpt_disk_guid} ${gpt_partition_guid} \
|
||
${bld}/check_makefile
|
||
cp $< $@
|
||
# gdisk commands:
|
||
# * eXpert mode,
|
||
# * Change partition GUID, the GUID itself,
|
||
# * change disk GUID, the GUID itself,
|
||
# * Print GPT, print prOtective MBR, Write, Proceed.
|
||
(if test "$$(uname -o)" = "Cygwin"; then printf "Y\n"; fi; \
|
||
printf "x\n"; \
|
||
printf "g\n$$(cat ${gpt_disk_guid})\n"; \
|
||
printf "c\n$$(cat ${gpt_partition_guid})\n"; \
|
||
printf "p\no\nw\nY\n") | ${gdisk_pipe_commands_slowly} | gdisk $@
|
||
# Inject MS-DOS newlines (CR+LF) and comments (":: ") in the GUID field of unused partition table entries,
|
||
# so that the part that is to be skipped by MS-DOS does not form a line longer than the MS-DOS maximum
|
||
# line length (8192 excluding CR+LF). $i below is the partition entry number, starting from 1
|
||
# The numbers 55 and 118 are arbitrarily chosen so that the space between two CR+LF is less than 8192.
|
||
for i in 55 118; do \
|
||
printf "\r\n:: %02x" $$i | dd bs=1 seek=$$(( 1024 + ( ($$i) - 1) * 128 + 16)) count=7 conv=notrunc of=$@; \
|
||
printf "\r\n:: %02x" $$i | dd bs=1 seek=$$(( ${bytes_gpt_mirror_start} + ( ($$i) - 1) * 128 + 16)) count=7 conv=notrunc of=$@; \
|
||
done
|
||
${os_filename}: ${bld}/os_with_guid_hash ${bld}/os.zip.adjusted \
|
||
${dep_bytes_zip_start} \
|
||
${bld}/check_makefile
|
||
cp -f $< $@
|
||
# splice in zip at the end
|
||
set -x; dd skip=${bytes_zip_start} seek=${bytes_zip_start} bs=1 conv=notrunc if=${bld}/os.zip.adjusted of=$@
|
||
chmod a+x-w $@
|
||
|
||
${bld}/os.file: ${os_filename} ${bld}/check_makefile
|
||
file -kr $< > $@
|
||
|
||
${bld}/os.gdisk: ${os_filename} ${bld}/check_makefile
|
||
# gdisk commands:
|
||
# * eXpert mode
|
||
# * Print partition table
|
||
# * print detailed Information about the (only) partition
|
||
# * print prOtective MBR table
|
||
# * Quit
|
||
printf '2\nx\np\ni\nr\no\nq\n' | ${gdisk_pipe_commands_slowly} | gdisk $< | tee $@
|
||
|
||
${bld}/os.offsets.hex: ${offset_names:%=${bld}/offsets/%.hex} ${bld}/check_makefile
|
||
grep '^' ${offset_names:%=${bld}/offsets/%.hex} | sed -e 's|^.*/||' -e 's/:/: 0x/' | column -t > $@
|
||
|
||
${bld}/os.offsets.dec: ${offset_names:%=${bld}/offsets/%.dec} ${bld}/check_makefile
|
||
grep '^' ${offset_names:%=${bld}/offsets/%.dec} | sed -e 's|^.*/||' -e 's/:/: /' | column -t > $@
|
||
|
||
${bld}/offsets/%.hex: ${bld}/offsets/%.dec
|
||
printf '%x\n' $$(cat $<) > $@
|
||
|
||
${bld}/os.hex_with_offsets: ${os_filename} ${bld}/os.offsets.hex
|
||
hexdump -C $< \
|
||
| grep -E -e "($$(cat ${bld}/os.offsets.hex | cut -d '=' -f 2 | sed -e 's/^[[:space:]]*0x\(.*\).$$/^\10/' | tr '\n' '|')^)" --color=yes > $@
|
||
|
||
${bld}/os.ndisasm.disasm: ${os_filename} utils/compact-ndisasm.sh ${bld}/check_makefile
|
||
./utils/compact-ndisasm.sh $< $@
|
||
|
||
${bld}/os.reasm.asm: ${bld}/os.ndisasm.disasm ${bld}/check_makefile
|
||
sed -e 's/^[^ ]\+ \+[^ ]\+ \+//' $< > $@
|