do not edit — generated by btf.
git.druid.rocksindexdruid520scsc.sh

sc.sh


#!/usr/bin/env sh
set -e
 
conf="sc.conf"
 
if [ -f "$conf" ]; then
	. ./"$conf"
else
	echo "err: sc.conf does not exist." >&2
	exit 1
fi
 
: "${bin:=}"
: "${libs:=}"
if [ -z "$bin" ] && [ -z "$libs" ]; then
	echo "err: sc.conf must set bin=\"name ...\" and/or libs=\"name ...\" (see sc.conf.example)." >&2
	exit 1
fi
: "${src:=src}"
: "${out:=out}"
: "${docsdir:=docs}"
: "${logdir:=logs}"
: "${prefix:=${PREFIX:-/usr/local}}"
 
# reserved: the six standardized mk/ targets -- a custom h() in sc.conf
# can't be allowed to shadow any of these on disk.
reserved="b c dm dh d dc"
 
# accumulated across every hard/soft cflag, ldflag and passed lib probe
# below -- becomes ccflgs/ldflgs in the generated mk.conf. -I. up front
# so "#include \"config.h\"" resolves from any source file, regardless
# of which dir under $src it lives in.
cfg_ccflags="-I."
cfg_ldflags=""
 
cfgh_tmp="config.h.new"
cat > "$cfgh_tmp" <<EOF
/* generated by sc.sh -- do not edit by hand, re-run sc.sh instead. */
#ifndef SC_CONFIG_H
#define SC_CONFIG_H
 
EOF
 
# uppercase, strip leading dashes (so "-Wall" -> "WALL", not "_WALL"),
# then turn everything else that isn't [A-Za-z0-9_] into an underscore
# (so "sys/types.h" -> "SYS_TYPES_H", "libxml-2.0" -> "LIBXML_2_0", a
# valid, collision-resistant C identifier fragment either way).
_sc_sanitize() {
	printf '%s' "$1" | sed -e 's/^-*//' -e 's/[^A-Za-z0-9_]/_/g' | tr 'a-z' 'A-Z'
}
 
_sc_macro() {
	printf '%s_%s_C' "$1" "$(_sc_sanitize "$2")"
}
 
# resolves "$hard_libs $hard_libs_$os" (etc) via eval -- the only
# POSIX-portable way to do "indirect" variable lookups.
_sc_osvar() {
	eval "printf '%s' \"\${$1:-} \${${1}_${os}:-}\""
}
 
# lib: pkg-config --exists, and on success folds its --cflags/--libs
# into the flags every source file gets built with.
_sc_check_lib() {
	name="$1"; hardness="$2"
	echo "lib: checking $name exists..."
	if pkg-config --exists "$name" 2>/dev/null; then found="yes"; else found="no"; fi
	echo "lib: $name exists? $found."
	if [ "$found" = "no" ]; then
		if [ "$hardness" = "hard" ]; then
			echo "err: please install $name." >&2
			exit 1
		fi
		val=0
	else
		cfg_ccflags="$cfg_ccflags $(pkg-config --cflags "$name")"
		cfg_ldflags="$cfg_ldflags $(pkg-config --libs "$name")"
		val=1
	fi
	echo "#define $(_sc_macro LIB "$name") $val" >> "$cfgh_tmp"
}
 
# header: exists if cpp can preprocess a bare #include of it.
_sc_check_header() {
	name="$1"; hardness="$2"
	echo "header: checking $name exists..."
	if echo "#include <$name>" | cpp $cfg_ccflags >/dev/null 2>&1; then found="yes"; else found="no"; fi
	echo "header: $name exists? $found."
	if [ "$found" = "no" ]; then
		if [ "$hardness" = "hard" ]; then
			echo "err: please install $name." >&2
			exit 1
		fi
		val=0
	else
		val=1
	fi
	echo "#define $(_sc_macro HEADER "$name") $val" >> "$cfgh_tmp"
}
 
# cflag: -Werror makes an unrecognized flag an actual failure instead of
# a silently-ignored warning (some compilers, clang especially, accept
# any -f/-W flag they don't recognize unless told to error on it).
_sc_check_cflag() {
	name="$1"; hardness="$2"
	echo "cflag: checking $name exists..."
	if echo "int main(void) { return 0; }" | $cc $cfg_ccflags $name -Werror -x c -o /dev/null - >/dev/null 2>&1; then
		found="yes"
	else
		found="no"
	fi
	echo "cflag: $name exists? $found."
	if [ "$found" = "no" ]; then
		if [ "$hardness" = "hard" ]; then
			echo "err: please get support for $name." >&2
			exit 1
		fi
		val=0
	else
		cfg_ccflags="$cfg_ccflags $name"
		val=1
	fi
	echo "#define $(_sc_macro CFLAG "$name") $val" >> "$cfgh_tmp"
}
 
_sc_check_ldflag() {
	name="$1"; hardness="$2"
	echo "ldflag: checking $name exists..."
	if echo "int main(void) { return 0; }" | $cc -Werror -x c -o /dev/null - $cfg_ldflags $name >/dev/null 2>&1; then
		found="yes"
	else
		found="no"
	fi
	echo "ldflag: $name exists? $found."
	if [ "$found" = "no" ]; then
		if [ "$hardness" = "hard" ]; then
			echo "err: please get support for $name." >&2
			exit 1
		fi
		val=0
	else
		cfg_ldflags="$cfg_ldflags $name"
		val=1
	fi
	echo "#define $(_sc_macro LDFLAG "$name") $val" >> "$cfgh_tmp"
}
 
