#!/usr/bin/env bash
export out=${out:-out}
. global/common.sh
noimpl() {
say "$1 is not currently implemented on your system. an exciting chance for you to contribute, no?"
exit 1
}
timestamp() { date "+%H:%M:%S %A"; }
if test "$os$arch$bits" = ""; then
say "set the following environment variables to the appropriate atoms describing your system. for help determining the appropriate atoms, see libk.md"
say ' - $os={lin|fbsd|hai|osx…}'
say ' - $arch={x86|arm|ia64|mips…}'
say ' - $bits={|32|64…}'
exit 1
fi
if test "$1" = "-C"; then
say "precleaning repo"
./clean.sh
fi
# TODO: make it possible for user to change
# default set with environment vars
modules=(kcore kmem kstr kio kgraft kfile)
# compose an arch tuple. this is used in
# places, mostly to select the correct
# version of assembly files for a given
# platform (each has an arch tuple in its
# name, following the linkage specifier)
target=$arch.$os
if test "$bits" != ""; then
target=$target.$bits
fi
# determine whether we have unix or posix
# APIs depending on the operating system.
# presumably if the user is running a bash
# script there is some degree of posix
# support available, but we might still be
# building for windows from within cygwin
# or whatever
case $os in
lin|?bsd|and|dar|osx) posix=yes; unix=yes;;
hai) posix=yes; unix=no;;
*) posix=no; unix=no;;
esac
case $os.$bits in
win.32) bin_fmt=win32;;
win.64) bin_fmt=win64;;
osx.32|dar.32) bin_fmt=macho32;;
osx.64|dar.64) bin_fmt=macho64;;
dos.*) bin_fmt=dosexe;;
none.*) bin_fmt=bin;;
*.32) bin_fmt=elf32;;
*.64) bin_fmt=elf64;;
*) say "cannot determine correct binary format to use for target $target"; exit 1;;
esac
# first, we establish the
# parameters of the build
has cc && _cc=cc
has clang && _cc=clang
has gcc && _cc=gcc # prefer gcc
cc=${cc:-$_cc}
has m4 && _m4=m4
m4=${m4:-$_m4}
has nasm && asm=nasm
has yasm && asm=yasm # prefer yasm
export gen=${gen:-gen}
library=${library:-static} # {static|shared|both}
export verbose=${verbose:-quiet} # {no|quiet|loud}
doc=${doc:-yes}
export doc_html=${doc_html:-yes}
export doc_pdf=${doc_pdf:-yes}
export doc_man=${doc_man:-yes}
case $library in
static) build_static_library=yes
build_shared_library=no;;
shared) build_static_library=no
build_shared_library=yes;;
both) build_static_library=yes
build_shared_library=yes;;
esac
check cc "your C compiler of choice"
check asm "an assembler that takes Intel syntax and nasm-style-macros"
check m4 "the path to your m4 installation"
export build=$(global/build-id.sh)
if test "$p_headers_syscall" = ""; then
case $os in
lin) headers_syscall_search=(
/usr/include/asm/unistd_${bits}.h
/usr/include/asm-generic/unistd.h
/usr/include/*-linux-gnu/asm/unistd_${bits}.h
);;
fbsd) p_headers_syscall_search=(
/usr/include/sys/syscall.h
);;
esac
fi
if test "$p_headers_errno" = ""; then
case $os in
lin) p_headers_errno="${p_headers_errno:-/usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h}";;
fbsd) p_headers_errno="${p_headers_errno:-/usr/include/errno.h}";;
esac
fi
for f in "${headers_syscall_search[@]}"; do
test -e "$f" || continue
p_headers_syscall="$f"
say "using syscall headers at $f"
break;
done
check p_headers_syscall \
'the location of a header defining your syscall numbers'
check p_headers_errno \
'the location of a header defining the values of each errno symbol'
macro_compile_env="-Datom_target_arch=$arch -Datom_target_os=$os -Dtarget_posix=$posix -Dtarget_unix=$unix"
if test "$bits" != ""; then
macro_compile_env="$macro_compile_env -Datom_target_bits=$bits"
fi
if test "$COLORTERM" != ""; then
announce() {
test "$verbose" = "silent" && return
color="$1" cmd="$2"; shift 2;
printf " [1;95m→ [38;5;$color;1m$cmd[m"
for a in "$@"; do
if test ${a:0:1} = "-"; then
printf " [91;3m$a[m";
else
printf " [94;4m$a[m";
fi
done
echo
}
else
announce() { shift; echo " --> " $@; }
fi
# the following function is called to report a command invocation
# the person compiling the library. the first argument should be
# an ansi format string; this is used to color-code the tool being
# launched and thus should be different for each one.
report() { announce $@; shift; $@; }
comp_mac() {
local src=$1
local output=$2
local flags=$3
if test -e "$output"; then
if test ! "$output" -ot "$src"; then
return
fi
fi
$m4 $macro_compile_env -I "$gen" $flags "$src" > "$output"
announce 207 $m4 $macro_compile_env -I "$gen" $flags "$src" \> "$output"
# yes, this is incredibly stupid. if you know a better way, feel
# free to submit a fix. the problem is there's no way to pass >
# to report in such a way that it'll do the right thing, and if
# you just write > it redirects *report's* output, instead of
# m4's. piece of shit that it is, m4 doesn't have any way to emit
# output into a fille - stdout only apparently. tl;dr i hate bash.
}
comp_asm() {
local src=$1
local output=$2
local flags=$3
if test -e "$output"; then
if test ! "$output" -ot "$src"; then
return
fi
fi
report 198 $asm $flags "-f$bin_fmt" -i "$gen" "$src" -o "$output";
}
comp_co() { comp_c $1 $2 "-c -fPIC"; }
comp_c(){
local src=$1
local output=$2
local flags=$3
if test -e "$output"; then
if test ! "$output" -ot "$src"; then
return
fi
fi
# only rebuild the file if the source file is newer
report 213 $cc $src $3 -std=c11 -isystem "$out" -isystem "$gen" -isystem "arch/" -nostdlib "-L$out" "-o$output"
}
scan() { find "$1" -name "$2"; }
say "commencing libk build $build at $(timestamp)"
# set -x
# get type data
mkdir -p $gen
$cc -D_emit_m4_include arch/typesize.c -o $gen/typesize
$gen/typesize > gen/typesize.m
# generate syscall tables
case $os in
lin) grep -h "#define __NR_" $p_headers_syscall | sed 's;^#define __NR_;;' > $gen/calls.tbl;;
fbsd) grep -h "#define SYS_" $p_headers_syscall | sed 's;^#define SYS_;;' | sed 's;[\t ]\+; ;' > $gen/calls.tbl;;
*) noimpl 'system call table generation';;
esac
cat $p_headers_syscall $gen/calls.tbl | cpp -P |
awk -f arch/syscall.awk -v out=s >$gen/system_calls.s
cat $p_headers_syscall $gen/calls.tbl | cpp -P |
awk -f arch/syscall.awk -v out=h >$gen/system_calls.h
# generate errno tables
grep -h "#[ ]*define[ ]\+E" $p_headers_errno | sed 's;^#[\t ]*define[\t ]\+\(E[A-Z0-9]\+\).*$;k_platform_error_\1 \1;' > $gen/error_names.tbl
cat $p_headers_errno $gen/error_names.tbl | cpp -P >$gen/error_numbers.tbl
awk -f arch/errtbl.awk <$gen/error_numbers.tbl >$gen/error_table.h
# generate symbol tables for error handling functions
mkdir -p "$out/k"
awk -f global/gen-conds.awk <global/modules >$out/k/internal.egroup.h
awk -f global/gen-ident.awk <global/modules >$gen/internal.ident.c
comp_co $gen/internal.ident.c $out/internal.ident.o
# first pass: copy all headers into place,
# including ones we need to generate
for mod in ${modules[@]}; do
for h in $(scan $mod '*.h'); do
base=$(basename $h)
cp "$h" "$out/k/$base"
done
for h in $(scan $mod '*.h.m'); do
base=$(basename $h)
dest=${base%%.m}
comp_mac "$h" "$out/k/$dest"
done
done
# second pass: generate manpage, html, and pdf
# versions of the documentation from the md src
if test "$doc" = "yes"; then
global/build-manpage.sh libk.md
for mod in ${modules[@]}; do
for doc in $(scan $mod '*.md'); do
base="$(basename $doc)"
stem="${base%%.md}"
report 177 global/build-manpage.sh "$doc"
done
done
fi
# third pass: compile sources and save the
# resulting object files to $out, tracking
# which is a runtime or function unit. exe's
# will not be compiled until a later pass
fn_objects=()
rt_objects=()
data_objects=( $out/internal.ident.o )
for mod in ${modules[@]}; do
for fn in $(scan $mod '*.fn.c'); do
base="$(basename $fn)"
dest="$out/$mod.${base%%.c}.o"
comp_co "$fn" "$dest"
fn_objects+=("$dest")
done
for rt in $(scan $mod '*.rt.c'); do
base="$(basename $rt)"
dest="$out/$mod.${base%%.c}.o"
comp_co "$rt" "$dest"
rt_objects+=("$dest")
done
for fn in $(scan $mod "*.fn.$target.s"); do
base="$(basename $fn)"
dest="$out/$mod.${base%%.s}.o"
comp_asm "$fn" "$dest"
fn_objects+=("$dest")
done
for rt in $(scan $mod "*.rt.$target.s"); do
base="$(basename $rt)"
dest="$out/$mod.${base%%.s}.o"
comp_asm "$rt" "$dest"
rt_objects+=("$dest")
done
done
# fourth pass: link the libraries that are
# configured to be built
if test $build_static_library == yes; then
for obj in ${fn_objects[@]} ${rt_objects[@]} ${data_objects[@]}; do
report 120 ar rc $out/libk.a $obj
done
report 105 ranlib $out/libk.a
fi
if test $build_shared_library == yes; then
report 216 ld -r ${rt_objects[@]} -o $out/boot.o
report 216 ld -shared ${fn_objects[@]} -o $out/libk.so
fi
# fifth pass: compile the executable tools
# against the libraries created in pass 5
for mod in ${modules[@]}; do
for exe in $(scan $mod '*.exe.c'); do
base="$(basename $exe)"
dest="$out/$mod.${base%%.exe.c}"
if test $build_shared_library == yes; then
comp_c "$out/boot.o $exe" "$dest" -lk
else
comp_c "$exe" "$dest" -lk
fi
done
done
set +x
say "all passes finished; build $build complete at $(timestamp)"