OILS / build / ninja-rules-py.sh View on Github | oils.pub

369 lines, 151 significant
1#!/usr/bin/env bash
2#
3# Ninja rules for translating Python to C++.
4#
5# Usage:
6# build/ninja-rules-py.sh <function name>
7#
8# Env variables:
9# _bin/shwrap/mycpp_main respects EXTRA_MYCPP_ARGS
10# for --stack-roots-warn 16 in CI
11
12set -o nounset
13set -o pipefail
14set -o errexit
15
16REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
17
18source build/dev-shell.sh # python2 in $PATH
19#source devtools/types.sh # typecheck-files
20source $REPO_ROOT/test/tsv-lib.sh # time-tsv
21
22die() {
23 echo "$@" >& 2
24 exit 1
25}
26
27main-example-unix() {
28 ### Used by mycpp/examples
29 local main_namespace=${1:-fib_iter}
30
31 cat <<EOF
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include "mycpp/runtime.h"
36
37namespace $main_namespace {
38void run_tests();
39void run_benchmarks();
40}
41
42int main(int argc, char **argv) {
43 gHeap.Init();
44
45 char* b = getenv("BENCHMARK");
46 if (b && strlen(b)) { // match Python's logic
47 fprintf(stderr, "Benchmarking...\\n");
48 $main_namespace::run_benchmarks();
49 } else {
50 $main_namespace::run_tests();
51 }
52
53 gHeap.CleanProcessExit();
54}
55EOF
56}
57
58main-unix() {
59 ### Used by oils-for-unix and yaks
60 local main_namespace=$1
61
62 cat <<EOF
63#include "mycpp/runtime.h"
64
65namespace $main_namespace {
66int main(List<BigStr*>* argv);
67}
68
69int main(int argc, char **argv) {
70 mylib::InitCppOnly(); // Initializes gHeap
71
72 auto* args = Alloc<List<BigStr*>>();
73 for (int i = 0; i < argc; ++i) {
74 args->append(StrFromC(argv[i]));
75 }
76
77 int status = $main_namespace::main(args);
78
79 gHeap.ProcessExit();
80
81 return status;
82}
83EOF
84}
85
86main-win32() {
87 ### Used by bin/hello
88 local main_namespace=$1
89
90 cat <<EOF
91#include "mycpp/gc_list.h"
92
93namespace $main_namespace {
94int main(List<BigStr*>* argv);
95}
96
97int main(int argc, char **argv) {
98 gHeap.Init();
99
100 auto* args = Alloc<List<BigStr*>>();
101 for (int i = 0; i < argc; ++i) {
102 args->append(StrFromC(argv[i]));
103 }
104
105 int status = $main_namespace::main(args);
106
107 gHeap.ProcessExit();
108
109 return status;
110}
111EOF
112}
113
114write-main() {
115 ### Write a C++ main file, for mycpp
116 local template=$1
117 local out=$2
118 local main_namespace=$3
119
120 case $template in
121 unix)
122 main-unix $main_namespace T
123 ;;
124 win32)
125 main-win32 $main_namespace T
126 ;;
127 example-unix)
128 main-example-unix $main_namespace T
129 ;;
130 *)
131 die "Invalid template '$template'"
132 ;;
133 esac > $out
134}
135
136# TODO: Move mycpp/example tasks out of Ninja since timing is not a VALUE. It
137# depends on the machine, can be done more than once, etc.
138
139task() {
140 local bin=$1 # Run this
141 local task_out=$2
142 local log_out=$3
143
144 shift 3
145 # The rest of the args are passed as flags to time-tsv
146
147 case $bin in
148 (mycpp/examples/*.py)
149 # we import mycpp.mylib
150 export PYTHONPATH="$REPO_ROOT/mycpp:$REPO_ROOT/vendor:$REPO_ROOT"
151 ;;
152 esac
153
154 case $task_out in
155 (_test/tasks/benchmark/*)
156 export BENCHMARK=1
157 ;;
158 esac
159
160 time-tsv -o $task_out --rusage "$@" --field $bin --field $task_out -- \
161 $bin >$log_out 2>&1
162}
163
164example-task() {
165 ### Run a program in the examples/ dir, either in Python or C++
166
167 local name=$1 # e.g. 'fib_iter'
168 local impl=$2 # 'Python' or 'C++'
169
170 local bin=$3 # Run this
171 local task_out=$4
172 local log_out=$5
173
174 task $bin $task_out $log_out --field $name --field $impl
175}
176
177benchmark-table() {
178 local out=$1
179 shift
180
181 # TODO: Use QTT header with types?
182 { time-tsv --print-header --rusage \
183 --field example_name --field impl \
184 --field bin --field task_out
185
186 # Concatenate task files
187 cat "$@"
188 } > $out
189}
190
191# Copied from devtools/types.sh
192
193MYPY_FLAGS='--strict --no-strict-optional'
194typecheck-files() {
195 echo "MYPY $@"
196
197 # TODO: Adjust path for mcypp/examples/modules.py
198 time MYPYPATH='.:pyext' python3 -m mypy --py2 --follow-imports=silent $MYPY_FLAGS "$@"
199}
200
201typecheck() {
202 ### Typecheck without translation
203 local main_py=$1
204 local out=$2
205 local skip_imports=${3:-}
206
207 if test -n "$skip_imports"; then
208 local more_flags='--follow-imports=silent'
209 else
210 local more_flags=''
211 fi
212
213 # Similar to devtools/types.sh
214
215 local status=0
216
217 set +o errexit
218 typecheck-files $main_py > $out
219 status=$?
220 set -o errexit
221
222 if test $status != 0; then
223 echo "FAIL $main_py"
224 cat $out
225 fi
226
227 return $status
228}
229
230logs-equal() {
231 local out=$1
232 shift
233
234 mycpp/compare_pairs.py "$@" | tee $out
235}
236
237#
238# shwrap rules
239#
240
241shwrap-py() {
242 ### Part of shell template for Python executables
243
244 local main=$1
245 echo 'PYTHONPATH=$REPO_ROOT:$REPO_ROOT/vendor exec $REPO_ROOT/'$main' "$@"'
246}
247
248shwrap-mycpp() {
249 ### Part of shell template for mycpp executable
250
251 cat <<'EOF'
252MYPYPATH=$1 # e.g. $REPO_ROOT/mycpp
253preamble_path=$2
254out=$3
255shift 3
256
257# Modifies $PATH; do not combine
258. build/dev-shell.sh
259
260tmp=$out.tmp # avoid creating partial files
261
262# The command we want to run
263# EXTRA_MYCPP_ARGS is for --stack-root-warn 16, in Soil CI
264set -- python3 mycpp/mycpp_main.py \
265 --preamble-path "$preamble_path" \
266 --cc-out $tmp \
267 ${EXTRA_MYCPP_ARGS:-} \
268 "$@"
269
270# If 'time' is on the system, add timing info. (It's not present on some
271# Debian CI images)
272if which time >/dev/null; then
273 # 'busybox time' supports -f but not --format.
274 set -- \
275 time -f 'MYCPP { elapsed: %e, max_RSS: %M }' -- \
276 "$@"
277fi
278
279MYPYPATH="$MYPYPATH" "$@"
280status=$?
281
282mv $tmp $out
283exit $status
284EOF
285}
286
287shwrap-pea() {
288 ### Part of shell template for pea executable
289
290 cat <<'EOF'
291MYPYPATH=$1 # e.g. $REPO_ROOT/mycpp
292preamble_path=$2
293out=$3
294shift 3
295
296tmp=$out.tmp # avoid creating partial files
297
298# copied from build/dev-shell.sh
299
300USER_WEDGE_DIR=~/wedge/oils-for-unix.org
301
302MYPY_VERSION=0.780
303MYPY_WEDGE=$USER_WEDGE_DIR/pkg/mypy/$MYPY_VERSION
304
305PY3_LIBS_VERSION=2023-03-04
306site_packages=lib/python3.10/site-packages
307PY3_LIBS_WEDGE=$USER_WEDGE_DIR/pkg/py3-libs/$PY3_LIBS_VERSION/$site_packages
308
309PYTHONPATH="$REPO_ROOT:$MYPY_WEDGE:$PY3_LIBS_WEDGE" MYPYPATH="$MYPYPATH" \
310 python3 pea/pea_main.py mycpp "$@" > $tmp
311status=$?
312
313mv $tmp $out
314exit $status
315EOF
316}
317
318print-shwrap() {
319 local template=$1
320 local unused=$2
321 shift 2
322
323 cat << 'EOF'
324#!/bin/sh
325REPO_ROOT=$(cd "$(dirname $0)/../.."; pwd)
326. $REPO_ROOT/build/py2.sh
327EOF
328
329 case $template in
330 py)
331 local main=$1 # additional arg
332 shift
333 shwrap-py $main
334 ;;
335 mycpp)
336 shwrap-mycpp
337 ;;
338 pea)
339 shwrap-pea
340 ;;
341 *)
342 die "Invalid template '$template'"
343 ;;
344 esac
345
346 echo
347 echo '# DEPENDS ON:'
348 for dep in "$@"; do
349 echo "# $dep"
350 done
351}
352
353write-shwrap() {
354 ### Create a shell wrapper for a Python tool
355
356 # Key point: if the Python code changes, then the C++ code should be
357 # regenerated and re-compiled
358
359 local unused=$1
360 local stub_out=$2
361
362 print-shwrap "$@" > $stub_out
363 chmod +x $stub_out
364}
365
366# sourced by devtools/bin.sh
367if test $(basename $0) = 'ninja-rules-py.sh'; then
368 "$@"
369fi