# func: proves the symbol exists at link time -- declared with no real
# prototype and only its address taken, so this works regardless of the
# function's actual signature (no header needed, no call made).
_sc_check_func() {
	name="$1"; hardness="$2"
	echo "func: checking $name exists..."
	if printf '#include <stddef.h>\nchar %s(void);\nint main(void) { return (int)(size_t)&%s; }\n' "$name" "$name" \
		| $cc $cfg_ccflags -x c -o /dev/null - $cfg_ldflags >/dev/null 2>&1; then
		found="yes"
	else
		found="no"
	fi
	echo "func: $name exists? $found."
	if [ "$found" = "no" ]; then
		if [ "$hardness" = "hard" ]; then
			echo "err: please provide $name." >&2
			exit 1
		fi
		val=0
	else
		val=1
	fi
	echo "#define $(_sc_macro FUNC "$name") $val" >> "$cfgh_tmp"
}
 
_sc_run_category() {
	kind="$1"; checker="$2"
	for item in $(_sc_osvar "hard_$kind"); do "$checker" "$item" hard; done
	for item in $(_sc_osvar "soft_$kind"); do "$checker" "$item" soft; done
}
 
: "${oss:?err: sc.conf must set oss=\"os1 os2 ...\"}"
os=$(uname -s | tr '[:upper:]' '[:lower:]')
echo "os: checking if $os is supported..."
supported="no"
# every entry in $oss is tried before giving up -- decided only after the
# loop ends, not inside it, so an os further down the list than the first
# still matches instead of being missed by an early exit.
for _os in $oss; do
	if [ "$_os" = "$os" ]; then
		supported="yes"
		break
	fi
done
echo "os: os supported? $supported."
if [ "$supported" = "no" ]; then
	echo "err: please use a supported os out of $oss." >&2
	exit 1
fi
echo "#define $(_sc_macro OS "$os") 1" >> "$cfgh_tmp"
 
# a $cc is only actually REQUIRED when something here will really use
# one: a $bin name resolving to a compiled kind (a "$src/<name>/"
# subdirectory, or a "$src/<name>.c"/".s"/".S" -- see mk/b.sh's own,
# fuller version of this same per-name resolution order for what each
# kind means), the single-bin fallback shape (a lone $bin name with
# NONE of its own sources at all, meaning every .c/.s/.S directly under
# $src compiles into it -- sc's original shape, predating any other
# kind), or any hard_/soft_ cflags/libs/ldflags/funcs probe (every one
# of those compiles a trial program to test with). a project made
# entirely of "$bin" names that are perl/sh/generic scripts, with none
# of those probes declared, never touches a C toolchain at all and
# should not be forced to have one installed just to configure.
needs_cc="no"
for name in $bin; do
	if [ -d "$src/$name" ] || [ -f "$src/$name.c" ] || [ -f "$src/$name.s" ] || [ -f "$src/$name.S" ]; then
		needs_cc="yes"
	elif [ ! -f "$src/$name.pl" ] && [ ! -f "$src/$name.sh" ] && [ ! -f "$src/$name" ]; then
		needs_cc="yes"
	fi
done
# libs=, unlike bin=, has no script-kind fallback at all -- a "library"
# is inherently a compiled-C concept, so ANY libs= entry means a C
# toolchain (and, further below, ar) is required, full stop.
[ -n "$libs" ] && needs_cc="yes"
# _sc_osvar's own return format has a fixed space between its two
# interpolated values (see its own definition above), so even a
# completely unset variable never comes back as a genuinely empty
# string -- checking "-n" directly against its output (or, worse, a
# concatenation of several) is unconditionally true regardless of
# whether anything was actually declared. an unquoted "echo $x" is the
# standard POSIX trick to collapse "nothing but whitespace" down to
# nothing at all: word-splitting drops empty fields, so a value with no
# real words in it becomes truly empty once rejoined.
_allprobes="$(_sc_osvar hard_cflags) $(_sc_osvar soft_cflags) $(_sc_osvar hard_libs) $(_sc_osvar soft_libs) $(_sc_osvar hard_ldflags) $(_sc_osvar soft_ldflags) $(_sc_osvar hard_funcs) $(_sc_osvar soft_funcs)"
if [ -n "$(echo $_allprobes)" ]; then
	needs_cc="yes"
fi
 
cc="${CC:-cc}"
if [ "$needs_cc" = "yes" ]; then
	command -v "$cc" >/dev/null 2>&1 || { echo "err: $cc not found on \$PATH." >&2; exit 1; }
fi
# ar: only libs= (the .a archive step) ever needs it -- bin= never links
# a static archive of its own, so a pure-binary project has no reason to
# need one installed.
ar="${AR:-ar}"
if [ -n "$libs" ]; then
	command -v "$ar" >/dev/null 2>&1 || { echo "err: $ar not found on \$PATH." >&2; exit 1; }
