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

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