Created
November 6, 2016 20:17
-
-
Save stisa/d7f1c536bc8a201c36418e987648bdf8 to your computer and use it in GitHub Desktop.
Reproducible windows builds of tcc - see http://lists.nongnu.org/archive/html/tinycc-devel/2016-10/msg00146.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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