fi
_alllibs="$(_sc_osvar hard_libs) $(_sc_osvar soft_libs)"
if [ -n "$(echo $_alllibs)" ]; then
	command -v pkg-config >/dev/null 2>&1 || { echo "err: pkg-config not found on \$PATH." >&2; exit 1; }
fi
 
# order matters: cflags first (base compiler flags/macros everything
# after builds with) -> libs (folds pkg-config --cflags/--libs into the
# accumulator) -> headers (benefits from both) -> ldflags -> funcs (the
# most demanding probe, gets the full accumulated cflags AND ldflags).
_sc_run_category cflags  _sc_check_cflag
_sc_run_category libs    _sc_check_lib
_sc_run_category headers _sc_check_header
_sc_run_category ldflags _sc_check_ldflag
_sc_run_category funcs   _sc_check_func
 
echo >> "$cfgh_tmp"
echo "#endif" >> "$cfgh_tmp"
 
# a name() { ... } defined in sc.conf becomes mk/name.sh, parsed
# statically out of sc.conf's own text (not via shell introspection,
# which isn't portable) so mk/name.sh gets exactly the body as written.
# brace-depth counted so a body with its own if/case/loop braces still
# extracts whole -- a literal "}" inside a string or comment would still
# confuse it, same known limit as any awk-level brace counter.
_sc_extract_funcs() {
	awk '
		/^[A-Za-z_][A-Za-z0-9_]*\(\)[ \t]*\{/ {
			line = $0
			name = line
			sub(/\(\).*/, "", name)
			rest = line
			sub(/^[^{]*\{/, "", rest)
			depth = 1
			body = ""
			tmp = rest
			depth += gsub(/\{/, "{", tmp) - gsub(/\}/, "}", tmp)
			if (rest != "") body = body rest "\n"
			while (depth > 0) {
				if ((getline line) <= 0) break
				tmp = line
				o = gsub(/\{/, "{", tmp)
				c = gsub(/\}/, "}", tmp)
				depth += o - c
				if (depth <= 0) {
					sub(/\}[ \t]*$/, "", line)
					if (line != "") body = body line "\n"
				} else {
					body = body line "\n"
				}
			}
			print "SC_FUNC_BEGIN " name
			printf "%s", body
			print "SC_FUNC_END"
		}
	' "$conf"
}
 
rm -rf mk.new
mkdir mk.new
 
custom_targets=""
current=""
_sc_extract_funcs | while IFS= read -r line; do
	case "$line" in
		"SC_FUNC_BEGIN "*)
			current="${line#SC_FUNC_BEGIN }"
			case " $reserved " in
				*" $current "*)
					echo "err: sc.conf: '$current' is a reserved target name." >&2
					exit 1
					;;
			esac
			{
				echo "#!/usr/bin/env sh"
				echo "set -e"
				echo
			} > "mk.new/$current.sh"
			echo "$current" >> mk.new/.targets
			;;
		"SC_FUNC_END")
			current=""
			;;
		*)
			[ -n "$current" ] && echo "$line" >> "mk.new/$current.sh"
			;;
	esac
done
[ -f mk.new/.targets ] && custom_targets="$(cat mk.new/.targets; rm -f mk.new/.targets)"
 
cat > mk.new/b.sh <<'BSH'
. ./mk.conf
set -e
 
