joeylib2/make/support/win16/owcc.sh

136 lines
5 KiB
Bash
Executable file

#!/usr/bin/env bash
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# owcc.sh - Open Watcom (component openWatcom) behind the command lines the
# shared build rules give a gcc and an ar, so make/hosted.mk drives it
# unchanged for the win16 port: 32-bit flat-model C for Watcom's Win386
# target, bound to its Windows 3.x supervisor.
#
# owcc.sh cc ARGS... compile (-c), or compile and link a program
# owcc.sh ar rcs LIB OBJ... make a library (Watcom's wlib)
#
# WIN16_WATCOM is the Open Watcom tree (make exports it from
# toolchains/config.mk). Every file is compiled with the Watcom headers,
# floating point as calls into the library (-fpc: no 387 and no emulator
# VxD needed), noAttributes.h force-included (Watcom has no GCC attributes)
# and Watcom's warning W202 off: it reports a static that is never used even
# when a header defines it (a table some of its includers do not read),
# which gcc and clang leave alone, and a project that builds on every other
# port must build here too. The gcc options that mean something else to
# owcc:
# -O2 -O3 owcc's include -oa, which assumes no global is ever reached
# through a pointer (JoeyLib's are), and -on; this is -ox
# (branch, inline, loop and scheduling optimisation, no stack
# checks) and -ot instead
# -MMD -MP Watcom's dependency file names the object and its source as
# given (-ad, -adt, -add); each header is then made a target of
# its own (-MP), so a deleted header does not stop make
# -Wno-error dropped (owcc misreads it); -w is owcc's -w0
# -lm dropped: the maths is in Watcom's C library
# A program (no -c): each source is compiled as above, the objects and
# libraries are linked into a 32-bit image (NAME.rex, its stack STACK_BYTES)
# and that is bound to the supervisor (WIN386.EXT), which makes NAME.EXE, a
# Windows 3.x NE executable whose module name is NAME -- Windows takes two
# programs with one module name for two copies of one program. A resource
# script among the arguments (its icon, make/hosted.mk) is compiled into the
# executable as it is bound.
set -euo pipefail
STACK_BYTES=262144
: "${WIN16_WATCOM:?WIN16_WATCOM is not set; run: ./install.sh install toolchain:win16}"
export WATCOM="$WIN16_WATCOM"
export PATH="$WATCOM/binl64:$PATH"
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
mode="$1"
shift
if [ "$mode" = "ar" ]; then
# ar rcs LIB OBJ...: the library is made new each time (-n), quietly, with
# case-sensitive names and no .bak copy.
shift
exec wlib -q -n -b -c "$@"
fi
[ "$mode" = "cc" ] || { echo "owcc.sh: cc or ar, not '$mode'" >&2; exit 2; }
flags=(-bwin386 -fpc -I"$WATCOM/h" -include "$here/noAttributes.h" -Wc,-wcd=202)
sources=()
libraries=()
resources=""
output=""
compileOnly=0
depFile=0
while [ $# -gt 0 ]; do
case "$1" in
-c) compileOnly=1 ;;
-o) output="$2"; shift ;;
-MMD) depFile=1 ;;
-MP|-Wno-error|-lm) ;;
-O2|-O3) flags+=(-Wc,-obmiler -Wc,-ot -Wc,-s) ;;
*.c) sources+=("$1") ;;
*.a) libraries+=("$1") ;;
*.rc) resources="$1" ;;
*) flags+=("$1") ;;
esac
shift
done
[ -n "$output" ] && [ ${#sources[@]} -gt 0 ] || { echo "owcc.sh: cc needs -o and a source" >&2; exit 2; }
# compileOne SOURCE OBJECT DEPS -- compile one source; with -MMD, append its
# dependency rule, made a rule for TARGET (the object, or the program), to
# DEPS.
compileOne() {
local depArgs=()
if [ "$depFile" = 1 ]; then
depArgs=(-MD -MF "$2.d" -MT "$2" -Wc,-add="$1")
fi
owcc "${flags[@]}" "${depArgs[@]}" -c "$1" -o "$2"
if [ "$depFile" = 1 ]; then
sed "s|^[^:]*:|$target:|" "$2.d" >> "$3"
rm -f "$2.d"
fi
}
# phonyHeaders DEPS -- every prerequisite in DEPS that is not a source, as a
# target of its own (gcc's -MP).
phonyHeaders() {
awk '{ for (i = 3; i <= NF; i++) if ($i !~ /\.c$/ && !($i in seen)) { seen[$i] = 1; printf "\n%s:\n", $i } }' "$1" >> "$1"
}
if [ "$compileOnly" = 1 ]; then
target="$output"
deps="${output%.*}.d"
rm -f "$deps"
compileOne "${sources[0]}" "$output" "$deps"
if [ "$depFile" = 1 ]; then
phonyHeaders "$deps"
fi
exit 0
fi
name="$(basename "${output%.*}")"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
target="$output"
deps="${output%.*}.d"
rm -f "$deps"
objects=()
for source in "${sources[@]}"; do
objects+=("$work/${#objects[@]}.o")
compileOne "$source" "${objects[-1]}" "$deps"
done
if [ "$depFile" = 1 ]; then
phonyHeaders "$deps"
fi
owcc -bwin386 -o "$work/$name.rex" "${objects[@]}" "${libraries[@]}" "-Wl,option stack=$STACK_BYTES"
if [ -n "$resources" ]; then
(cd "$work" && wbind "$name" -q -s "$WATCOM/binw/win386.ext" -R -q -bt=windows "$resources" "$name.exe")
else
(cd "$work" && wbind "$name" -n -q -s "$WATCOM/binw/win386.ext")
fi
mv "$work/$name.exe" "$output"