OILS / build / ninja_lib.py View on Github | oils.pub

791 lines, 453 significant
1#!/usr/bin/env python2
2"""
3ninja_lib.py
4
5Runtime options:
6
7 CXXFLAGS Additional flags to pass to the C++ compiler
8
9Notes on ninja_syntax.py:
10
11- escape_path() seems wrong?
12 - It should really take $ to $$.
13 - It doesn't escape newlines
14
15 return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:')
16
17 Ninja shouldn't have used $ and ALSO used shell commands (sh -c)! Better
18 solutions:
19
20 - Spawn a process with environment variables.
21 - use % for substitution instead
22
23- Another problem: Ninja doesn't escape the # comment character like $#, so
24 how can you write a string with a # as the first char on a line?
25"""
26from __future__ import print_function
27
28import collections
29import os
30import sys
31
32
33def log(msg, *args):
34 if args:
35 msg = msg % args
36 print(msg, file=sys.stderr)
37
38
39# Matrix of configurations
40
41COMPILERS_VARIANTS = [
42 ('cxx', 'dbg'),
43 ('cxx', 'opt'),
44 ('cxx', 'asan'),
45 ('cxx', 'asan+gcalways'),
46 ('cxx', 'asan32+gcalways'),
47 ('cxx', 'ubsan'),
48
49 #('clang', 'asan'),
50 ('clang', 'dbg'), # compile-quickly
51 ('clang', 'opt'), # for comparisons
52 ('clang', 'ubsan'), # finds different bugs
53 ('clang', 'coverage'),
54]
55
56GC_PERF_VARIANTS = [
57 ('cxx', 'opt+bumpleak'),
58 ('cxx', 'opt+bumproot'),
59 ('cxx', 'opt+bumpsmall'),
60 #('cxx', 'asan+bumpsmall'),
61 ('cxx', 'opt+nopool'),
62
63 # TODO: should be binary with different files
64 #('cxx', 'opt+tcmalloc'),
65
66 # For tracing allocations, or debugging
67 ('cxx', 'uftrace'),
68
69 # Test performance of 32-bit build. (It uses less memory usage, but can be
70 # slower.)
71 ('cxx', 'opt32'),
72]
73
74OTHER_VARIANTS = [
75 # Affects mycpp/gc_mops.cc - we can do overflow checking
76 ('cxx', 'opt+bigint'),
77 ('cxx', 'asan+bigint'),
78]
79
80SMALL_TEST_MATRIX = [
81 ('cxx', 'asan'),
82 ('cxx', 'ubsan'),
83 ('clang', 'coverage'),
84]
85
86
87def ConfigDir(config):
88 compiler, variant, more_cxx_flags = config
89 if more_cxx_flags is None:
90 return '%s-%s' % (compiler, variant)
91 else:
92 # -D CPP_UNIT_TEST -> D_CPP_UNIT_TEST
93 flags_str = more_cxx_flags.replace('-', '').replace(' ', '_')
94 return '%s-%s-%s' % (compiler, variant, flags_str)
95
96
97def ObjPath(src_path, config):
98 rel_path, _ = os.path.splitext(src_path)
99 return '_build/obj/%s/%s.o' % (ConfigDir(config), rel_path)
100
101
102# Used namedtuple since it doesn't have any state
103CcBinary = collections.namedtuple(
104 'CcBinary',
105 'main_cc symlinks implicit deps matrix phony_prefix preprocessed bin_path')
106
107
108class CcLibrary(object):
109 """
110 Life cycle:
111
112 1. A cc_library is first created
113 2. A cc_binary can depend on it
114 - maybe writing rules, and ensuring uniques per configuration
115 3. The link step needs the list of objects
116 4. The tarball needs the list of sources for binary
117 """
118
119 def __init__(self, label, srcs, implicit, deps, headers,
120 generated_headers):
121 self.label = label
122 self.srcs = srcs # queried by SourcesForBinary
123 self.implicit = implicit
124 self.deps = deps
125 self.headers = headers
126 # TODO: asdl() rule should add to this.
127 # Generated headers are different than regular headers. The former need an
128 # implicit dep in Ninja, while the latter can rely on the .d mechanism.
129 self.generated_headers = generated_headers
130
131 self.obj_lookup = {} # config -> list of objects
132 self.preprocessed_lookup = {} # config -> boolean
133
134 def _CalculateImplicit(self, ru):
135 """ Compile actions for cc_library() also need implicit deps on generated headers"""
136
137 out_deps = set()
138 ru._TransitiveClosure(self.label, self.deps, out_deps)
139 unique_deps = sorted(out_deps)
140
141 implicit = list(self.implicit) # copy
142 for label in unique_deps:
143 cc_lib = ru.cc_libs[label]
144 implicit.extend(cc_lib.generated_headers)
145 return implicit
146
147 def MaybeWrite(self, ru, config, preprocessed):
148 if config not in self.obj_lookup: # already written by some other cc_binary()
149 implicit = self._CalculateImplicit(ru)
150
151 objects = []
152 for src in self.srcs:
153 obj = ObjPath(src, config)
154 ru.compile(obj, src, self.deps, config, implicit=implicit)
155 objects.append(obj)
156
157 self.obj_lookup[config] = objects
158
159 if preprocessed and config not in self.preprocessed_lookup:
160 implicit = self._CalculateImplicit(ru)
161
162 for src in self.srcs:
163 # no output needed
164 ru.compile('',
165 src,
166 self.deps,
167 config,
168 implicit=implicit,
169 maybe_preprocess=True)
170 self.preprocessed_lookup[config] = True
171
172
173class Rules(object):
174 """High-level wrapper for NinjaWriter
175
176 What should it handle?
177
178 - The (compiler, variant) matrix loop
179 - Implicit deps for generated code
180 - Phony convenience targets
181
182 Maybe: exporting data to test runner
183
184 Terminology:
185
186 Ninja has
187 - rules, which are like Bazel "actions"
188 - build targets
189
190 Our library has:
191 - Build config: (compiler, variant), and more later
192
193 - Labels: identifiers starting with //, which are higher level than Ninja
194 "targets"
195 cc_library:
196 //mycpp/runtime
197
198 //mycpp/examples/expr.asdl
199 //frontend/syntax.asdl
200
201 - Deps are lists of labels, and have a transitive closure
202
203 - H Rules / High level rules? B rules / Boil?
204 cc_binary, cc_library, asdl, etc.
205 """
206
207 def __init__(self, n):
208 self.n = n # direct ninja writer
209
210 self.cc_bins = [] # list of CcBinary() objects to write
211 self.cc_libs = {} # label -> CcLibrary object
212 self.cc_binary_deps = {} # main_cc -> list of LABELS
213 self.phony = {} # list of phony targets
214
215 def AddPhony(self, phony_to_add):
216 self.phony.update(phony_to_add)
217
218 def WritePhony(self):
219 for name in sorted(self.phony):
220 targets = self.phony[name]
221 if targets:
222 self.n.build([name], 'phony', targets)
223 self.n.newline()
224
225 def WriteRules(self):
226 for cc_bin in self.cc_bins:
227 self.WriteCcBinary(cc_bin)
228
229 def compile(self,
230 out_obj,
231 in_cc,
232 deps,
233 config,
234 implicit=None,
235 maybe_preprocess=False):
236 """ .cc -> compiler -> .o """
237
238 implicit = implicit or []
239
240 compiler, variant, more_cxx_flags = config
241 if more_cxx_flags is None:
242 flags_str = "''"
243 else:
244 assert "'" not in more_cxx_flags, more_cxx_flags # can't handle single quotes
245 flags_str = "'%s'" % more_cxx_flags
246
247 v = [('compiler', compiler), ('variant', variant),
248 ('more_cxx_flags', flags_str)]
249 if maybe_preprocess:
250 # Limit it to certain configs
251 if more_cxx_flags is None and variant in ('dbg', 'opt'):
252 pre = '_build/preprocessed/%s-%s/%s' % (compiler, variant,
253 in_cc)
254 self.n.build(pre,
255 'preprocess', [in_cc],
256 implicit=implicit,
257 variables=v)
258 else:
259 self.n.build([out_obj],
260 'compile_one', [in_cc],
261 implicit=implicit,
262 variables=v)
263
264 self.n.newline()
265
266 def link(self, out_bin, main_obj, deps, config):
267 """ list of .o -> linker -> executable, along with stripped version """
268 compiler, variant, _ = config
269
270 assert isinstance(out_bin, str), out_bin
271 assert isinstance(main_obj, str), main_obj
272
273 objects = [main_obj]
274 for label in deps:
275 try:
276 cc_lib = self.cc_libs[label]
277 except KeyError:
278 raise RuntimeError("Couldn't resolve label %r" % label)
279
280 o = cc_lib.obj_lookup[config]
281 objects.extend(o)
282
283 v = [('compiler', compiler), ('variant', variant),
284 ('more_link_flags', "''")]
285 self.n.build([out_bin], 'link', objects, variables=v)
286 self.n.newline()
287
288 # Strip any .opt binaries
289 if variant.startswith('opt') or variant.startswith('opt32'):
290 stripped = out_bin + '.stripped'
291 symbols = out_bin + '.symbols'
292 self.n.build([stripped, symbols], 'strip', [out_bin])
293 self.n.newline()
294
295 def comment(self, s):
296 self.n.comment(s)
297 self.n.newline()
298
299 def cc_library(
300 self,
301 label,
302 srcs=None,
303 implicit=None,
304 deps=None,
305 # note: headers is only used for tarball manifest, not compiler command line
306 headers=None,
307 generated_headers=None):
308
309 # srcs = [] is allowed for _gen/asdl/hnode.asdl.h
310 if srcs is None:
311 raise RuntimeError('cc_library %r requires srcs' % label)
312
313 implicit = implicit or []
314 deps = deps or []
315 headers = headers or []
316 generated_headers = generated_headers or []
317
318 if label in self.cc_libs:
319 raise RuntimeError('%s was already defined' % label)
320
321 self.cc_libs[label] = CcLibrary(label, srcs, implicit, deps, headers,
322 generated_headers)
323
324 def _TransitiveClosure(self, name, deps, unique_out):
325 """
326 Args:
327 name: for error messages
328 """
329 for label in deps:
330 if label in unique_out:
331 continue
332 unique_out.add(label)
333
334 try:
335 cc_lib = self.cc_libs[label]
336 except KeyError:
337 raise RuntimeError('Undefined label %s in %s' % (label, name))
338
339 self._TransitiveClosure(cc_lib.label, cc_lib.deps, unique_out)
340
341 def cc_binary(
342 self,
343 main_cc, # e.g. cpp/core_test.cc
344 symlinks=None, # make these symlinks - separate rules?
345 implicit=None, # for COMPILE action, not link action
346 deps=None, # libraries to depend on, transitive closure
347 matrix=None, # $compiler $variant +bumpleak
348 phony_prefix=None, # group
349 preprocessed=False, # generate report
350 bin_path=None, # default is _bin/$compiler-$variant/rel/path
351 ):
352 """
353 A cc_binary() depends on a list of cc_library() rules specified by
354 //package/label
355
356 It accepts a config matrix of (compiler, variant, +other)
357
358 The transitive closure is computed.
359
360 Then we write Ninja rules corresponding to each dependent library, with
361 respect to the config.
362 """
363 symlinks = symlinks or []
364 implicit = implicit or []
365 deps = deps or []
366 if not matrix:
367 raise RuntimeError("Config matrix required")
368
369 cc_bin = CcBinary(main_cc, symlinks, implicit, deps, matrix,
370 phony_prefix, preprocessed, bin_path)
371
372 self.cc_bins.append(cc_bin)
373
374 def WriteCcBinary(self, cc_bin):
375 c = cc_bin
376
377 out_deps = set()
378 self._TransitiveClosure(c.main_cc, c.deps, out_deps)
379 unique_deps = sorted(out_deps)
380
381 # to compute tarball manifest, with SourcesForBinary()
382 self.cc_binary_deps[c.main_cc] = unique_deps
383
384 compile_imp = list(c.implicit)
385 for label in unique_deps:
386 cc_lib = self.cc_libs[label] # should exit
387 # compile actions of binaries that have ASDL label deps need the
388 # generated header as implicit dep
389 compile_imp.extend(cc_lib.generated_headers)
390
391 for config in c.matrix:
392 if len(config) == 2:
393 config = (config[0], config[1], None)
394
395 for label in unique_deps:
396 cc_lib = self.cc_libs[label] # should exit
397
398 cc_lib.MaybeWrite(self, config, c.preprocessed)
399
400 # Compile main object, maybe with IMPLICIT headers deps
401 main_obj = ObjPath(c.main_cc, config)
402 self.compile(main_obj,
403 c.main_cc,
404 c.deps,
405 config,
406 implicit=compile_imp)
407 if c.preprocessed:
408 self.compile('',
409 c.main_cc,
410 c.deps,
411 config,
412 implicit=compile_imp,
413 maybe_preprocess=True)
414
415 config_dir = ConfigDir(config)
416 bin_dir = '_bin/%s' % config_dir # e.g. _bin/cxx-asan
417
418 if c.bin_path:
419 # e.g. _bin/cxx-dbg/oils_for_unix
420 bin_to_link = '%s/%s' % (bin_dir, c.bin_path)
421
422 # For _bin/cxx-asan/mycpp-souffle/oils-for-unix
423 bin_subdir, _, bin_name = c.bin_path.rpartition('/')
424 if bin_subdir:
425 bin_dir = '%s/%s' % (bin_dir, bin_subdir)
426 else:
427 bin_name = c.bin_path
428
429 else:
430 # e.g. _gen/mycpp/examples/classes.mycpp
431 rel_path, _ = os.path.splitext(c.main_cc)
432
433 # Put binary in _bin/cxx-dbg/mycpp/examples, not _bin/cxx-dbg/_gen/mycpp/examples
434 if rel_path.startswith('_gen/'):
435 rel_path = rel_path[len('_gen/'):]
436
437 bin_to_link = '%s/%s' % (bin_dir, rel_path)
438
439 # Link with OBJECT deps
440 self.link(bin_to_link, main_obj, unique_deps, config)
441
442 # Make symlinks
443 for symlink in c.symlinks:
444 # Must explicitly specify bin_path to have a symlink, for now
445 assert c.bin_path is not None
446 self.n.build(['%s/%s' % (bin_dir, symlink)],
447 'symlink', [bin_to_link],
448 variables=[('dir', bin_dir), ('target', bin_name),
449 ('new', symlink)])
450 self.n.newline()
451
452 if c.phony_prefix:
453 key = '%s-%s' % (c.phony_prefix, config_dir)
454 if key not in self.phony:
455 self.phony[key] = []
456 self.phony[key].append(bin_to_link)
457
458 def SourcesForBinary(self, main_cc):
459 """
460 Used for preprocessed metrics, release tarball, _build/oils.sh, etc.
461 """
462 deps = self.cc_binary_deps[main_cc]
463 sources = [main_cc]
464 for label in deps:
465 sources.extend(self.cc_libs[label].srcs)
466 return sources
467
468 def HeadersForBinary(self, main_cc):
469 deps = self.cc_binary_deps[main_cc]
470 headers = []
471 for label in deps:
472 headers.extend(self.cc_libs[label].headers)
473 headers.extend(self.cc_libs[label].generated_headers)
474 return headers
475
476 def asdl_library(self,
477 asdl_path,
478 deps=None,
479 pretty_print_methods=True,
480 abbrev_module=None):
481
482 deps = deps or []
483
484 # SYSTEM header, _gen/asdl/hnode.asdl.h
485 deps.append('//asdl/hnode.asdl')
486 deps.append('//display/pretty.asdl')
487
488 # to create _gen/mycpp/examples/expr.asdl.h
489 prefix = '_gen/%s' % asdl_path
490
491 out_cc = prefix + '.cc'
492 out_header = prefix + '.h'
493
494 asdl_flags = []
495
496 if pretty_print_methods:
497 outputs = [out_cc, out_header]
498 else:
499 outputs = [out_header]
500 asdl_flags.append('--no-pretty-print-methods')
501
502 if abbrev_module:
503 asdl_flags.append('--abbrev-module=%s' % abbrev_module)
504
505 debug_mod = prefix + '_debug.py'
506 outputs.append(debug_mod)
507
508 # Generating syntax_asdl.h does NOT depend on hnode_asdl.h existing ...
509 self.n.build(outputs,
510 'asdl-cpp', [asdl_path],
511 implicit=['_bin/shwrap/asdl_main'],
512 variables=[
513 ('action', 'cpp'),
514 ('out_prefix', prefix),
515 ('asdl_flags', ' '.join(asdl_flags)),
516 ('debug_mod', debug_mod),
517 ])
518 self.n.newline()
519
520 # ... But COMPILING anything that #includes it does.
521 # Note: assumes there's a build rule for this "system" ASDL schema
522
523 srcs = [out_cc] if pretty_print_methods else []
524 # Define lazy CC library
525 self.cc_library(
526 '//' + asdl_path,
527 srcs=srcs,
528 deps=deps,
529 # For compile_one steps of files that #include this ASDL file
530 generated_headers=[out_header],
531 )
532
533 def py_binary(self, main_py, deps_base_dir='_build/NINJA', template='py'):
534 """Wrapper for Python script with dynamically discovered deps
535
536 Args:
537 template: py, mycpp, or pea
538
539 Example:
540 _bin/shwrap/mycpp_main wraps mycpp/mycpp_main.py
541 - using dependencies from prebuilt/ninja/mycpp.mycpp_main/deps.txt
542 - with the 'shwrap-mycpp' template defined in build/ninja-lib.sh
543 """
544 rel_path, _ = os.path.splitext(main_py)
545 # asdl/asdl_main.py -> asdl.asdl_main
546 py_module = rel_path.replace('/', '.')
547
548 deps_path = os.path.join(deps_base_dir, py_module, 'deps.txt')
549 with open(deps_path) as f:
550 deps = [line.strip() for line in f]
551
552 deps.remove(main_py) # raises ValueError if it's not there
553
554 shwrap_name = os.path.basename(rel_path)
555 self.n.build('_bin/shwrap/%s' % shwrap_name,
556 'write-shwrap', [main_py] + deps,
557 variables=[('template', template)])
558 self.n.newline()
559
560 def souffle_binary(self, souffle_cpp):
561 """
562 Compile souffle C++ into a native executable.
563 """
564 rel_path, _ = os.path.splitext(souffle_cpp)
565 basename = os.path.basename(rel_path)
566
567 souffle_obj = '_build/obj/datalog/%s.o' % basename
568 self.n.build([souffle_obj],
569 'compile_one',
570 souffle_cpp,
571 variables=[('compiler', 'cxx'), ('variant', 'opt'),
572 ('more_cxx_flags', "'-Ivendor -std=c++17'")])
573
574 souffle_bin = '_bin/datalog/%s' % basename
575 self.n.build([souffle_bin],
576 'link',
577 souffle_obj,
578 variables=[('compiler', 'cxx'), ('variant', 'opt'),
579 ('more_link_flags', "'-lstdc++fs'")])
580
581 self.n.newline()
582
583
584SHWRAP = {
585 'mycpp': '_bin/shwrap/mycpp_main',
586 'mycpp-souffle': '_bin/shwrap/mycpp_main_souffle',
587 'pea': '_bin/shwrap/pea_main',
588}
589
590# TODO: should have dependencies with sh_binary
591RULES_PY = 'build/ninja-rules-py.sh'
592
593# Copied from build/ninja-rules-py.sh mycpp-gen
594DEFAULT_MYPY_PATH = '$NINJA_REPO_ROOT:$NINJA_REPO_ROOT/pyext'
595
596
597def TryDynamicDeps(py_main):
598 """
599 Read dynamic deps files built in ./NINJA-config.sh
600 """
601 # bin/oils_for_unix
602 py_rel_path, _ = os.path.splitext(py_main)
603 # bin.oils_for_unix
604 py_module = py_rel_path.replace('/', '.')
605
606 deps_file = '_build/NINJA/%s/translate.txt' % py_module
607 if os.path.exists(deps_file):
608 with open(deps_file) as f:
609 return [line.strip() for line in f]
610
611 return None
612
613
614# TODO:
615#
616# //bin/oils_for_unix.mycpp
617# //bin/oils_for_unix.mycpp-souffle
618#
619# mycpp_library('bin/oils_for_unix.py',
620# mypy_path=''
621# preamble=''
622# souffle=True,
623# pea=True, # another option
624# )
625#
626# //bin/oils_for_unix.main
627#
628# gen_cpp_main(namespace='oils_for_unix',
629# style='example')
630#
631# mycpp_binary(
632# '//bin/oils_for_unix.main', # cc_library() for main
633# bin_path='bin/oils_for_unix.mycpp-souffle',
634# deps=['//bin/oils_for_unix.mycpp-souffle'],
635# )
636#
637# mycpp_binary(
638# '//bin/oils_for_unix.main',
639# deps=['//bin/oils_for_unix.mycpp'],
640# bin_path='bin/oils_for_unix.mycpp',
641# symlinks=[],
642# preprocessed=True,
643# phony_prefix=''
644# )
645
646
647def mycpp_library(ru,
648 py_main,
649 mypy_path=DEFAULT_MYPY_PATH,
650 preamble=None,
651 translator='mycpp',
652 py_inputs=None,
653 deps=None):
654 """
655 Generate a .cc file with mycpp, and a cc_library() for it
656 """
657
658 # e.g. bin/oils_for_unix
659 py_rel_path, _ = os.path.splitext(py_main)
660 # e.g. bin.oils_for_unix
661 #py_module = py_rel_path.replace('/', '.')
662
663 py_inputs = py_inputs or [py_main] # if not specified, it's a single file
664 #matrix = matrix or COMPILERS_VARIANTS
665 deps = deps or []
666
667 # TODO: preamble should go in mycpp?
668 if preamble is None:
669 p = py_rel_path + '_preamble.h'
670 preamble = p if os.path.exists(p) else "''" # Ninja empty string!
671
672 n = ru.n
673
674 # Two steps
675 raw = '_gen/%s.%s-lib.cc' % (py_rel_path, translator)
676
677 translator_shwrap = SHWRAP[translator]
678
679 n.build(
680 raw,
681 'translate-%s' % translator,
682 py_inputs, # files to translate
683 # Implicit dependency: if the translator changes, regenerate source
684 # code. But don't pass it on the command line.
685 implicit=[translator_shwrap],
686 # examples/parse uses pyext/fastfunc.pyi
687 variables=[('mypypath', mypy_path)])
688
689 ru.cc_library(
690 # e.g. //bin/oils_for_unix.mycpp-souffle
691 '//%s.%s' % (py_rel_path, translator),
692 srcs=[raw],
693 deps=deps,
694 #matrix=matrix,
695 )
696
697
698def main_library(ru, py_main, template='unix'):
699 """
700 Generate a .main.cc file, and a cc_library() for it
701 """
702 n = ru.n
703
704 py_rel_path, _ = os.path.splitext(py_main)
705 main_cc = '_gen/%s.main.cc' % py_rel_path
706 n.build(
707 [main_cc],
708 'write-main',
709 [],
710 variables=[
711 ('template', template),
712 # e.g. 'hello'
713 ('main_namespace', os.path.basename(py_rel_path))
714 ])
715
716 ru.cc_library(
717 # e.g. //bin/hello.main
718 '//%s.main' % py_rel_path,
719 srcs=[main_cc],
720 # no link deps
721 deps=[],
722 #matrix=matrix,
723 )
724
725
726def mycpp_binary(ru,
727 py_main,
728 mypy_path=DEFAULT_MYPY_PATH,
729 bin_path=None,
730 symlinks=None,
731 preprocessed=False,
732 preamble=None,
733 translator='mycpp',
734 main_style='main-wrapper',
735 py_inputs=None,
736 phony_prefix=None,
737 matrix=None,
738 deps=None):
739 # e.g. bin/oils_for_unix
740 py_rel_path, _ = os.path.splitext(py_main)
741 # e.g. bin.oils_for_unix
742 py_module = py_rel_path.replace('/', '.')
743
744 py_inputs = py_inputs or [py_main] # if not specified, it's a single file
745 symlinks = symlinks or []
746 matrix = matrix or COMPILERS_VARIANTS
747 deps = deps or []
748 if preamble is None:
749 p = py_rel_path + '_preamble.h'
750 preamble = p if os.path.exists(p) else "''" # Ninja empty string!
751
752 n = ru.n
753
754 # Two steps
755 raw = '_gen/_tmp/%s.%s-raw.cc' % (py_rel_path, translator)
756 main_cc_src = '_gen/%s.%s.cc' % (py_rel_path, translator)
757
758 translator_shwrap = SHWRAP[translator]
759
760 n.build(
761 raw,
762 'translate-%s' % translator,
763 py_inputs, # files to translate
764 # Implicit dependency: if the translator changes, regenerate source
765 # code. But don't pass it on the command line.
766 implicit=[translator_shwrap],
767 # examples/parse uses pyext/fastfunc.pyi
768 variables=[('mypypath', mypy_path)])
769
770 # Make a translation unit
771 n.build(main_cc_src,
772 'wrap-cc',
773 raw,
774 implicit=[RULES_PY],
775 variables=[
776 ('main_namespace', os.path.basename(py_rel_path)),
777 ('main_style', main_style),
778 ('preamble', preamble),
779 ])
780
781 n.newline()
782
783 ru.cc_binary(
784 main_cc_src,
785 bin_path=bin_path,
786 symlinks=symlinks,
787 preprocessed=preprocessed,
788 deps=deps,
789 matrix=matrix,
790 phony_prefix=phony_prefix,
791 )