_b_main() {
mkdir -p "$out"
 
# compiler identification/verbose info -- straight into build.log only
# (a direct append, never through the terminal-visible pipe _sc_capture
# wraps this whole function in), once per build, not per file: exactly
# the kind of thing worth having on hand later without cluttering every
# normal build's terminal output with it. skipped outright when $cc
# isn't even a real command -- a project made entirely of perl/sh/
# generic $bin names has no reason to need one installed at all (see
# sc.sh's own needs_cc check), so this can't assume "-v" will work.
if command -v "$cc" >/dev/null 2>&1; then
	{ echo "compiler info ($cc):"; $cc -v 2>&1; echo; } >> "$logfile"
fi
 
# $bin is one or more space-separated names, each independently
# resolved to a KIND by checking, in order:
#   "$src/<name>/"      compiled, a whole subdirectory (several files).
#   "$src/<name>.c"     compiled, one C file.
#   "$src/<name>.s"/".S" compiled, one assembly file ($cc accepts .s/.S
#                        directly -- no separate assembler needed)
#   "$src/<name>.pl"    a Perl script -- syntax-checked ("perl -c"),
#                       then installed to "$out/<name>" as-is, no
#                       linking: the script itself IS the artifact.
#   "$src/<name>.sh"    a POSIX sh script -- same idea, checked with
#                       "sh -n".
#   "$src/<name>"       a bare, extension-less file -- a pre-built
#                       binary or a script this project doesn't want
#                       sc to try to compile OR syntax-check at all
#                       (unrecognized kind), just installed as-is.
# a lone $bin name with NONE of the above is sc's original single-
# binary shape: every .c/.s/.S directly under "$src" is SHARED (built
# once, linked into every compiled-kind name in $bin) and there is
# nothing name-specific to add, so that one binary is built from all of
# "$src" same as always -- every kind added since changes nothing for
# that existing, common layout. a .c/.s/.S directly under "$src" whose
# OWN basename matches some OTHER $bin name is that name's own compiled
# entry point instead, not shared (see _is_bin_name below) -- give a
# shared file and a per-binary entry point files different basenames if
# both exist, or one silently shadows the other's object.
 
_is_bin_name() {
	for _b in $bin; do
		[ "$_b" = "$1" ] && return 0
	done
	return 1
}
 
# same idea, for libs= -- needed so a source file whose basename matches
# a LIBRARY name is treated as that library's own entry point (below),
# not folded into the generic shared pool either, the identical
# reasoning _is_bin_name already has for $bin names.
_is_lib_name() {
	for _l in $libs; do
		[ "$_l" = "$1" ] && return 0
	done
	return 1
}
 
# f=source o=object d=depfile x=extra cflags (optional -- $picflag for a
# libs= shared-object variant, empty/omitted for a normal, plain object)
# -- sets $need to yes/no and, if yes, actually compiles. each source
# gets its own .o via "$cc -MMD -MP -c" (works for .c, .s, and .S
# alike), which drops a matching .d listing every header it pulled in
# (empty/absent for assembly, which is fine -- the "-f $d" guard below
# just skips it) -- an object is only recompiled if it's missing, older
# than its source, or older than any header the .d says it depends on.
# re-running this (via "mk . b"/"make b" too, all three run this same
# file) with nothing changed then does no work at all beyond each
# binary's/library's final link check. $x is appended, not prepended --
# so it can override an earlier $ccflgs setting (e.g. -fPIC after a
# stray -fno-PIC some hard_cflags= probe added) the same way a
# rightmost flag always wins with a real compiler.
_compile() {
	f="$1"; o="$2"; d="$3"; x="${4:-}"
	need="no"
	if [ ! -f "$o" ]; then
		need="yes"
	elif [ "$f" -nt "$o" ]; then
		need="yes"
	elif [ -f "$d" ]; then
		for dep in $(sed -e 's/^[^:]*://' -e 's/\\$//' "$d"); do
			[ -f "$dep" ] || continue
			if [ "$dep" -nt "$o" ]; then
				need="yes"
				break
			fi
		done
	fi
	if [ "$need" = "yes" ]; then
		echo "cc: $f"
		_sc_run $cc $ccflgs $x -MMD -MP -c "$f" -o "$o"
	fi
}
 
# f=source dest=installed-path checker="checking command"|"" -- for the
# three non-compiled kinds (perl/sh/generic script). no object, no
# link: $dest IS the final artifact, so this is the whole job. $checker
# runs (via _sc_run, so a real syntax error still shows up on the
# terminal live, exactly like a real compile error does) only when
# $dest actually needs updating, same "missing or older than its
# source" rule _compile uses -- re-checking a script's syntax on every
# single build regardless of whether it changed would be needless work
# for a large script. empty $checker (the bare, unrecognized-kind case)
# skips straight to installing -- there is no known way to validate an
# arbitrary pre-built binary or foreign-language script.
_install_script() {
	f="$1"; dest="$2"; checker="$3"
	need="no"
	if [ ! -f "$dest" ]; then
		need="yes"
	elif [ "$f" -nt "$dest" ]; then
		need="yes"
	fi
	if [ "$need" = "yes" ]; then
		if [ -n "$checker" ]; then
			echo "check: $f"
			_sc_run $checker "$f"
		fi
		echo "cp: $f -> $dest"
		_sc_run cp "$f" "$dest"
		chmod +x "$dest"
	fi
}
 
shared_objs=""
shared_relink="no"
for f in "$src"/*.c "$src"/*.s "$src"/*.S; do
	[ -e "$f" ] || continue
	base="$(basename "$f")"; base="${base%.*}"
	_is_bin_name "$base" && continue
	_is_lib_name "$base" && continue
	o="$out/$base.o"
	_compile "$f" "$o" "$out/$base.d"
	[ "$need" = "yes" ] && shared_relink="yes"
	shared_objs="$shared_objs $o"
done
 
# the identical shared pool, a second time, as -fPIC objects -- only
# ever built at all when libs= is non-empty (a plain bin=-only project
# never needs a PIC variant of anything, so this stays free for it).
# separate object suffix (.pic.o/.pic.d, not .o/.d) so the two variants
# of the same source never fight over one .d/.o pair -- both may need
# rebuilding independently of each other (a bin= relink never touches
# this pool, and vice versa).
shared_pic_objs=""
shared_pic_relink="no"
if [ -n "$libs" ]; then
	for f in "$src"/*.c "$src"/*.s "$src"/*.S; do
		[ -e "$f" ] || continue
		base="$(basename "$f")"; base="${base%.*}"
		_is_bin_name "$base" && continue
		_is_lib_name "$base" && continue
		o="$out/$base.pic.o"
		_compile "$f" "$o" "$out/$base.pic.d" "$picflag"
		[ "$need" = "yes" ] && shared_pic_relink="yes"
		shared_pic_objs="$shared_pic_objs $o"
	done
fi
 
for name in $bin; do
	own_objs=""
	relink="$shared_relink"
	kind=""
	if [ -d "$src/$name" ]; then
		kind="compiled"
		mkdir -p "$out/$name"
		for f in "$src/$name"/*.c "$src/$name"/*.s "$src/$name"/*.S; do
			[ -e "$f" ] || continue
			base="$(basename "$f")"; base="${base%.*}"
			o="$out/$name/$base.o"
			_compile "$f" "$o" "$out/$name/$base.d"
			[ "$need" = "yes" ] && relink="yes"
			own_objs="$own_objs $o"
		done
	elif [ -f "$src/$name.c" ]; then
		kind="compiled"; ownsrc="$src/$name.c"
	elif [ -f "$src/$name.s" ]; then
		kind="compiled"; ownsrc="$src/$name.s"
	elif [ -f "$src/$name.S" ]; then
		kind="compiled"; ownsrc="$src/$name.S"
	elif [ -f "$src/$name.pl" ]; then
		kind="script"; ownsrc="$src/$name.pl"; checker="perl -c"
	elif [ -f "$src/$name.sh" ]; then
		kind="script"; ownsrc="$src/$name.sh"; checker="sh -n"
	elif [ -f "$src/$name" ]; then
		kind="script"; ownsrc="$src/$name"; checker=""
	fi
 
	if [ "$kind" = "script" ]; then
		_install_script "$ownsrc" "$out/$name" "$checker"
		continue
	fi
	if [ "$kind" = "compiled" ] && [ -n "$ownsrc" ]; then
		o="$out/$name.o"
		_compile "$ownsrc" "$o" "$out/$name.d"
		[ "$need" = "yes" ] && relink="yes"
		own_objs="$o"
	fi
 
	# an EMPTY $kind here is NOT automatically an error: it's exactly
	# what the single-bin fallback shape looks like (nothing of its own
	# at all under any per-kind name), which is fine as long as
	# $shared_objs has something for it to link against -- sc's
	# original, most common shape, unchanged by any kind added since.
	# it only becomes a real error once $shared_objs is ALSO empty,
	# meaning there is truly nothing anywhere to build this binary from
	# (a plain "src=" pointing at a nonexistent/misconfigured directory
	# falls into this same case for every $bin name at once, and gets
	# exactly this same error, once per name).
	if [ -z "$shared_objs$own_objs" ]; then
		_sc_die "no source found for binary '$name' -- looked for $src/$name/, $src/$name.{c,s,S,pl,sh}, a bare $src/$name, and anything shared directly under $src."
	fi
 
	if [ "$relink" = "yes" ] || [ ! -f "$out/$name" ]; then
		echo "ld: $out/$name"
		_sc_run $cc $ccflgs -o "$out/$name" $shared_objs $own_objs $ldflgs
	fi
done
 
# libs=: same per-name resolution order as $bin (a "$src/<name>/"
# subdirectory, or a "$src/<name>.c"/".s"/".S"), minus the three
# script kinds -- a "library" is inherently something compiled, there
# is no equivalent of installing a perl/sh script as one. unlike a
# binary, EVERY name here needs both a plain object (for the .a) and a
# -fPIC one (for the .so) built in parallel, since the archive and the
# shared object can't share objects with each other.
for name in $libs; do
	own_objs=""; own_pic_objs=""
	relink="$shared_relink"; relink_pic="$shared_pic_relink"
	if [ -d "$src/$name" ]; then
		mkdir -p "$out/$name"
		for f in "$src/$name"/*.c "$src/$name"/*.s "$src/$name"/*.S; do
			[ -e "$f" ] || continue
			base="$(basename "$f")"; base="${base%.*}"
			o="$out/$name/$base.o"
			_compile "$f" "$o" "$out/$name/$base.d"
			[ "$need" = "yes" ] && relink="yes"
			own_objs="$own_objs $o"
			opic="$out/$name/$base.pic.o"
			_compile "$f" "$opic" "$out/$name/$base.pic.d" "$picflag"
			[ "$need" = "yes" ] && relink_pic="yes"
			own_pic_objs="$own_pic_objs $opic"
		done
	else
		ownsrc=""
		for ext in c s S; do
			if [ -f "$src/$name.$ext" ]; then ownsrc="$src/$name.$ext"; break; fi
		done
		if [ -n "$ownsrc" ]; then
			o="$out/$name.o"
			_compile "$ownsrc" "$o" "$out/$name.d"
			[ "$need" = "yes" ] && relink="yes"
			own_objs="$o"
			opic="$out/$name.pic.o"
			_compile "$ownsrc" "$opic" "$out/$name.pic.d" "$picflag"
			[ "$need" = "yes" ] && relink_pic="yes"
			own_pic_objs="$opic"
		fi
	fi
 
	# same "empty own_objs is fine as long as the shared pool has
	# something" fallback bin= already has (see its own comment above)
	# -- a libs= name with nothing of its own draws from the same
	# $src/*.c shared pool bin= does, PIC variant included.
	if [ -z "$shared_objs$own_objs" ]; then
		_sc_die "no source found for library '$name' -- looked for $src/$name/, $src/$name.{c,s,S}, and anything shared directly under $src."
	fi
 
	# ${name}_version=/${name}_desc=, an indirect (eval'd) lookup for
	# the exact same reason _sc_osvar's own comment gives: $name is only
	# known at runtime, so the variable to read ("${name}_version", not
	# a literal name) can't be spelled out in advance. both default to
	# something usable even if sc.conf never set them at all -- mk.conf
	# always defines both (empty string if unset in sc.conf), so this
	# ":-" fallback is really "empty vs. actually-set", not "unset vs.
	# set" (a distinction with no observable difference here).
	ver="$(eval "printf '%s' \"\${${name}_version:-0.0.0}\"")"
	vmajor="${ver%%.*}"
	soname="lib$name.so.$vmajor"
	sofull="lib$name.so.$ver"
 
	if [ "$relink" = "yes" ] || [ ! -f "$out/lib$name.a" ]; then
		echo "ar: $out/lib$name.a"
		rm -f "$out/lib$name.a"
		_sc_run $ar rcs "$out/lib$name.a" $shared_objs $own_objs
	fi
	# soname versioning: $soname (the "-Wl,-soname" baked into the .so
	# itself, and what anything linking "-l$name" resolves to at
	# runtime) carries only the MAJOR version, so a minor/patch bump
	# ships as a drop-in runtime replacement -- $sofull (the real
	# filename) carries the complete version, and $soname/lib$name.so
	# are just symlinks onto it, same convention every real shared
	# library on this system already follows.
	if [ "$relink_pic" = "yes" ] || [ ! -f "$out/$sofull" ]; then
		echo "ld: $out/$sofull"
		_sc_run $cc $ccflgs -shared -Wl,-soname,"$soname" -o "$out/$sofull" $shared_pic_objs $own_pic_objs $ldflgs
		ln -sf "$sofull" "$out/$soname"
		ln -sf "$soname" "$out/lib$name.so"
	fi
 
	# .pc: $prefix is baked in as-is (see sc.sh's own PREFIX=/"${prefix:=
	# ...}" comment) -- whatever the CALLER of sc.sh set it to (a port's
	# own install: phase, typically) is where this library will actually
	# live by the time anything reads this file, so that's what belongs
	# here, not sc's own build-time $out. regenerated whenever either
	# object set actually rebuilt, same as the .a/.so themselves, so a
	# stale prefix from a previous configure never lingers silently.
	pcfile="$out/lib$name.pc"
	desc="$(eval "printf '%s' \"\${${name}_desc:-$name library}\"")"
	if [ "$relink" = "yes" ] || [ "$relink_pic" = "yes" ] || [ ! -f "$pcfile" ]; then
		echo "pc: $pcfile"
		cat > "$pcfile" <<PCEOF
prefix=$prefix
libdir=\${prefix}/lib
includedir=\${prefix}/include
 
Name: $name
Description: $desc
Version: $ver
Libs: -L\${libdir} -l$name
Cflags: -I\${includedir}
PCEOF
	fi
done
}
_sc_capture build.log "$out" _b_main
BSH
 
cat > mk.new/c.sh <<'CSH'
. ./mk.conf
set -e
 
_c_main() {
	# only ever removes $out -- config.h/mk.conf/makefile are configure-
	# time output, re-run sc.sh to regenerate or remove those instead.
	echo "rm: $out"
	_sc_run rm -rf "$out"
}
_sc_capture clean.log "$out" _c_main
CSH
 
cat > mk.new/dh.sh <<'DHSH'
. ./mk.conf
set -e
 
_dh_main() {
	# renders $docsdir/*.btft (recursively, any subfolder included) into
	# $docsdir/html/, mirroring the relative path each source file has
	# under $docsdir -- run from the repo root, same as every other
	# mk/*.sh (via "mk . dh" if the mk tool is installed, or plain
	# "sh mk/dh.sh"/"make dh" otherwise).
	#
	# resolved through PATH, same as mk.conf's cc="${CC:-cc}" -- never a
	# hardcoded install location, so this works wherever btf2html
	# actually is (a different PREFIX, a user-local install, anywhere
	# on $PATH). never the plain generic name "btf" -- that monolithic
	# tool doesn't exist anymore, and even if some other "btf" happened
	# to be on $PATH, it wouldn't take the same flags.
	btf2html="${BTF2HTML:-btf2html}"
	[ -f "$docsdir/style.btfs" ] || _sc_die "$docsdir/style.btfs missing."
	command -v "$btf2html" >/dev/null 2>&1 || _sc_die "$btf2html not found on \$PATH."
	echo "rm: $docsdir/html"
	_sc_run rm -rf "$docsdir/html"
	mkdir -p "$docsdir/html"
	# -o takes an outdir relative to the CURRENT directory, not to the
	# dir being rendered -- "$docsdir/html", not the tool's own "html"
	# default, or this lands one level up (repo root) instead of inside
	# $docsdir/.
	echo "btf2html: $docsdir -> $docsdir/html"
	_sc_run "$btf2html" -f -o "$docsdir/html" "$docsdir"
}
_sc_capture docshtml.log "$docsdir/html" _dh_main
DHSH
 
cat > mk.new/dm.sh <<'DMSH'
. ./mk.conf
set -e
 
_dm_main() {
	# renders $docsdir/*.btft into $docsdir/man/ as troff man pages --
	# no style.btfs involved, a man page has no concept of a stylesheet.
	btf2man="${BTF2MAN:-btf2man}"
	command -v "$btf2man" >/dev/null 2>&1 || _sc_die "$btf2man not found on \$PATH."
	echo "rm: $docsdir/man"
	_sc_run rm -rf "$docsdir/man"
	mkdir -p "$docsdir/man"
	echo "btf2man: $docsdir -> $docsdir/man"
	_sc_run "$btf2man" -o "$docsdir/man" "$docsdir"
}
_sc_capture docsman.log "$docsdir/man" _dm_main
DMSH
 
# "d" (docs, both forms at once) is deliberately just this -- two plain
# sequential calls into dm.sh/dh.sh, not a real make prerequisite graph
# (see this file's own note further down on why mk/*.sh, not make's
# dependency resolution, is the one real interface): each already
# fully manages its own log/manifest independently, so "d" needs
# neither of its own.
cat > mk.new/d.sh <<'DSH'
set -e
sh mk/dm.sh
sh mk/dh.sh
DSH
 
cat > mk.new/dc.sh <<'DCSH'
. ./mk.conf
set -e
 
_dc_main() {
	echo "rm: $docsdir/html $docsdir/man"
	_sc_run rm -rf "$docsdir/html" "$docsdir/man"
}
_sc_capture docsclean.log "$docsdir" _dc_main
DCSH
 
chmod +x mk.new/*.sh
 
cat > mk.conf.new <<EOF
bin="$bin"
libs="$libs"
src="$src"
out="$out"
docsdir="$docsdir"
logdir="$logdir"
prefix="$prefix"
 
cc="\${CC:-$cc}"
ar="\${AR:-$ar}"
ccflgs="$cfg_ccflags"
ldflgs="$cfg_ldflags"
# hardcoded, not probed: -fPIC is universal across every real ELF
# target this tool (and everything it builds) has ever run on -- see
# oss= in sc.conf.example -- so a whole hard_cflags="-fPIC"-style probe
# round-trip through the compiler would only ever confirm what's
# already known. only ever used for libs='s own -fPIC object variant
# (see mk/b.sh's own _compile calls); a bin=-only project never
# references this at all.
picflag="-fPIC"
EOF
# ${name}_version=/${name}_desc=, one pair of lines per libs= name,
# appended (not part of the fixed heredoc above, since $libs's own
# names -- and therefore how many of these lines exist at all -- are
# only known at this point, not fixed in advance). always written, even
# when sc.conf never set either for a given name, so mk/b.sh's own
# "${name}_version:-0.0.0" fallback is choosing between "set" and
# "deliberately empty", never hitting a genuinely undefined variable
# (which "set -e"-style strict shells, and sc's own eval-based indirect
# lookup, both handle far less gracefully than an empty string).
for _l in $libs; do
	_lver="$(eval "printf '%s' \"\${${_l}_version:-}\"")"
	_ldesc="$(eval "printf '%s' \"\${${_l}_desc:-}\"")"
	printf '%s_version="%s"\n' "$_l" "$_lver" >> mk.conf.new
	printf '%s_desc="%s"\n' "$_l" "$_ldesc" >> mk.conf.new
done
cat >> mk.conf.new <<EOF
 
# the default state _sc_capture leaves every mk/*.sh target running in:
# fd 2 pointed straight at the log file (never through the terminal-
# visible pipe) with "set -x" on, so every single shell command the
# script runs from here on -- every "[ -e ... ]", every "basename",
# every variable assignment -- traces into the log, fully expanded,
# with zero terminal noise. the ONLY reason this is safe (and doesn't
# also hide a real compiler error the way redirecting fd 2 for the
# whole run would) is that _sc_run below temporarily undoes exactly
# this, just long enough to run one real external command, for exactly
# the commands that can produce output someone actually needs to see.
_sc_die() {
	# for the script's OWN deliberate "err: ..." messages -- these must
	# reach the terminal unconditionally, the same as _sc_run's wrapped
	# commands, and for the identical reason (fd 2 defaults to log-only).
	exec 2>&1
	echo "err: \$*" >&2
	exit 1
}
 
# runs one real external command (a compiler, btf, rm, ...) with fd 2
# pointed back at the terminal (exec 2>&1: fd 2 -> wherever fd 1 already
# goes, the pipe to tee) and tracing paused, so its own stdout/stderr --
# a real compile error very much included -- shows up live exactly as
# it always did, with no "+ ..." trace line of its own leaking out
# alongside it. the exact command line still lands in the log either
# way, via the explicit "+ ..." append below (BEFORE the toggle, so it's
# always recorded even if the command itself never touches stderr) --
# tracing resumes, and fd 2 goes back to log-only, immediately after.
_sc_run() {
	printf '+ %s\n' "\$*" >> "\$logfile"
	# "set +x" first, "exec 2>&1" second: reversed, "set +x" would trace
	# ITSELF (a command is traced before it runs, and disabling tracing
	# only takes effect once it actually runs) to fd 2 -- which by then
	# would already be pointed at the terminal by "exec 2>&1", leaking a
	# stray "+ set +x" line out. this order keeps that trace (if any) on
	# the log, where fd 2 still points at the moment it fires.
	set +x
	exec 2>&1
	"\$@"
	rc=\$?
	exec 2>>"\$logfile"
	set -x
	return "\$rc"
}
 
# every mk/*.sh target runs its own body through this: \$1 is the log
# file's name under \$logdir, \$2 a directory to report added/removed
# files under (empty string to skip that section entirely -- "mk . c"'s
# ONLY job already IS deleting \$out, so its own removed-files list is
# exactly its point), the rest is a function (defined earlier in that
# same script) to run. sets the global \$logfile before calling it, so
# _sc_run/_sc_die (and, for _b_main, its one-time compiler-info dump)
# know where to append, then hands it "set -x" and fd 2 already pointed
# at the log (see _sc_run's own comment for why that's safe).
#
# the terminal sees exactly what it always did: the target's own
# "cc: ...", "ld: ...", "err: ..." lines (fd 1, untouched, piped to tee
# below) plus whatever a real command _sc_run ran chose to print. run as
# a plain top-level statement (never inside if/&&/||/while, which would
# silence "set -e" for everything inside "\$@"), so a real failure still
# aborts the calling script exactly as before this existed; the real
# exit status survives the "| tee" below via \$rcfile, since a
# pipeline's own \$? would otherwise be tee's, not the function's.
_sc_capture() {
	name="\$1"; mandir="\$2"; shift 2
	mkdir -p "\$logdir"
	logfile="\$logdir/\$name"
	: > "\$logfile"
	if [ -n "\$mandir" ]; then
		if [ -d "\$mandir" ]; then find "\$mandir" -type f 2>/dev/null | sort > "\$logdir/.manifest-before"
		else : > "\$logdir/.manifest-before"
		fi
	fi
	rcfile="\$logdir/.rc.\$\$"
	( ( set -e; exec 2>>"\$logfile"; set -x; "\$@" ); echo "\$?" > "\$rcfile" ) | tee -a "\$logfile"
	rc="\$(cat "\$rcfile" 2>/dev/null)"; rc="\${rc:-1}"
	rm -f "\$rcfile"
	if [ -n "\$mandir" ]; then
		if [ -d "\$mandir" ]; then find "\$mandir" -type f 2>/dev/null | sort > "\$logdir/.manifest-after"
		else : > "\$logdir/.manifest-after"
		fi
		added="\$(comm -13 "\$logdir/.manifest-before" "\$logdir/.manifest-after")"
		removed="\$(comm -23 "\$logdir/.manifest-before" "\$logdir/.manifest-after")"
		# awk, not "wc -l": some wc implementations right-pad a bare count
		# with leading whitespace for column alignment (harmless in a
		# table of several files, but stray -- and inconsistent between
		# a single- and double-digit count, so it LOOKS random -- spliced
		# straight into a one-line sentence like this one).
		nadd=0; [ -n "\$added" ] && nadd="\$(printf '%s\\n' "\$added" | awk 'END{print NR}')"
		nrem=0; [ -n "\$removed" ] && nrem="\$(printf '%s\\n' "\$removed" | awk 'END{print NR}')"
		{
			[ -n "\$added" ] && printf '%s\\n' "\$added" | sed 's/^/+ /'
			[ -n "\$removed" ] && printf '%s\\n' "\$removed" | sed 's/^/- /'
		} >> "\$logfile"
		if [ "\$nadd" -gt 0 ] || [ "\$nrem" -gt 0 ]; then
			echo "ok: \$nadd added, \$nrem removed under \$mandir." | tee -a "\$logfile"
		fi
		rm -f "\$logdir/.manifest-before" "\$logdir/.manifest-after"
	fi
	return "\$rc"
}
EOF
 
# every target below is exactly "sh mk/<name>.sh", nothing more -- no
# prerequisites, no $(OBJS), no per-object rules, no -include *.d. mk/
# is the real interface; this file exists purely so "make b" works for
# people/tools without the mk wrapper installed. "make b", "mk . b" and
# "sh mk/b.sh" run the exact same script and MUST stay equivalent --
# never add a real Make dependency graph here, that logic belongs in
# mk/b.sh itself (it already does its own incremental rebuild via .d
# files). lowercase "makefile" (POSIX make's first search name, ahead
# of "Makefile") and strictly POSIX make syntax only -- no GNU-isms
# (.PHONY, $(wildcard ...), ifeq, %-patterns, include) and no BSD-isms
# (.if, .include, ${VAR:M...}). every recipe below must keep working
# under "pdpmake --posix", not just GNU make.
{
	echo "b:"
	echo "	sh mk/b.sh"
	echo
	echo "c:"
	echo "	sh mk/c.sh"
	echo
	echo "dm:"
	echo "	sh mk/dm.sh"
	echo
	echo "dh:"
	echo "	sh mk/dh.sh"
	echo
	echo "d:"
	echo "	sh mk/d.sh"
	echo
	echo "dc:"
	echo "	sh mk/dc.sh"
	for t in $custom_targets; do
		echo
		echo "$t:"
		echo "	sh mk/$t.sh"
	done
	echo
	echo "default: b"
	echo "build: b"
	echo "clean: c"
	echo "docsman: dm"
	echo "docshtml: dh"
	echo "docs: d"
	echo "docsclean: dc"
} > makefile.new
 
mv -f config.h.new config.h
mv -f mk.conf.new mk.conf
mv -f makefile.new makefile
rm -rf mk
mv mk.new mk
 
echo "sc: configured. run 'make' (or 'mk . b'/'sh mk/b.sh') to build."
powered by btf.