Skip to content

Instantly share code, notes, and snippets.

@stisa
Created November 6, 2016 20:17
Show Gist options
  • Save stisa/d7f1c536bc8a201c36418e987648bdf8 to your computer and use it in GitHub Desktop.
Save stisa/d7f1c536bc8a201c36418e987648bdf8 to your computer and use it in GitHub Desktop.
#!/bin/sh
# SEE: http://lists.nongnu.org/archive/html/tinycc-devel/2016-10/msg00146.html
# Config
DEFAULT_DEST="win32/reprobuild" # relative to top source dir
TMP_DIR="_tmp" # dir for intermidiate files inside each output dir
# Builds one windows tcc distribution
# args: TARGET_DIR, CC, {I386|X86_64}, [LIBS_CC, LIBS_AR]
# Doesn't touch anything outside of $TARGET_DIR - which must not exist on entry.
# libtcc1 objects are compiled with CC_LIBS and joined into libtcc1.a with AR_LIBS
# if they're missing, then the resulting tcc and tiny_libmaker are used instead,
# which means that CC must be host-native (bootstrap mode and unknown host/CC)
build_one() {
local TARGET_DIR="$1"; local CC="$2"; local CPU="$3"; local CC_LIBS="$4"; local AR_LIBS="$5"
local BOOTSTRAP=; [ -z "$5" ] && BOOTSTRAP=1
$run_dbg mkdir -p "$TARGET_DIR" || return 1
echo "Creating package at '$(real_dir "$TARGET_DIR")'" # real_dir requires existing dir
# Create the dist except the binaries (tcc, libtcc1.a, tiny_libmaker, tiny_impdef)
$run_dbg mkdir "$TARGET_DIR/lib"
$run_dbg cp -r win32/include "$TARGET_DIR/include"
$run_dbg cp include/* "$TARGET_DIR/include"
$run_dbg cp win32/lib/*.def "$TARGET_DIR/lib"
# Build dir for intermediate files
local BLD_DIR="$TARGET_DIR/$TMP_DIR"
mkdir "$BLD_DIR"
echo '
#ifndef CONFIG_TCCDIR
# define CONFIG_TCCDIR "."
#endif
#define GCC_MAJOR 0
#define GCC_MINOR 0
#define TCC_VERSION "0.9.26"
' > "$BLD_DIR/config.h"
# Compile the binaries. We're using ONE_SOURCE since it's simpler to build - no shared libtcc.
local CFLAGS="-Wall"
local PLAT_TARGET="-DTCC_TARGET_PE -DTCC_TARGET_${CPU}"
local TCC_FLAGS="-I$BLD_DIR -DONE_SOURCE" # $BLD_DIR should be quoted but tcc does not like it quoted
[ -n "$BOOTSTRAP" ] && TCC_FLAGS="$TCC_FLAGS -DCONFIG_TCCDIR=\"$TARGET_DIR\""
# just shorter to use
local TLM=win32/tools/tiny_libmaker.c
local TID=win32/tools/tiny_impdef.c
# Use the external $CC to create the executables. Despite the exe suffix,
# depending on $CC and the host, the binaries might not be windows executables.
$run_dbg $CC $CFLAGS $PLAT_TARGET $TCC_FLAGS -o "$TARGET_DIR/tcc.exe" tcc.c &&
$run_dbg $CC $CFLAGS $PLAT_TARGET -o "$TARGET_DIR/tiny_libmaker.exe" $TLM &&
([ -n "$BOOTSTRAP" ] || $run_dbg $CC $CFLAGS $PLAT_TARGET -o "$TARGET_DIR/tiny_impdef.exe" $TID) ||
return 1
# ms compiler leaves .obj
rm *.obj 2> /dev/null
# Done with the executables, but we still need libtcc1.a to make the resulting tcc
# fully functional.
[ -n "$BOOTSTRAP" ] && CC_LIBS="$TARGET_DIR/tcc.exe"
[ -n "$BOOTSTRAP" ] && AR_LIBS="$TARGET_DIR/tiny_libmaker.exe"
local LD=lib; local WLD=win32/lib
local LIBTCC1_SRCS="$LD/libtcc1.c $WLD/chkstk.S $WLD/crt1.c $WLD/wincrt1.c $WLD/dllcrt1.c $WLD/dllmain.c"
[ "$CPU" = "I386" ] &&
LIBTCC1_SRCS="$LIBTCC1_SRCS $LD/alloca86.S $LD/alloca86-bt.S" ||
LIBTCC1_SRCS="$LIBTCC1_SRCS $LD/alloca86_64.S $LD/alloca86_64-bt.S"
make_libtcc1() { # args: source files for libtcc1.a
local objs; local o;
while [ -n "$1" ]; do
o="$BLD_DIR/$(basename $1.o)"
$run_dbg $CC_LIBS $CFLAGS $PLAT_TARGET -o "$o" -c "$1" || return 1
objs="$objs $o" # we'd like to quote $o, but tiny_libmaker doesn't like it
shift; done
$run_dbg $AR_LIBS rcs "$TARGET_DIR/lib/libtcc1.a" $objs || return 1
}
make_libtcc1 $LIBTCC1_SRCS || return 1
# build dummy libm (an empty string also works, but just looks weird)
echo "void _tcc_dummy_libm(){}" | $run_dbg $CC_LIBS -o "$BLD_DIR/libm.o" -c - || return 1
$run_dbg $AR_LIBS rcs "$TARGET_DIR/lib/libm.a" "$BLD_DIR/libm.o" || return 1
# remove temps
[ -n "$TCCREPRO_KEEPTMP" ] || rm -rf $BLD_DIR
}
# args: target dir, initial $cc to create bootstraps or tcc32 and tcc64 dirs as bootstraps
# Expects to be already at $TOP_SRCDIR and that target dir doesn't exist
build_dist() {
local TARGET_DIR="$1"; local BOOT=; local BOOT32=; local BOOT64=
if [ -n "$3" ]; then # use the provided bootstrap tcc's
BOOT32="$2"; BOOT64="$3"
else # Build temp host-native [cross] tcc targetting win32/64 as bootstraps
echo "Building bootstrap tcc's..."
local CC="$2"; BOOT="$TARGET_DIR/_boot"
build_one "$BOOT/win32" "$CC" I386 &&
build_one "$BOOT/win64" "$CC" X86_64 ||
return 1
BOOT32="$BOOT/win32"; BOOT64="$BOOT/win64"; echo ''
fi
echo "Building native Windows tcc distributions..."
local CC32="$BOOT32/tcc.exe"; local AR32="$BOOT32/tiny_libmaker.exe"
local CC64="$BOOT64/tcc.exe"; local AR64="$BOOT64/tiny_libmaker.exe"
# Build the 4 distributions: native 32 and 64, and cross 32->64 and 64->32
build_one "$TARGET_DIR/tcc32" "$CC32" I386 "$CC32" "$AR32" &&
build_one "$TARGET_DIR/tcc64" "$CC64" X86_64 "$CC64" "$AR64" &&
build_one "$TARGET_DIR/xtcc64" "$CC32" X86_64 "$CC64" "$AR64" &&
build_one "$TARGET_DIR/xtcc32" "$CC64" I386 "$CC32" "$AR32" ||
return 1
[ -z "$TCCREPRO_KEEPTMP" ] && [ -n "$BOOT" ] && rm -rf "$BOOT"
echo "Done OK at '$(real_dir "$TARGET_DIR")'"
[ -n "$TCCREPRO_KEEPTMP" ] && echo "Warning: signature includes temp and non-deterministic host files."
echo "Signature: $(signature_dir "$TARGET_DIR")";
}
# uses the output tcc's to rebuild the dist and compare the results
# args: target dir, mode ("32" -> host can run only 32, otherwise can run 32 and 64)
verify_dist() {
local DIST="$1"; local MODE="$2"
local expected="$(signature_dir "$DIST")"
echo ''; echo "Verifying the resulting 32 bit binaries..."
build_dist "$DIST/verify_32" "$DIST/tcc32" "$DIST/xtcc64" &&
[ "$expected" = "$(signature_dir "$DIST/verify_32")" ] ||
(echo "--> Verification 32: ERROR"; false) || return 1
echo "--> Verification 32: OK"
[ -z "$TCCREPRO_KEEPTMP" ] && rm -rf "$DIST/verify_32"
[ "$MODE" = "32" ] && return
echo ''; echo "Verifying the resulting 64 bit binaries..."
build_dist "$DIST/verify_64" "$DIST/xtcc32" "$DIST/tcc64" &&
[ "$expected" = "$(signature_dir "$DIST/verify_64")" ] ||
(echo "--> Verification 64: ERROR"; false) || return 1
echo "--> Verification 64: OK"
[ -z "$TCCREPRO_KEEPTMP" ] && rm -rf "$DIST/verify_64"
}
# Utilities
signature_dir() { # list all files recursively -> sort -> md5 of each -> md5
local sum="$( (echo 1 | md5sum)>/dev/null 2>&1 && echo "md5sum" || echo "md5 -r")" # bsd/osx: "md5 -r"
(cd "$1" && find . -type f | LC_ALL=C sort | xargs $sum | tr -d '*' | tr -s ' ' | $sum | cut -f 1 -d ' ')
}
real_dir() { echo "$(cd "$1" && pwd)"; } # mandatory arg: dir which must already exist
run_dbg=_run_dbg # set to empty to completely bypasss run_dbg
_run_dbg() { [ -n "$TCCREPRO_V" ] && echo "$@"; "$@"; } # echo if dbg, run args
maybe_relative() { # arg: absolute/relative dir, output: relative to pwd or unmodified input
local bn="$(basename "$(real_dir "$1")")"
[ "$(pwd)/win32/$bn" = "$(real_dir "$1")" ] && echo "win32/$bn" || echo "$1"
}
# main
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
echo "Usage: CC=<host-native-compiler> $(basename "$0") [<target-dir>]"
echo "Build tcc for windows, hopefully deterministically regardless of the OS/CC."
echo "Creates two host-native tcc which target win32/64 and uses them to build win-"
echo "native tcc32/64 and also cross 32-to-64 and 64-to-32 (4 distributions overall)."
echo "TCCREPRO_VERIFY (env): empty -> none. 32 -> test/run the 32 binaries, else also 64"
echo "TCCREPRO_KEEPTMP (env): non-empty -> keep temporary build files (in dedicated dirs)"
echo "TCCREPRO_V (env): non-empty -> verbose output (not 100% reliable on windows)."
else
_TOP_SRCDIR="$(real_dir "$(dirname "$0")/..")"
[ -n "$1" ] && _TARGET_DIR="$1" || _TARGET_DIR="$_TOP_SRCDIR/$DEFAULT_DEST"
if [ -e "$_TARGET_DIR" ]; then
echo "Target dir must not exist. Aborting. ($_TARGET_DIR)"
else
mkdir -p "$_TARGET_DIR" &&
_TARGET_DIR=$(real_dir "$_TARGET_DIR") &&
$run_dbg cd "$_TOP_SRCDIR" &&
_TARGET_DIR="$(maybe_relative "$_TARGET_DIR")" &&
build_dist "$_TARGET_DIR" "$CC" &&
( [ -z "$TCCREPRO_VERIFY" ] || verify_dist "$_TARGET_DIR" "$TCCREPRO_VERIFY" )
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment