1 | #!/usr/bin/env bash
|
2 | #
|
3 | # Compile OVM tarball.
|
4 | #
|
5 | # Usage:
|
6 | # build/ovm-compile.sh <function name>
|
7 |
|
8 | set -o nounset
|
9 | set -o pipefail
|
10 | set -o errexit
|
11 | shopt -s strict:all 2>/dev/null || true # dogfood for OSH
|
12 |
|
13 | REPO_ROOT=$(cd $(dirname $0)/..; pwd)
|
14 | readonly REPO_ROOT
|
15 |
|
16 | source build/common.sh
|
17 |
|
18 | source-detected-config-or-die() {
|
19 | if ! source _build/detected-config.sh; then
|
20 | # Make this error stand out.
|
21 | echo
|
22 | echo "FATAL: can't find _build/detected-config.h. Run './configure'"
|
23 | echo
|
24 | exit 1
|
25 | fi
|
26 | }
|
27 |
|
28 | # NOTES on trying to delete certain modules:
|
29 | #
|
30 | # _warnings.c: There weren't that many; it probably could be deleted.
|
31 | # bufferobject.c: the types.py module uses it.
|
32 | # Python-ast.h: pythonrun.c uses it in several places (mod_ty), and a lot of
|
33 | # stuff uses pythonrun.c.
|
34 | # pythonrun.c: lots interpreter flags and interpreter initialization caused
|
35 | # link errors.
|
36 | # pyctype.c: Tables needed for many string operations.
|
37 |
|
38 | # getargs.c: needed for Python-C API, e.g. PyArg_ParseTuple.
|
39 | # dtoa.c: not tried, but I assume that %.3f for 'time' uses it.
|
40 |
|
41 |
|
42 | readonly OVM_PYTHON_OBJS='
|
43 | Python/_warnings.c
|
44 | Python/bltinmodule.c
|
45 | Python/ceval.c
|
46 | Python/errors.c
|
47 | Python/getargs.c
|
48 | Python/getcompiler.c
|
49 | Python/getplatform.c
|
50 | Python/getversion.c
|
51 | Python/import.c
|
52 | Python/marshal.c
|
53 | Python/modsupport.c
|
54 | Python/mystrtoul.c
|
55 | Python/mysnprintf.c
|
56 | Python/pyarena.c
|
57 | Python/pyctype.c
|
58 | Python/pyfpe.c
|
59 | Python/pystate.c
|
60 | Python/pythonrun.c
|
61 | Python/random.c
|
62 | Python/structmember.c
|
63 | Python/sysmodule.c
|
64 | Python/traceback.c
|
65 | Python/pystrtod.c
|
66 | Python/dtoa.c
|
67 | Python/pymath.c
|
68 | '
|
69 | # NOTE: pystrtod.c needs some floating point functions in pymath.c
|
70 |
|
71 | OBJECT_OBJS='
|
72 | Objects/abstract.c
|
73 | Objects/boolobject.c
|
74 | Objects/bufferobject.c
|
75 | Objects/bytes_methods.c
|
76 | Objects/capsule.c
|
77 | Objects/cellobject.c
|
78 | Objects/classobject.c
|
79 | Objects/cobject.c
|
80 | Objects/codeobject.c
|
81 | Objects/descrobject.c
|
82 | Objects/enumobject.c
|
83 | Objects/exceptions.c
|
84 | Objects/genobject.c
|
85 | Objects/fileobject.c
|
86 | Objects/floatobject.c
|
87 | Objects/frameobject.c
|
88 | Objects/funcobject.c
|
89 | Objects/intobject.c
|
90 | Objects/iterobject.c
|
91 | Objects/listobject.c
|
92 | Objects/longobject.c
|
93 | Objects/dictobject.c
|
94 | Objects/methodobject.c
|
95 | Objects/moduleobject.c
|
96 | Objects/object.c
|
97 | Objects/obmalloc.c
|
98 | Objects/rangeobject.c
|
99 | Objects/setobject.c
|
100 | Objects/sliceobject.c
|
101 | Objects/stringobject.c
|
102 | Objects/structseq.c
|
103 | Objects/tupleobject.c
|
104 | Objects/typeobject.c
|
105 | Objects/weakrefobject.c
|
106 | '
|
107 |
|
108 | # Non-standard lib stuff.
|
109 | MODULE_OBJS='
|
110 | Modules/main.c
|
111 | Modules/gcmodule.c
|
112 | '
|
113 |
|
114 | # The stuff in Modules/Setup.dist, signalmodule.c. NOTE: In Python,
|
115 | # signalmodule.c is specified in Modules/Setup.config, which comes from
|
116 | # 'configure' output.
|
117 | MODOBJS='
|
118 | Modules/errnomodule.c
|
119 | Modules/pwdmodule.c
|
120 | Modules/_weakref.c
|
121 | Modules/zipimport.c
|
122 | Modules/signalmodule.c
|
123 | '
|
124 |
|
125 | # Parser/myreadline.c is needed for raw_input() to work. There is a dependency
|
126 | # from Python/bltinmodule.c to it.
|
127 | OVM_LIBRARY_OBJS="
|
128 | Modules/getbuildinfo.c
|
129 | Parser/myreadline.c
|
130 | $OBJECT_OBJS
|
131 | $OVM_PYTHON_OBJS
|
132 | $MODULE_OBJS
|
133 | $MODOBJS
|
134 | "
|
135 |
|
136 | readonly EMPTY_STR='""'
|
137 |
|
138 | # Stub out a few variables
|
139 | readonly PREPROC_FLAGS=(
|
140 | -D OVM_MAIN \
|
141 | -D PYTHONPATH="$EMPTY_STR" \
|
142 | -D VERSION="$EMPTY_STR" \
|
143 | -D VPATH="$EMPTY_STR" \
|
144 | -D Py_BUILD_CORE \
|
145 | # Python already has support for disabling complex numbers!
|
146 | -D WITHOUT_COMPLEX
|
147 | )
|
148 |
|
149 | # NOTE: build/oil-defs is hard-coded to the oil.ovm app. We're abandoning
|
150 | # hello.ovm and opy.ovm for now, but those can easily be added later. We
|
151 | # haven't mangled the CPython source!
|
152 | readonly INCLUDE_PATHS=(
|
153 | -I . # for pyconfig.h
|
154 | -I .. # for _gen/frontend/id_kind_asdl_c.h etc.
|
155 | -I Include
|
156 | -I ../build/oil-defs
|
157 | )
|
158 | readonly CC=${CC:-cc} # cc should be on POSIX systems
|
159 |
|
160 | # BASE_CFLAGS is copied by observation from what configure.ac does on my Ubuntu
|
161 | # 16.04 system. Then we check if it works on Alpine Linux too.
|
162 |
|
163 | # "Python violates C99 rules, by casting between incompatible pointer types.
|
164 | # GCC may generate bad code as a result of that, so use -fno-strict-aliasing if
|
165 | # supported."
|
166 | # - gcc 4.x and Clang need -fwrapv
|
167 |
|
168 | # TODO:
|
169 | # - -DNDEBUG is also passed. That turns off asserts. Do we want that?
|
170 | # - We should auto-detect the flags in configure, or simplify the source so it
|
171 | # isn't necessary. Python's configure.ac sometimes does it by compiling a test
|
172 | # file; at other times it does it by grepping $CC --help.
|
173 |
|
174 | # pyext/fanos.c needs -std=c99
|
175 | BASE_CFLAGS='-fno-strict-aliasing -fwrapv -Wall -Wstrict-prototypes -std=c99'
|
176 |
|
177 | # These flags are disabled for OS X. I would have thought it would work in
|
178 | # Clang? It works with both GCC and Clang on Linux.
|
179 | # https://stackoverflow.com/questions/6687630/how-to-remove-unused-c-c-symbols-with-gcc-and-ld
|
180 | #BASE_CFLAGS="$BASE_CFLAGS -fdata-sections -ffunction-sections"
|
181 |
|
182 | # Needed after cpython-defs filtering.
|
183 | BASE_CFLAGS="$BASE_CFLAGS -Wno-unused-variable -Wno-unused-function"
|
184 | readonly BASE_CFLAGS
|
185 |
|
186 | BASE_LDFLAGS=''
|
187 | # Disabled for OS X
|
188 | # BASE_LDFLAGS='-Wl,--gc-sections'
|
189 |
|
190 | # The user should be able to customize CFLAGS, but it shouldn't disable what's
|
191 | # in BASE_CFLAGS.
|
192 | readonly CFLAGS=${CFLAGS:-}
|
193 | readonly LDFLAGS=${LDFLAGS:-}
|
194 |
|
195 | build() {
|
196 | local out=${1:-$PY27/ovm2}
|
197 | local module_init=${2:-$PY27/Modules/config.c}
|
198 | local main_name=${3:-_tmp/hello/main_name.c}
|
199 | local c_module_srcs=${4:-_tmp/hello/c-module-srcs.txt}
|
200 | shift 4
|
201 |
|
202 | local abs_out=$PWD/$out
|
203 | local abs_module_init=$PWD/$module_init
|
204 | local abs_main_name=$PWD/$main_name
|
205 | local abs_c_module_srcs=$PWD/$c_module_srcs
|
206 |
|
207 | #echo $OVM_LIBRARY_OBJS
|
208 |
|
209 | # HAVE_READLINE defined in detected-config.sh.
|
210 | source-detected-config-or-die
|
211 |
|
212 | pushd $PY27
|
213 |
|
214 | local readline_flags=''
|
215 | if [[ "$HAVE_READLINE" -eq 1 ]]; then
|
216 | # Readline interface for tokenizer.c and [raw_]input() in bltinmodule.c.
|
217 | # For now, we are using raw_input() for the REPL. TODO: Parameterize this!
|
218 | # We should create a special no_readline_raw_input().
|
219 |
|
220 | c_module_src_list=$(cat $abs_c_module_srcs)
|
221 |
|
222 | if [[ -n "$READLINE_DIR" ]]; then
|
223 | readline_flags+="-L $READLINE_DIR/lib -I $READLINE_DIR/include "
|
224 | fi
|
225 |
|
226 | # NOTE: pyconfig.h has HAVE_LIBREADLINE but doesn't appear to use it?
|
227 | readline_flags+="-l readline -D HAVE_READLINE"
|
228 | else
|
229 | # don't fail
|
230 | c_module_src_list=$(grep -E -v '/readline.c|/line_input.c' $abs_c_module_srcs || true)
|
231 | fi
|
232 |
|
233 | # $PREFIX comes from ./configure and defaults to /usr/local.
|
234 | # $EXEC_PREFIX is a GNU thing and used in getpath.c. Could probably get rid
|
235 | # of it.
|
236 |
|
237 | time $CC \
|
238 | ${BASE_CFLAGS} \
|
239 | ${CFLAGS} \
|
240 | "${INCLUDE_PATHS[@]}" \
|
241 | "${PREPROC_FLAGS[@]}" \
|
242 | -D PREFIX="\"$PREFIX\"" \
|
243 | -D EXEC_PREFIX="\"$PREFIX\"" \
|
244 | -o $abs_out \
|
245 | $OVM_LIBRARY_OBJS \
|
246 | $abs_module_init \
|
247 | $abs_main_name \
|
248 | $c_module_src_list \
|
249 | Modules/ovm.c \
|
250 | -l m \
|
251 | ${BASE_LDFLAGS} \
|
252 | ${LDFLAGS} \
|
253 | $readline_flags \
|
254 | "$@"
|
255 |
|
256 | # NOTE:
|
257 | # -l readline -l termcap -- for Python readline. Hm it builds without -l
|
258 | # termcap.
|
259 | # -l z WOULD be needed for zlibmodule.c, but we don't need it because our zip
|
260 | # file has no compression -- see build/make_zip.py with ZIP_STORED.
|
261 | # zipimport works fine without this.
|
262 | }
|
263 |
|
264 | # build the optimized one. Makefile uses -O3.
|
265 |
|
266 | # Clang -O2 is 1.37 MB. 18 seconds to compile.
|
267 | # -m32 is 1.12 MB. But I probably have to redefine a few things because
|
268 | # there are more warnings.
|
269 | # -O3 is 1.40 MB.
|
270 |
|
271 | # GCC -O2 is 1.35 MB. 21 seconds to compile.
|
272 |
|
273 | build-dbg() {
|
274 | build "$@" -O0 -g -D OVM_DEBUG
|
275 | }
|
276 |
|
277 | # This will be stripped later.
|
278 | build-opt() {
|
279 | # frame pointer for perf. Otherwise stack traces are messed up!
|
280 | # http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html#C But why
|
281 | # isn't debuginfo enough? Because it's a recursive function?
|
282 | # Does this make things slower? Do I need a "perf" build?
|
283 | build "$@" -O3 -fno-omit-frame-pointer
|
284 | }
|
285 |
|
286 | #
|
287 | # Source Release (uses same files
|
288 | #
|
289 |
|
290 | add-py27() {
|
291 | xargs -I {} -- echo $PY27/{}
|
292 | }
|
293 |
|
294 | python-sources() {
|
295 | echo "$OVM_LIBRARY_OBJS" | add-py27
|
296 | }
|
297 |
|
298 | _headers() {
|
299 | local c_module_srcs=${1:-_tmp/hello/c-module-srcs.txt}
|
300 | local abs_c_module_srcs=$PWD/$c_module_srcs
|
301 |
|
302 | cd $PY27
|
303 |
|
304 | # -MM: no system headers
|
305 | gcc \
|
306 | "${INCLUDE_PATHS[@]}" \
|
307 | "${PREPROC_FLAGS[@]}" \
|
308 | -MM $OVM_LIBRARY_OBJS \
|
309 | Modules/ovm.c \
|
310 | $(cat $abs_c_module_srcs)
|
311 | }
|
312 |
|
313 | # NOTE: 91 headers in Include, but only 81 referenced here. So it's worth it.
|
314 | # These are probably for the parser.
|
315 | #
|
316 | # NOTE: We also should get rid of asdl.h and so forth.
|
317 |
|
318 | python-headers() {
|
319 | local c_module_srcs=$1
|
320 |
|
321 | # 1. -MM outputs Makefile fragments, so egrep turns those into proper lines.
|
322 | #
|
323 | # 2. The user should generated detected-config.h, so remove it.
|
324 | #
|
325 | # 3. # gcc outputs paths like
|
326 | # Python-2.7.13/Python/../Objects/stringlib/stringdefs.h
|
327 | # but 'Python/..' causes problems for tar.
|
328 | #
|
329 |
|
330 | # NOTE: need .def for build/oil-defs.
|
331 | _headers $c_module_srcs \
|
332 | | egrep --only-matching '[^ ]+\.(h|def)' \
|
333 | | grep -v '_build/detected-config.h' \
|
334 | | sed 's|^Python/../||' \
|
335 | | sort | uniq | add-py27
|
336 | }
|
337 |
|
338 | make-tar() {
|
339 | local app_name=${1:-hello}
|
340 | local bytecode_zip=${2:-bytecode-cpython.zip}
|
341 | local out=${3:-_release/hello.tar}
|
342 |
|
343 | local version_file
|
344 | case $app_name in
|
345 | oil)
|
346 | version_file=oil-version.txt
|
347 | ;;
|
348 | hello)
|
349 | version_file=build/testdata/hello-version.txt
|
350 | ;;
|
351 | *)
|
352 | die "Unknown app $app_name"
|
353 | exit 1
|
354 | ;;
|
355 | esac
|
356 | local version=$(head -n 1 $version_file)
|
357 |
|
358 | echo "Creating $app_name version $version"
|
359 |
|
360 | local c_module_srcs=_build/$app_name/c-module-srcs.txt
|
361 |
|
362 | # Add oil-0.0.0/ to the beginning of every path.
|
363 | local sed_expr="s,^,${app_name}-${version}/,"
|
364 |
|
365 | # Differences between tarball and repo:
|
366 | #
|
367 | # - build/portable-rules.mk is intentionally not included in the release tarball.
|
368 | # The Makefile can and should operate without it.
|
369 | #
|
370 | # - We include intermediate files like c-module-srcs.txt, so we don't have to
|
371 | # ship tools dynamic_deps.py. The end-user build shouldn't depend on Python.
|
372 |
|
373 | # Note: python-headers runs gcc -M, including pyconfig.h and
|
374 | # _build/detected-config.h.
|
375 |
|
376 | tar --create --transform "$sed_expr" --file $out \
|
377 | LICENSE.txt \
|
378 | INSTALL-old.txt \
|
379 | configure \
|
380 | install \
|
381 | uninstall \
|
382 | Makefile \
|
383 | doc/osh.1 \
|
384 | build/ovm-compile.sh \
|
385 | build/ovm-actions.sh \
|
386 | build/clean.sh \
|
387 | build/common.sh \
|
388 | build/detect-*.c \
|
389 | _build/$app_name/$bytecode_zip \
|
390 | _build/$app_name/*.c \
|
391 | $PY27/LICENSE \
|
392 | $PY27/Modules/ovm.c \
|
393 | $c_module_srcs \
|
394 | $(cat $c_module_srcs | add-py27) \
|
395 | $(python-headers $c_module_srcs) \
|
396 | $(python-sources)
|
397 |
|
398 | ls -l $out
|
399 | }
|
400 |
|
401 | # 123K lines.
|
402 | # Excluding MODOBJS, it's 104K lines.
|
403 | #
|
404 | # Biggest: posixmodule,unicodeobject,typeobject,ceval.
|
405 | #
|
406 | # Remove tmpnam from posixmodule, other cruft.
|
407 | #
|
408 | # Big ones to rid of: unicodeobject.c, import.c
|
409 | # codecs and codecsmodule? There is some non-unicode stuff there though.
|
410 | #
|
411 | # Probably need unicode for compatibility with modules and web frameworks
|
412 | # especially.
|
413 |
|
414 | count-c-lines() {
|
415 | pushd $PY27
|
416 | wc -l $OVM_LIBRARY_OBJS | sort -n
|
417 |
|
418 | # 90 files.
|
419 | # NOTE: To count headers, use the tar file.
|
420 | echo
|
421 | echo 'Files:'
|
422 | { for i in $OVM_LIBRARY_OBJS; do
|
423 | echo $i
|
424 | done
|
425 | } | wc -l
|
426 |
|
427 | popd
|
428 | }
|
429 |
|
430 | "$@"
|