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

dtc.sh


#!/usr/bin/env sh
# dtc -- devel toolchain. a handful of small, focused commands for
# working with an sc/btf-based project, from scratch through ongoing
# growth: scaffold a new one, check the toolchain it needs is actually
# present, add a binary or a docs page to one that already exists, or
# scaffold a new mp port for something ready to be packaged. dtc never
# bundles copies of the tools it scaffolds FOR (sc, btf2html, btf2man)
# -- every command below relies on them already being on $PATH, real
# dependencies of this tool's own port, same as any other command
# reaching for a compiler or the like.
#
# usage: dtc <command> [args].
# commands: new doctor addbin newdoc port.
set -e
 
_dtc_die() {
	echo "err: $*" >&2
	exit 1
}
 
# reads "key=value" (a plain double-quoted assignment, sc.conf's own
# format) out of sc.conf -- first match, so a later, commented-out, or
# per-target override line never wins over the real one. empty if the
# key isn't set at all, same as sc.sh's own "keeps its default" case --
# every caller applies ITS OWN default the identical way sc.sh does
# (": ${x:=...}"), rather than this function guessing one on its
# behalf.
_dtc_conf_get() {
	sed -n "s/^$1=\"\([^\"]*\)\"\$/\1/p" sc.conf | head -1
}
 
# writes one starter source file for $1=path $2=kind $3=display-name,
# matching exactly the kinds sc.sh's own per-$bin-name resolution
# understands (see sc.conf.example's own bin= documentation for the
# full list) -- shared between "new" (every name defaults to "c", its
# long-standing behavior, unchanged) and "addbin" (which asks).
_dtc_write_stub() {
	path="$1"; kind="$2"; label="$3"
	case "$kind" in
	c)
		# a prototype above main(), the function itself defined below
		# it -- mp's own C (src/*.c) always declares a function before
		# using it, never relies on an implicit/later declaration;
		# return type on its own line, same as every real mp function
		# definition (see e.g. src/mpx.c's own main()).
		cat > "$path" <<EOF
#include <stdio.h>
 
int main(void);
 
int
main(void)
{
	printf("hello from $label\n");
	return 0;
}
EOF
		;;
	s|S)
		# a minimal, dependency-free x86-64 Linux syscall exit -- unlike
		# the C stub above, assembly has no portable "print a greeting"
		# convention to lean on (it would need a different syscall
		# number/calling convention per target OS), so this just proves
		# the binary links and exits cleanly rather than pretending to
		# be portable.
		cat > "$path" <<EOF
.global main
.text
main:
	mov \$60, %rax
	xor %rdi, %rdi
	syscall
EOF
		;;
	pl)
		cat > "$path" <<EOF
#!/usr/bin/env perl
use strict;
use warnings;
 
print "hello from $label\n";
EOF
		chmod +x "$path"
		;;
	sh|bin)
		cat > "$path" <<EOF
#!/bin/sh
echo "hello from $label"
EOF
		chmod +x "$path"
		;;
	esac
}
 
