-
Notifications
You must be signed in to change notification settings - Fork 0
/
compile
executable file
·358 lines (316 loc) · 14 KB
/
compile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#!/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 "$@"