| 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 | "$@"
|