bc6e0e691d55904925ada4f4d83025e39e61ad83
[senf.git] / site_scons / site_tools / Boost.py
1 import SCons.Script
2 import SCons.Script.SConscript
3 import SCons.Defaults
4 import os.path
5 import os
6 import sys
7 import tempfile
8 import SCons.Scanner.C
9
10 # ARGH ... Why do they put a '+' in the module name ????????
11 SCons.Tool.cplusplus=getattr(__import__('SCons.Tool.c++', globals(), locals(), []).Tool, 'c++')
12
13 _ALL_TESTS = []
14
15 def scanTests(f):
16     tests = {}
17     name = start = None
18     linenr= 0
19     for line in f:
20         linenr += 1
21         if line.startswith('COMPILE_FAIL(') and ')' in line:
22             name = line.split('(',1)[-1].split(')',1)[0]
23             start = linenr
24         elif line.startswith('}') and name and start:
25             tests[name] = (start, linenr)
26     return tests
27
28 def CompileCheck(target, source, env):
29     tests = scanTests(file(source[0].abspath))
30     cenv = env.Clone()
31     cenv.Append( CPPDEFINES = [ 'COMPILE_CHECK' ] )
32     out = tempfile.TemporaryFile()
33     cenv['SPAWN'] = lambda sh, escape, cmd, args, env, pspawn=cenv['PSPAWN'], out=out: \
34                     pspawn(sh, escape, cmd, args, env, out, out)
35     SCons.Script.Action('$CXXCOM').execute(target, source, cenv)
36     passedTests = {}
37     delay_name = None
38     out.seek(0)
39     result = out.read();
40     for error in result.splitlines():
41         elts = error.split(':',2)
42         if len(elts) != 3 : continue
43         filename, line, message = elts
44         if not os.path.exists(filename) : continue
45         try: line = int(line)
46         except ValueError : continue
47         message = message.strip()
48
49         if delay_name and not message.startswith('instantiated from '):
50             print "Passed test '%s': %s" % (delay_name, message)
51             delay_name = None
52             continue
53             
54         filename = os.path.abspath(filename)
55         if filename != source[0].abspath : continue
56
57         for name,lines in tests.iteritems():
58             if line >= lines[0] and line <= lines[1]:
59                 passedTests[name] = 1
60                 if message.startswith('instantiated from '):
61                     delay_name = name
62                 else:
63                     print "Passed test '%s': %s" % (name, message)
64     if delay_name:
65         print "Passed test '%s': <unknown message ??>" % delay_name
66     failedTests = set(tests.iterkeys()) - set(passedTests.iterkeys())
67     if failedTests:
68         for test in failedTests:
69             print "Test '%s' FAILED" % test
70         print
71         print "*** %d tests FAILED" % len(failedTests)
72         if os.path.exists(target[0].abspath):
73             os.unlink(target[0].abspath)
74         return 1
75     file(target[0].abspath,"w").write(result)
76     return 0
77
78 CompileCheck = SCons.Script.Action(CompileCheck)
79
80 def BoostUnitTest(env, target=None, source=None,  **kw):
81     global _ALL_TESTS
82
83     target = env.arg2nodes(target)[0]
84     source = env.arg2nodes(source)
85
86     binnode = target.dir.File('.' + target.name + '.bin')
87     stampnode = target.dir.File('.' + target.name + '.stamp')
88
89     bin = env.Program(binnode, source, 
90                       LIBS = env['LIBS'] + [ '$TEST_EXTRA_LIBS' ],
91                       _LIBFLAGS = ' -Wl,-Bstatic -l$BOOSTTESTLIB -Wl,-Bdynamic ' + env['_LIBFLAGS'],
92                       **kw)
93
94     stamp = env.Command(stampnode, bin,
95                         [ './$SOURCE $BOOSTTESTARGS', SCons.Script.Touch('$TARGET')],
96                         **kw)
97
98     alias = env.Command(env.File(target), stamp, [ env.NopAction() ] )
99
100     compileTests = [ src for src in source 
101                      if src.suffix in SCons.Tool.cplusplus.CXXSuffixes \
102                          and src.exists() \
103                          and 'COMPILE_CHECK' in file(str(src)).read() ]
104     if compileTests:
105         env.Depends(alias, env.CompileCheck(source = compileTests))
106
107     _ALL_TESTS.append(alias)
108         
109     return alias
110
111 def FindAllBoostUnitTests(env, target, source):
112     global _ALL_TESTS
113     return _ALL_TESTS
114
115 def NopAction(env, target, source):
116     def nop(target, source, env) : return None
117     def nopstr(target, source, env) : return ''
118     return env.Action(nop, nopstr)
119
120 def generate(env):
121     env.SetDefault(
122         BOOST_VARIANT     = '',
123         _BOOST_VARIANT    = '${BOOST_VARIANT and "-" or None}$BOOST_VARIANT',
124
125         BOOSTTESTLIB      = 'boost_unit_test_framework$_BOOST_VARIANT',
126         BOOSTREGEXLIB     = 'boost_regex$_BOOST_VARIANT',
127         BOOSTFSLIB        = 'boost_filesystem$_BOOST_VARIANT',
128         BOOSTIOSTREAMSLIB = 'boost_iostreams$_BOOST_VARIANT',
129         BOOSTSIGNALSLIB   = 'boost_signals$_BOOST_VARIANT',
130
131         BOOSTTESTARGS     = [ '--build_info=yes', '--log_level=test_suite' ],
132     )
133
134     env['BUILDERS']['BoostUnitTest'] = BoostUnitTest
135     env['BUILDERS']['FindAllBoostUnitTests'] = FindAllBoostUnitTests
136     env['BUILDERS']['CompileCheck'] = env.Builder(
137         action = CompileCheck,
138         suffix = '.checked',
139         src_suffix = '.cc',
140         source_scanner = SCons.Scanner.C.CScanner(),
141         single_source=1
142         )
143     env['BUILDERS']['NopAction'] = NopAction
144
145 def exists(env):
146     return True