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

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