# writes the shared base style.btfs -- factored out since both "new"
# and "newdoc" need to be able to lay one down (a docs/ folder started
# by "new" already has one; "newdoc" run against a project that opted
# out of docs at "new" time, or had its docs/ folder created some other
# way, still needs to create one the first time it adds a page).
_dtc_write_style() {
	cat > "$1" <<'BTFS'
[rule sel="*"]
[prop n="box-sizing"]border-box[e]
[e]
[rule sel="body"]
[prop n="background"]#0a0a0a[e]
[prop n="color"]#b8b8b8[e]
[prop n="font"]14px/1.6 "Courier New", Courier, monospace[e]
[prop n="max-width"]72ch[e]
[prop n="margin"]0 auto[e]
[prop n="padding"]2ch 2ch 8ch 2ch[e]
[e]
[rule sel="h1, h2, h3, h4"]
[prop n="color"]#d0d0d0[e]
[prop n="font"]bold 16px/1.4 "Courier New", Courier, monospace[e]
[prop n="border-bottom"]1px solid #222[e]
[prop n="margin"]1.5em 0 0.5em 0[e]
[e]
[rule sel="a"]
[prop n="color"]#88ccbb[e]
[prop n="text-decoration"]none[e]
[e]
[rule sel="a:hover"]
[prop n="background"]#222[e]
[prop n="color"]#cceecc[e]
[e]
[rule sel="pre, code, tt"]
[prop n="font"]14px/1.4 "Courier New", Courier, monospace[e]
[prop n="background"]#0f0f0f[e]
[prop n="padding"]0.5em[e]
[prop n="overflow-x"]auto[e]
[e]
[rule sel="hr"]
[prop n="border"]0[e]
[prop n="border-top"]1px solid #222[e]
[prop n="margin"]1.5em 0[e]
[e]
[rule sel="ul, ol"]
[prop n="padding-left"]2ch[e]
[e]
[rule sel="table"]
[prop n="border-collapse"]collapse[e]
[e]
[rule sel="th, td"]
[prop n="border"]1px solid #222[e]
[prop n="padding"]0.3em 0.6em[e]
[e]
[rule sel="th"]
[prop n="background"]#111[e]
[prop n="color"]#d0d0d0[e]
[e]
[rule sel="#btf-hdr"]
[prop n="text-align"]right[e]
[prop n="color"]#555[e]
[prop n="font"]11px/1 "Courier New", Courier, monospace[e]
[prop n="border-bottom"]1px solid #1a1a1a[e]
[prop n="padding-bottom"]0.4em[e]
[prop n="margin-bottom"]1.5em[e]
[e]
[rule sel="#btf-hdr a"]
[prop n="color"]#666[e]
[e]
[rule sel="#btf-hdr a:hover"]
[prop n="background"]transparent[e]
[prop n="color"]#999[e]
[e]
[rule sel="#btf-ftr"]
[prop n="text-align"]right[e]
[prop n="color"]#444[e]
[prop n="font"]11px/1 "Courier New", Courier, monospace[e]
[prop n="border-top"]1px solid #1a1a1a[e]
[prop n="padding-top"]0.5em[e]
[prop n="margin-top"]2em[e]
[e]
[rule sel="#btf-ftr a"]
[prop n="color"]#555[e]
[e]
[rule sel="#btf-ftr a:hover"]
[prop n="background"]transparent[e]
[prop n="color"]#888[e]
[e]
[rule sel="#tabs"]
[prop n="display"]flex[e]
[prop n="flex-wrap"]wrap[e]
[prop n="gap"]0.3em 1.2em[e]
[prop n="border-bottom"]1px solid #222[e]
[prop n="padding-bottom"]0.5em[e]
[prop n="margin-bottom"]1.5em[e]
[prop n="font"]13px/1 "Courier New", Courier, monospace[e]
[e]
[rule sel="#tabs a"]
[prop n="color"]#88ccbb[e]
[e]
[rule sel="#tabs a:hover"]
[prop n="background"]#222[e]
[prop n="color"]#cceecc[e]
[e]
BTFS
}
 
_dtc_new() {
	dir="$1"
	if [ -z "$dir" ]; then
		printf 'project directory [.]: '
		read -r dir
		[ -z "$dir" ] && dir='.'
	fi
	mkdir -p "$dir"
	if [ -f "$dir/sc.conf" ]; then
		_dtc_die "$dir/sc.conf already exists -- dtc only scaffolds a NEW project (see 'dtc addbin'/'dtc newdoc' to grow an existing one)."
	fi
 
	printf 'binary name (or several, space-separated) [myprogram]: '
	read -r bin
	[ -z "$bin" ] && bin='myprogram'
 
	printf 'supported OSes [linux freebsd netbsd openbsd]: '
	read -r oss
	[ -z "$oss" ] && oss='linux freebsd netbsd openbsd'
 
	printf 'add a docs/ folder (style.btfs + a starter index.btft)? [Y/n] '
	read -r want_docs
	case "$want_docs" in
		[Nn]*) want_docs='no' ;;
		*) want_docs='yes' ;;
	esac
 
	mkdir -p "$dir/src"
	# "set -- $bin" overwrites the script's own $1/$#/$@ (its CLI args,
	# already-saved into $dir above by this point) -- firstbin is
	# captured explicitly, here, before that happens, since the docs/
	# section further down wants "the project's name" for its starter
	# page and has no other reasonable source for one.
	set -- $bin
	firstbin="$1"
 
	# always src/<name>.c, one name at a time (never src/main.c, even
	# for a single name): a file named after its own $bin entry is that
	# binary's own compiled entry point, never sc's "shared" bucket (see
	# sc.sh's own _is_bin_name) -- a later "dtc addbin" adding a second
	# compiled binary can then never collide with this one's "main",
	# which a shared src/main.c genuinely would (sc.sh links every
	# shared source into every compiled-kind binary, "main" symbol
	# included). "dtc addbin" is how to add one of the other kinds
	# afterward.
	for name in "$@"; do
		[ -e "$dir/src/$name.c" ] && continue
		_dtc_write_stub "$dir/src/$name.c" c "$name"
	done
 
	cat > "$dir/sc.conf" <<EOF
