libk  build.sh at [09a1767049]

File build.sh artifact 81c1265d8b part of check-in 09a1767049


#!/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 " → [38;5;$color;1m$cmd"
		for a in "$@"; do
			if test ${a:0:1} = "-"; then
				printf " $a";
			else
				printf " $a";
			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)"