OILS / build / ninja-rules-py.sh View on Github | oilshell.org

349 lines, 177 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# EXTRA_MYCPP_ARGS - passed to mycpp_main
10
11set -o nounset
12set -o pipefail
13set -o errexit
14
15REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
16
17source build/dev-shell.sh # python2 in $PATH
18source mycpp/common-vars.sh # MYPY_REPO
19source $REPO_ROOT/test/tsv-lib.sh # time-tsv
20
21example-main-wrapper() {
22 ### Used by mycpp/examples
23
24 local main_module=${1:-fib_iter}
25
26 cat <<EOF
27int main(int argc, char **argv) {
28 gHeap.Init();
29
30 char* b = getenv("BENCHMARK");
31 if (b && strlen(b)) { // match Python's logic
32 fprintf(stderr, "Benchmarking...\\n");
33 $main_module::run_benchmarks();
34 } else {
35 $main_module::run_tests();
36 }
37
38 gHeap.CleanProcessExit();
39}
40EOF
41}
42
43main-wrapper() {
44 ### Used by oils-for-unix and yaks
45 local main_namespace=$1
46
47 cat <<EOF
48int main(int argc, char **argv) {
49 mylib::InitCppOnly(); // Initializes gHeap
50
51 auto* args = Alloc<List<BigStr*>>();
52 for (int i = 0; i < argc; ++i) {
53 args->append(StrFromC(argv[i]));
54 }
55
56 int status = $main_namespace::main(args);
57
58 gHeap.ProcessExit();
59
60 return status;
61}
62EOF
63}
64
65gen-oils-for-unix() {
66 local main_name=$1
67 local out_prefix=$2
68 local preamble=$3
69 shift 3 # rest are inputs
70
71 # Put it in _build/tmp so it's not in the tarball
72 local tmp=_build/tmp
73 mkdir -p $tmp
74
75 local raw_cc=$tmp/${main_name}_raw.cc
76 local cc_out=${out_prefix}.cc
77
78 local raw_header=$tmp/${main_name}_raw.h
79 local header_out=${out_prefix}.h
80
81 local mypypath="$REPO_ROOT:$REPO_ROOT/pyext"
82
83 _bin/shwrap/mycpp_main $mypypath $raw_cc \
84 --header-out $raw_header \
85 ${EXTRA_MYCPP_ARGS:-} \
86 "$@"
87
88 # oils_for_unix -> OILS_FOR_UNIX_MYCPP_H'
89 local guard=${main_name^^}_MYCPP_H
90
91 { echo "// $main_name.mycpp.h: translated from Python by mycpp"
92 echo
93 echo "#ifndef $guard"
94 echo "#define $guard"
95
96 cat $raw_header
97
98 echo "#endif // $guard"
99
100 } > $header_out
101
102 { cat <<EOF
103// $main_name.mycpp.cc: translated from Python by mycpp
104
105// #include "$header_out"
106
107#include "$preamble"
108EOF
109
110 cat $raw_cc
111
112 main-wrapper $main_name
113 } > $cc_out
114}
115
116print-wrap-cc() {
117 local translator=$1
118 local main_module=$2
119 local in=$3
120 local preamble_path=$4
121
122 echo "// examples/$main_module translated by $translator"
123 echo
124
125 if test -f "$preamble_path"; then
126 echo "#include \"$preamble_path\""
127 fi
128
129 cat $in
130
131 # main() function
132 case $translator in
133 mycpp)
134 example-main-wrapper $main_module
135 ;;
136 yaks)
137 main-wrapper $main_module
138 ;;
139 pea)
140 echo '#include <stdio.h>'
141 echo 'int main() { printf("stub\n"); return 1; }'
142 ;;
143 (*)
144 die "Invalid translator $translator"
145 ;;
146 esac
147}
148
149wrap-cc() {
150 local out=$1
151 shift
152
153 # $translator $main_module $in $preamble_path
154 print-wrap-cc "$@" > $out
155}
156
157# TODO: Move mycpp/example tasks out of Ninja since timing is not a VALUE. It
158# depends on the machine, can be done more than once, etc.
159
160task() {
161 local bin=$1 # Run this
162 local task_out=$2
163 local log_out=$3
164
165 shift 3
166 # The rest of the args are passed as flags to time-tsv
167
168 case $bin in
169 (mycpp/examples/*.py)
170 # we import mycpp.mylib
171 export PYTHONPATH="$REPO_ROOT/mycpp:$REPO_ROOT/vendor:$REPO_ROOT"
172 ;;
173 esac
174
175 case $task_out in
176 (_test/tasks/benchmark/*)
177 export BENCHMARK=1
178 ;;
179 esac
180
181 time-tsv -o $task_out --rusage "$@" --field $bin --field $task_out -- \
182 $bin >$log_out 2>&1
183}
184
185example-task() {
186 ### Run a program in the examples/ dir, either in Python or C++
187
188 local name=$1 # e.g. 'fib_iter'
189 local impl=$2 # 'Python' or 'C++'
190
191 local bin=$3 # Run this
192 local task_out=$4
193 local log_out=$5
194
195 task $bin $task_out $log_out --field $name --field $impl
196}
197
198benchmark-table() {
199 local out=$1
200 shift
201
202 # TODO: Use QTT header with types?
203 { time-tsv --print-header --rusage \
204 --field example_name --field impl \
205 --field bin --field task_out
206
207 # Concatenate task files
208 cat "$@"
209 } > $out
210}
211
212# TODO: No longer works. This is called by ninja mycpp-check
213# I think it's giving strict warnings.
214mypy() {
215 ( source $MYCPP_VENV/bin/activate
216 # Don't need this since the virtualenv we created with it?
217 # source build/dev-shell.sh
218 PYTHONPATH=$MYPY_REPO python3 -m mypy "$@";
219 )
220}
221
222typecheck() {
223 ### Typecheck without translation
224 local main_py=$1
225 local out=$2
226 local skip_imports=${3:-}
227
228 if test -n "$skip_imports"; then
229 local more_flags='--follow-imports=silent'
230 else
231 local more_flags=''
232 fi
233
234 # $more_flags can be empty
235 MYPYPATH="$REPO_ROOT:$REPO_ROOT/mycpp" \
236 mypy --py2 --strict $more_flags $main_py > $out
237}
238
239logs-equal() {
240 local out=$1
241 shift
242
243 mycpp/compare_pairs.py "$@" | tee $out
244}
245
246#
247# shwrap rules
248#
249
250shwrap-py() {
251 ### Part of shell template for Python executables
252
253 local main=$1
254 echo 'PYTHONPATH=$REPO_ROOT:$REPO_ROOT/vendor exec $REPO_ROOT/'$main' "$@"'
255}
256
257shwrap-mycpp() {
258 ### Part of shell template for mycpp executable
259
260 cat <<'EOF'
261MYPYPATH=$1 # e.g. $REPO_ROOT/mycpp
262out=$2
263shift 2
264
265# Modifies $PATH; do not combine
266. build/dev-shell.sh
267
268tmp=$out.tmp # avoid creating partial files
269
270MYPYPATH="$MYPYPATH" \
271 python3 mycpp/mycpp_main.py --cc-out $tmp "$@"
272status=$?
273
274mv $tmp $out
275exit $status
276EOF
277}
278
279shwrap-pea() {
280 ### Part of shell template for pea executable
281
282 cat <<'EOF'
283MYPYPATH=$1 # e.g. $REPO_ROOT/mycpp
284out=$2
285shift 2
286
287tmp=$out.tmp # avoid creating partial files
288
289PYTHONPATH="$REPO_ROOT:$MYPY_REPO" MYPYPATH="$MYPYPATH" \
290 python3 pea/pea_main.py cpp "$@" > $tmp
291status=$?
292
293mv $tmp $out
294exit $status
295EOF
296}
297
298print-shwrap() {
299 local template=$1
300 local unused=$2
301 shift 2
302
303 cat << 'EOF'
304#!/bin/sh
305REPO_ROOT=$(cd "$(dirname $0)/../.."; pwd)
306. $REPO_ROOT/build/py2.sh
307EOF
308
309 case $template in
310 (py)
311 local main=$1 # additional arg
312 shift
313 shwrap-py $main
314 ;;
315 (mycpp)
316 shwrap-mycpp
317 ;;
318 (pea)
319 shwrap-pea
320 ;;
321 (*)
322 die "Invalid template '$template'"
323 ;;
324 esac
325
326 echo
327 echo '# DEPENDS ON:'
328 for dep in "$@"; do
329 echo "# $dep"
330 done
331}
332
333write-shwrap() {
334 ### Create a shell wrapper for a Python tool
335
336 # Key point: if the Python code changes, then the C++ code should be
337 # regenerated and re-compiled
338
339 local unused=$1
340 local stub_out=$2
341
342 print-shwrap "$@" > $stub_out
343 chmod +x $stub_out
344}
345
346# sourced by devtools/bin.sh
347if test $(basename $0) = 'ninja-rules-py.sh'; then
348 "$@"
349fi