# sc.sh config -- see sc.conf.example (in the sc repo) for every key
# this file could set, with full documentation of each. dtc only ever
# fills in the handful a new project actually needs to get building;
# everything else keeps sc.sh's own defaults until you ask for it.
 
bin="$bin"
oss="$oss"
EOF
 
	# sc.sh's own build output (out/, logs/) and rendered docs
	# (docs/html, docs/man) -- every one of them regenerated from source
	# on the next "sc && make"/"mk . d", never something worth
	# committing. never clobbers an existing .gitignore -- appends
	# instead, same "grow what's already there" reasoning "dtc addbin"
	# already applies to sc.conf's own bin=.
	if [ -e "$dir/.gitignore" ]; then
		{
			echo "/out"
			echo "/docs/html"
			echo "/docs/man"
			echo "/logs"
		} >> "$dir/.gitignore"
	else
		cat > "$dir/.gitignore" <<'EOF'
/out
/docs/html
/docs/man
/logs
EOF
	fi
 
	if [ "$want_docs" = 'yes' ]; then
		mkdir -p "$dir/docs"
		if [ ! -e "$dir/docs/index.btft" ]; then
			cat > "$dir/docs/index.btft" <<EOF
[h1]$firstbin[e]
 
welcome to $firstbin.
EOF
		fi
		[ -e "$dir/docs/style.btfs" ] || _dtc_write_style "$dir/docs/style.btfs"
	fi
 
	echo "dtc: scaffolded $dir. run 'sc' there (or 'cd $dir && sc') to configure it."
}
 
_dtc_doctor() {
	# $CC, if set, is what a real "sc" run in this same environment
	# would actually invoke in place of a bare "cc" -- checking that
	# one specifically (when set) matches sc's own "${CC:-cc}" exactly,
	# rather than reporting a plain "cc" that's present while the one
	# sc would really use is not.
	ccbin="${CC:-cc}"
	echo "dtc doctor:"
	for tool in sc btf2html btf2man mk "$ccbin" perl pkg-config; do
		label="$tool"
		[ "$tool" = "$ccbin" ] && label="cc ($ccbin)"
		if command -v "$tool" >/dev/null 2>&1; then
			echo "ok: $label found."
		else
			echo "warn: $label not found on \$PATH."
		fi
	done
}
 
_dtc_addbin() {
	name="$1"; kind="${2:-c}"
	[ -n "$name" ] || _dtc_die "usage: dtc addbin <name> [c|s|S|pl|sh|bin]."
	[ -f sc.conf ] || _dtc_die "no sc.conf here -- run this from an existing sc project's own directory (see 'dtc new')."
	case "$kind" in
		c|s|S|pl|sh|bin) ;;
		*) _dtc_die "unknown kind '$kind' -- one of c, s, S, pl, sh, bin (bin: a bare, extension-less file -- see sc.conf.example's own bin= documentation)." ;;
	esac
 
	src="$(_dtc_conf_get src)"; : "${src:=src}"
	mkdir -p "$src"
 
	ext=""; [ "$kind" != "bin" ] && ext=".$kind"
	target="$src/$name$ext"
	[ -e "$target" ] && _dtc_die "$target already exists."
	_dtc_write_stub "$target" "$kind" "$name"
 
	grep -q '^bin="' sc.conf \
		|| _dtc_die "sc.conf has no plain bin=\"...\" line to append to (unexpected format) -- add '$name' to it by hand instead."
	awk -v name="$name" '/^bin="/ { sub(/"$/, " " name "\"") } { print }' sc.conf > sc.conf.new
	mv sc.conf.new sc.conf
 
	echo "dtc: added '$name' ($kind) at $target, and appended it to sc.conf's bin=. re-run sc to reconfigure."
}
 
_dtc_newdoc() {
	name="$1"
	[ -n "$name" ] || _dtc_die "usage: dtc newdoc <name>."
	[ -f sc.conf ] || _dtc_die "no sc.conf here -- run this from an existing sc project's own directory (see 'dtc new')."
 
	docsdir="$(_dtc_conf_get docsdir)"; : "${docsdir:=docs}"
	mkdir -p "$docsdir"
	target="$docsdir/$name.btft"
	[ -e "$target" ] && _dtc_die "$target already exists."
	cat > "$target" <<EOF
[h1]$name[e]
 
EOF
	[ -e "$docsdir/style.btfs" ] || _dtc_write_style "$docsdir/style.btfs"
	echo "dtc: added $target."
}
 
