mirror of
https://github.com/notwa/rc
synced 2024-12-21 21:49:40 -08:00
358 lines
14 KiB
Bash
Executable file
358 lines
14 KiB
Bash
Executable file
#!/usr/bin/env zsh
|
|
# compat: -ash -bash -dash +zsh
|
|
|
|
setup_clang_ubuntu() { ### @-
|
|
### print (but don't execute) the commands necessary to install
|
|
### a fairly recent version of clang on ubuntu-based distros.
|
|
###
|
|
### ```sh
|
|
### $ setup_clang_ubuntu noble
|
|
### wget -O- http://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
|
### echo > "/etc/apt/sources.list.d/llvm-toolchain-noble.list" \
|
|
### "
|
|
### deb http://apt.llvm.org/noble/ llvm-toolchain-noble main
|
|
### # deb-src http://apt.llvm.org/noble/ llvm-toolchain-noble main
|
|
### # 18
|
|
### deb http://apt.llvm.org/noble/ llvm-toolchain-noble-18 main
|
|
### # deb-src http://apt.llvm.org/noble/ llvm-toolchain-noble-18 main"
|
|
### export DEBIAN_FRONTEND=noninteractive NEEDRESTART_SUSPEND=1
|
|
### apt-get update -y && apt-get install -y clang-18 lld-18
|
|
### update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 1800
|
|
### update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 1800
|
|
### update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-18 1800
|
|
### ```
|
|
|
|
local site="http://apt.llvm.org"
|
|
local name="$1"
|
|
local version=18 # NOTE: no longer decimal-based
|
|
local priority=$(( version * 100 ))
|
|
[ -n "$name" ] || name="$(lsb_release -c | cut -f2)"
|
|
# TODO: use https? this is sketchy
|
|
echo wget -O- "$site/llvm-snapshot.gpg.key" \| apt-key add -
|
|
printf %s\\n "echo > \"/etc/apt/sources.list.d/llvm-toolchain-$name.list\" \\" \
|
|
'"' \
|
|
"deb $site/$name/ llvm-toolchain-$name main" \
|
|
"# deb-src $site/$name/ llvm-toolchain-$name main" \
|
|
"# $version" \
|
|
"deb $site/$name/ llvm-toolchain-$name-$version main" \
|
|
"# deb-src $site/$name/ llvm-toolchain-$name-$version main\""
|
|
echo export DEBIAN_FRONTEND=noninteractive NEEDRESTART_SUSPEND=1
|
|
echo apt-get update -y \&\& apt-get install -y clang-$version lld-$version
|
|
#echo needrestart -ra
|
|
for p in clang clang++ llvm-symbolizer; do
|
|
echo update-alternatives --install /usr/bin/$p $p /usr/bin/$p-$version $priority
|
|
done
|
|
}
|
|
|
|
compile() { ### @-
|
|
### compile single-file C and C++ programs, messily.
|
|
###
|
|
### supports gcc and clang on \*nix, and mingw64 gcc, msvc clang,
|
|
### and regular msvc on Windows. tested on x86\_64 and on ARMv7 as well.
|
|
### does not support MacOS, maybe someday…
|
|
###
|
|
### defaults to gnu11 and gnu++1z as C and C++ standards respectively.
|
|
### defaults to clang, gcc, and msvc in that order.
|
|
###
|
|
### `compile` attempts to guess the most sane switches for any program, so that compilation may reduce to:
|
|
###
|
|
### ```sh
|
|
### # debug build
|
|
### compile rd.c
|
|
### compile debug rd.c
|
|
### # debug build with warning/error flags defined in ~/sh/arrays
|
|
### # (requires .zshrc for global alias expansion)
|
|
### compile WHOA rd.c
|
|
### # likewise for C++
|
|
### compile WHOA WELP rd.cc
|
|
### compile WHOA WELP rd.cpp
|
|
### # "derelease" build (release build with debug information)
|
|
### compile derelease WHOA rd.c
|
|
### # release build (with symbols stripped)
|
|
### compile release WHOA rd.c
|
|
### # hardened build (only useful on *nix)
|
|
### compile hardened WHOA rd.c
|
|
### # specifying compiler
|
|
### compile gcc WHOA rd.c
|
|
### compile msvc WHOA rd.c
|
|
### compile release clang WHOA rd.c
|
|
### # compile and execute (FIXME: writing to /tmp is a security concern)
|
|
### compile derelease rd.c && /tmp/rd
|
|
### ```
|
|
|
|
# FIXME: compile gcc portrend.c -lsdl
|
|
# this causes mayhem!
|
|
|
|
local gcc="$(whence -p gcc 2>/dev/null)"
|
|
local clang="$(whence -p clang 2>/dev/null)"
|
|
local clang_flags=() # currently just for clang-msvc
|
|
|
|
local flag= d= # iterator variables
|
|
|
|
local cl= vc=
|
|
if [ -n "$MSYSTEM" ]; then # using msys2?
|
|
if [ -z "$clang" ]; then # don't have native clang?
|
|
# then maybe we have clang-msvc installed.
|
|
local dir=
|
|
printf "%s\n" "/c/Program Files/LLVM"*(On/N[1]) | read -r dir
|
|
if [ -d "$dir" ] && [ -e "$dir/bin/clang" ]; then
|
|
clang="$dir/bin/clang"
|
|
|
|
# not sure if i'll need this:
|
|
#local clang_include=
|
|
#printf "%s\n" "$dir/lib/clang/"*(On/N[1]) | read -r clang_include
|
|
#[ -n "$clang_include" ] || { echo "failed glob; missing clang include" >&2; return 1 }
|
|
#clang_flags+=(-I"$clang_include/include")
|
|
|
|
export PATH="$PATH:$dir/bin/"
|
|
fi
|
|
fi
|
|
|
|
local winkit=
|
|
printf "%s\n" "/c/Program Files (x86)/Windows Kits/"*(on/N[1]) | read -r winkit
|
|
[ -z "$winkit" ] || printf "%s\n" "$winkit/Lib/"*(On/N[1]) | read -r winkit
|
|
|
|
if [ -z "$winkit" ]; then
|
|
#echo "failed glob; missing winkit" >&2
|
|
:
|
|
else
|
|
# detect MSVC.
|
|
local clarch= arch= msvc_dig_deep=
|
|
[ "$MSYSTEM" = MINGW64 ] && clarch="/amd64" || clarch=""
|
|
[ "$MSYSTEM" = MINGW64 ] && arch="x64" || arch="x86"
|
|
if [ -d "/c/Program Files (x86)/Microsoft Visual Studio" ]; then # 2017+
|
|
printf "%s\n" "/c/Program Files (x86)/Microsoft Visual Studio/20"*(On/N[1]) | read vc
|
|
printf "%s\n" "$vc"/*/VC | read vc
|
|
printf "%s\n" "$vc/Tools/MSVC/"*(On/N[1]) | read vc
|
|
msvc_dig_deep="yes"
|
|
else # older versions
|
|
printf "%s\n" "/c/Program Files (x86)/Microsoft Visual Studio "*(On/N[1]) | read vc
|
|
vc="$vc/VC"
|
|
fi
|
|
|
|
# setup MSVC.
|
|
if [ -n "$msvc_dig_deep" ] && [ -e "$vc/bin/Host$arch/$arch/cl" ]; then
|
|
cl="$vc/bin/Host$arch/$arch/cl"
|
|
export PATH="$PATH:$vc/bin/Host$arch/$arch"
|
|
export LIB="$(cygpath -w "$vc/lib/$arch")"
|
|
export LIBPATH="$(cygpath -w "$vc/lib/$arch")"
|
|
elif [ -d "$vc/bin$clarch" ] && [ -e "$vc/bin$clarch/$cl" ]; then
|
|
cl="$vc/bin$clarch/cl"
|
|
export PATH="$PATH:$vc/bin$clarch"
|
|
export LIB="$(cygpath -w "$vc/LIB$clarch")"
|
|
export LIBPATH="$(cygpath -w "$vc/LIB$clarch")"
|
|
fi
|
|
|
|
# finish up.
|
|
if [ -n "$cl" ]; then
|
|
export INCLUDE="$(cygpath -w "$vc/INCLUDE")"
|
|
export INCLUDE="$INCLUDE;$(cygpath -w "${winkit/Lib/Include}/ucrt")"
|
|
export LIB="$LIB;$(cygpath -w "$winkit/um/$arch")"
|
|
export LIB="$LIB;$(cygpath -w "$winkit/ucrt/$arch")"
|
|
|
|
for d in "${(@s/;/)INCLUDE}"; do
|
|
clang_flags+=(-I"$d")
|
|
done
|
|
# ignore MSVC's non-standard deprecation warnings.
|
|
clang_flags+=(-D_CRT_SECURE_NO_WARNINGS)
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
local sep_once=
|
|
print_separated() {
|
|
echo -n "${sep_once:+|}$1" >&2
|
|
sep_once=1
|
|
}
|
|
|
|
if [ $# -eq 0 ]; then
|
|
echo -n "usage: compile [" >&2
|
|
[ -n "$clang" ] && print_separated "clang"
|
|
[ -n "$gcc" ] && print_separated "gcc"
|
|
[ -n "$cl" ] && print_separated "msvc"
|
|
echo "] [debug|derelease|release|hardened] [flags...] {source file}" >&2
|
|
return 1
|
|
fi
|
|
|
|
# set some defaults.
|
|
local sepples=0 CC= CXX=
|
|
[ -n "$clang" ] && CC=clang || CC=gcc
|
|
[ -n "$clang" ] && CXX=clang++ || CXX=g++
|
|
local our_flags=(-I.)
|
|
|
|
# guess if we're compiling C++ by the file extension.
|
|
local file=${@[-1]}
|
|
[ "${file##*.}" = "c" ] || [ "${file##*.}" = "h" ] || sepples=1
|
|
|
|
# select the appropriate executable if a compiler name is given.
|
|
{ [ "$1" = clang ] && CC="clang" && CXX="clang++" && shift } || \
|
|
{ [ "$1" = gcc ] && CC="gcc" && CXX="g++" && shift } || \
|
|
{ [ "$1" = msvc ] && CC="cl" && CXX="cl" && shift }
|
|
|
|
# always color outputs.
|
|
[ "$CC" = cl ] || our_flags+=-fdiagnostics-color=always
|
|
|
|
# add our clang-specific flags. (currently just for clang-msvc)
|
|
if [ $CC = clang ] && [ -n "$clang_flags" ]; then
|
|
for flag in $clang_flags; do
|
|
our_flags+=($flag)
|
|
done
|
|
fi
|
|
|
|
# if they exist, include some directories that contain useful headers.
|
|
maybe_include() {
|
|
[ -d "$1" ] && our_flags+=("-I$1")
|
|
}
|
|
#maybe_include "$HOME/opt/local/include"
|
|
#maybe_include "$HOME/src/ustl"
|
|
|
|
# set the build flags for each mode.
|
|
if [ $CC = cl ]; then
|
|
our_flags+=(-nologo -utf-8)
|
|
local debug_flags=(-Od -ZI -sdl);
|
|
local release_flags=(-Ox)
|
|
local dr_flags=(-Ox -Zi)
|
|
local hardened_flags=(-Ox -sdl)
|
|
else
|
|
if [ $CC = clang ]; then
|
|
# clang doesn't like -march=native on ARM for some reason.
|
|
our_flags+=(-mcpu=native)
|
|
else
|
|
our_flags+=(-march=native)
|
|
fi
|
|
local debug_flags=(-O1 -g -D_DEBUG);
|
|
local release_flags=(-Ofast -fwhole-program -fweb -mtune=native -g0 -fomit-frame-pointer -s -DNDEBUG)
|
|
local dr_flags=(-Ofast -g -fomit-frame-pointer -DNDEBUG)
|
|
local hardened_flags=(-O3 -g0 -s
|
|
-DNDEBUG -D_FORTIFY_SOURCE=2
|
|
-Wformat -Wformat-security -Werror=format-security)
|
|
|
|
if [ -z "$MSYSTEM" ]; then
|
|
hardened_flags+=(-fPIE -pie)
|
|
hardened_flags+=(-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now)
|
|
fi
|
|
|
|
local nomalloc=(-fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free)
|
|
if [ -e /usr/bin/pprof ]; then
|
|
#debug_flags+=(-ltcmalloc $nomalloc)
|
|
dr_flags+=(-lprofiler $nomalloc)
|
|
elif [ -e /usr/bin/google-pprof ]; then
|
|
#debug_flags+=(-l:libtcmalloc.so.4 $nomalloc)
|
|
dr_flags+=(-l:libtcmalloc_and_profiler.so.4 $nomalloc)
|
|
fi
|
|
fi
|
|
|
|
# select appropriate version and executable for the language.
|
|
local compiler=
|
|
if [ $sepples -eq 1 ]; then
|
|
compiler=$CXX
|
|
[ $CC = cl ] && std="-TP" || std="-std=gnu++1z"
|
|
else
|
|
compiler=$CC
|
|
[ $CC = cl ] && std="-TC" || std="-std=gnu11"
|
|
fi
|
|
|
|
local clang_msvc=0
|
|
if [ $CC = clang ]; then
|
|
if $compiler --version | grep -q windows-msvc; then
|
|
clang_msvc=1
|
|
fi
|
|
fi
|
|
|
|
local gold=
|
|
# utilize clang's vast debugging and hardening features where available.
|
|
if [ $CC = clang ]; then
|
|
debug_flags+=(-ftrapv)
|
|
[ -z "$MSYSTEM" ] && gold=gold || gold=lld
|
|
[ -n "$MSYSTEM" ] && our_flags+=(-fansi-escape-codes) || true
|
|
if [ $clang_msvc -eq 1 ] || [ -z "$MSYSTEM" ]; then
|
|
debug_flags+=(-fsanitize=undefined) # this SHOULD work with mingw,
|
|
# but it fails to link.
|
|
debug_flags+=(-fsanitize=address)
|
|
debug_flags+=(-fvisibility=hidden -fuse-ld=$gold -flto -fsanitize=cfi)
|
|
|
|
hardened_flags+=(-fsanitize=safe-stack)
|
|
hardened_flags+=(-fstack-protector-strong)
|
|
hardened_flags+=(-fvisibility=hidden -fuse-ld=$gold -flto -fsanitize=cfi)
|
|
else
|
|
fi
|
|
fi
|
|
|
|
# select and merge the flags for our build mode.
|
|
# TODO: add static option.
|
|
{ [ "$1" = debug ] && our_flags+=($debug_flags) && shift } || \
|
|
{ [ "$1" = release ] && our_flags+=($release_flags) && shift } || \
|
|
{ [ "$1" = derelease ] && our_flags+=($dr_flags) && shift } || \
|
|
{ [ "$1" = hardened ] && our_flags+=($hardened_flags) && shift } || \
|
|
{ our_flags+=($debug_flags) } # our default
|
|
|
|
local flags=(${@[1,-2]})
|
|
|
|
# drop everything past the first dot and use that as our output filename.
|
|
local out=/tmp/${${file##*/}%%.*}
|
|
if [ -n "$MSYSTEM" ]; then
|
|
# explicitly output as .exe to avoid weirdness.
|
|
out="$out.exe"
|
|
fi
|
|
|
|
# TODO: naive idea:
|
|
# allow multiple source files (using the firstmost to determine the program name)
|
|
# by generating a file that #includes each given file.
|
|
|
|
local final_flags=() libraries=() warnings=()
|
|
for flag in $our_flags $flags; do
|
|
# move -l flags to the end because gcc won't respect them otherwise.
|
|
if [[ $flag == -l* ]]; then
|
|
libraries+=($flag)
|
|
|
|
# split warning flags so they don't spam the console.
|
|
elif [[ $flag == -W* ]] && [[ $flag != -Wl* ]] || [[ $flag == -Wlogical-op ]]; then
|
|
warnings+=($flag)
|
|
|
|
if [ $sepples -eq 0 ] && [[ $flag == -Wextra ]] then
|
|
# enable some warnings just for C. too annoying in C++.
|
|
warnings+=(-Wshadow -Winline)
|
|
# these ones only work with C.
|
|
warnings+=(-Wjump-misses-init)
|
|
fi
|
|
|
|
if [ $CC = cl ] && [ $flag = -Wall ]; then
|
|
# disable some obnoxious msvc warnings.
|
|
warnings+=(
|
|
-wd4505 # unreferenced local function has been removed
|
|
-wd4514 # unreferenced inline function has been removed
|
|
-wd4625 # copy constructor was implicitly defined as deleted because a base class copy constructor is inaccessible or deleted
|
|
-wd4626 # assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted
|
|
-wd4710 # function not inlined
|
|
-wd4711 # function selected for automatic inline expansion
|
|
)
|
|
fi
|
|
|
|
else
|
|
if [ $CC = clang ]; then
|
|
# these are specific to gcc. (for now)
|
|
if [ $flag = "-findirect-inlining" ] \
|
|
|| [ $flag = "-finline-small-functions" ]; then
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
if [ $clang_msvc -eq 1 ]; then
|
|
# remove linker-related flags from our compiler-only clang.
|
|
if [ $flag = "-Wl,--gc-sections" ] \
|
|
|| [ $flag = "-s" ]; then
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
final_flags+=($flag)
|
|
fi
|
|
done
|
|
|
|
# do the thing!
|
|
[ $CC = cl ] && local outflag=-Fe: || local outflag=-o
|
|
echo $compiler $std ${final_flags[@]} $file ${libraries[@]} $outflag $out >&2
|
|
$compiler $std ${final_flags[@]} $file ${libraries[@]} ${warnings[@]} $outflag $out
|
|
}
|
|
|
|
[ -n "${preload+-}" ] || compile "$@"
|