# unlike "new"/"addbin"/"newdoc" (which grow a project), this wraps one
# that already exists -- run FROM an sc project's own directory, it
# scaffolds the real, working port for exactly that project's own sc.conf
# (every bin= name, a real "sc; mk . b" build step), not mp new's generic,
# not-yet-written-project template (a plain "cp <name> ...", nothing to
# actually build) -- the two are complementary, not overlapping: mp new
# for a from-scratch or non-sc port, this for "I already have a working
# sc project, now make it installable". category defaults to the literal
# "port" and name to the project's own first bin= entry (dtc port, no
# args, from a project called myprogram, scaffolds port/myprogram) --
# pass <category>/<name> explicitly to override either half.
_dtc_port() {
	catname="$1"
	[ -f sc.conf ] || _dtc_die "no sc.conf here -- run this from an existing sc project's own directory (see 'dtc new') -- for a from-scratch, non-sc port instead, see mp new."
	bin="$(_dtc_conf_get bin)"
	[ -n "$bin" ] || _dtc_die "sc.conf has no bin=\"...\" to build a port from."
	out="$(_dtc_conf_get out)"; : "${out:=out}"
	set -- $bin
	firstbin="$1"
	: "${catname:=port/$firstbin}"
	case "$catname" in
		*/*) ;;
		*) _dtc_die "expected <category>/<name>, e.g. port/$firstbin." ;;
	esac
	name="${catname##*/}"
	[ -e "$catname" ] && _dtc_die "$catname already exists."
	mkdir -p "$catname"
 
	# the project's own git remote, if this is a git checkout with one --
	# a real pkg_fetch instead of a TODO placeholder whenever it's
	# actually knowable, since the whole point of this command is
	# "already have a real project", not "starting from nothing".
	remote=""
	if git rev-parse --git-dir >/dev/null 2>&1; then
		remote="$(git remote get-url origin 2>/dev/null)"
	fi
 
	{
		echo "pkg_name=\"$name\""
		echo "pkg_license=\"fmc\""
		echo "pkg_ver=\"git\""
		echo "pkg_tags=\"$name\""
		echo "pkg_pref=\"50\""
		echo "pkg_deps=\"sc mk %shell %coreutils\""
		if [ -n "$remote" ]; then
			echo "pkg_fetch=\"git:$remote\""
		else
			echo "# TODO: point this at the real repo before pushing."
			echo "pkg_fetch=\"git:git://REPLACE_ME/$name.git\""
		fi
		echo
		echo "install:"
		printf '\tsc\n'
		printf '\tmk . b\n'
		printf '\tmkdir -p $MP_PREFIX/bin\n'
		for b in $bin; do
			printf '\tcp %s/%s $MP_PREFIX/bin/%s\n' "$out" "$b" "$b"
		done
		printf '\tchmod +x'
		for b in $bin; do printf ' $MP_PREFIX/bin/%s' "$b"; done
		printf '\n\n'
		echo "remove:"
		printf '\trm -f'
		for b in $bin; do printf ' $MP_PREFIX/bin/%s' "$b"; done
		printf '\n'
	} > "$catname/pkg.conf"
 
	echo "dtc: scaffolded $catname/pkg.conf from this project's own sc.conf (bin=\"$bin\")."
	if [ -n "$remote" ]; then
		echo "dtc: fetches from $remote -- push there, then 'mp install $name' (or 'mp devtest --dir=. $name' to try the whole install/remove cycle straight from here first)."
	else
		echo "dtc: no git remote found here -- fill in pkg_fetch by hand before pushing."
	fi
}
 
cmd="$1"
[ -n "$cmd" ] && shift
case "$cmd" in
	new)     _dtc_new "$@" ;;
	doctor)  _dtc_doctor "$@" ;;
	addbin)  _dtc_addbin "$@" ;;
	newdoc)  _dtc_newdoc "$@" ;;
	port)    _dtc_port "$@" ;;
	''|help|-h|--help)
		echo "usage: dtc <command> [args]." >&2
		echo "commands: new doctor addbin newdoc port." >&2
		[ -z "$cmd" ] && exit 2
		exit 0
		;;
	*)
		echo "err: unknown command '$cmd'." >&2
		echo "usage: dtc <command> [args]." >&2
		echo "commands: new doctor addbin newdoc port." >&2
		exit 2
		;;
esac
powered by btf.