f9c144c32dd4c3a5cf8691eac9fb32e5c1ddd3ad
[scons.git] / src / engine / SCons / SConfTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import os
27 import re
28 import string
29 import StringIO
30 import sys
31 from types import *
32 import unittest
33
34 import TestCmd
35
36 sys.stdout = StringIO.StringIO()
37
38 if sys.platform == 'win32':
39     existing_lib = "msvcrt"
40 else:
41     existing_lib = "m"
42
43 class SConfTestCase(unittest.TestCase):
44
45     def setUp(self):
46         # we always want to start with a clean directory
47         self.test = TestCmd.TestCmd(workdir = '') 
48
49     def tearDown(self):
50         self.test.cleanup()
51
52     def _resetSConfState(self):
53         # Ok, this is tricky, and i do not know, if everything is sane.
54         # We try to reset scons' state (including all global variables)
55         import SCons.SConsign
56         SCons.SConsign.write() # simulate normal scons-finish
57         for n in sys.modules.keys():
58             if string.split(n, '.')[0] == 'SCons':
59                 m = sys.modules[n]
60                 if type(m) is ModuleType:
61                     # if this is really a scons module, clear its namespace
62                     del sys.modules[n]
63                     m.__dict__.clear()
64         # we only use SCons.Environment and SCons.SConf for these tests.
65         import SCons.Environment
66         import SCons.SConf
67         import SCons.Script.SConscript
68         SCons.Script.SConscript.sconscript_reading = 1
69         self.Environment = SCons.Environment
70         self.SConf = SCons.SConf
71         # and we need a new environment, cause references may point to
72         # old modules (well, at least this is safe ...)
73         self.scons_env = self.Environment.Environment()
74         self.scons_env.AppendENVPath('PATH', os.environ['PATH'])
75
76         # we want to do some autodetection here
77         # this stuff works with
78         #    - cygwin on win32 (using cmd.exe, not bash)
79         #    - posix
80         #    - msvc on win32 (hopefully)
81         if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
82             not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
83             not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
84             raise Exception, "This test needs an installed compiler!"
85         if self.scons_env['CXX'] == 'g++':
86             global existing_lib
87             existing_lib = 'm'
88         
89     def _baseTryXXX(self, TryFunc):
90         # TryCompile and TryLink are much the same, so we can test them
91         # in one method, we pass the function as a string ('TryCompile',
92         # 'TryLink'), so we are aware of reloading modules.
93         
94         def checks(self, sconf, TryFuncString):
95             TryFunc = self.SConf.SConf.__dict__[TryFuncString]
96             res1 = TryFunc( sconf, "int main() { return 0; }", ".c" )
97             res2 = TryFunc( sconf,
98                             '#include "no_std_header.h"\nint main() {return 0; }',
99                             '.c' )
100             return (res1,res2)
101
102         # 1. test initial behaviour (check ok / failed)
103         self._resetSConfState()
104         sconf = self.SConf.SConf(self.scons_env,
105                                  conf_dir=self.test.workpath('config.tests'),
106                                  log_file=self.test.workpath('config.log'))
107         try:
108             res = checks( self, sconf, TryFunc )
109             assert res[0] and not res[1] 
110         finally:
111             sconf.Finish()
112             
113         # 2.1 test the error caching mechanism (no dependencies have changed)
114         self._resetSConfState()
115         sconf = self.SConf.SConf(self.scons_env,
116                                  conf_dir=self.test.workpath('config.tests'),
117                                  log_file=self.test.workpath('config.log'))
118         try:
119             res = checks( self, sconf, TryFunc )
120             assert res[0] and not res[1] 
121         finally:
122             sconf.Finish()
123         # we should have exactly one one error cached 
124         log = self.test.read( self.test.workpath('config.log') )
125         expr = re.compile( ".*(\(cached\))", re.DOTALL ) 
126         firstOcc = expr.match( log )
127         assert firstOcc != None 
128         secondOcc = expr.match( log, firstOcc.end(0) )
129         assert secondOcc == None
130
131         # 2.2 test the error caching mechanism (dependencies have changed)
132         self._resetSConfState()
133         sconf = self.SConf.SConf(self.scons_env,
134                                  conf_dir=self.test.workpath('config.tests'),
135                                  log_file=self.test.workpath('config.log'))
136         test_h = self.test.write( self.test.workpath('config.tests', 'no_std_header.h'),
137                                   "/* we are changing a dependency now */" );
138         try:
139             res = checks( self, sconf, TryFunc )
140             log = self.test.read( self.test.workpath('config.log') )
141             assert res[0] and res[1] 
142         finally:
143             sconf.Finish()
144
145     def test_TryBuild(self):
146         """Test SConf.TryBuild
147         """
148         # 1 test that we can try a builder that returns a list of nodes
149         self._resetSConfState()
150         sconf = self.SConf.SConf(self.scons_env,
151                                  conf_dir=self.test.workpath('config.tests'),
152                                  log_file=self.test.workpath('config.log'))
153         class MyBuilder:
154             def __init__(self):
155                 self.prefix = ''
156                 self.suffix = ''
157             def __call__(self, env, target, source):
158                 class MyNode:
159                     def __init__(self, name):
160                         self.name = name
161                         self.state = None
162                         self.side_effects = []
163                         self.builder = None
164                     def has_builder(self):
165                         return 1
166                     def add_pre_action(self, *actions):
167                         pass
168                     def add_post_action(self, *actions):
169                         pass
170                     def children(self):
171                         return []
172                     def get_state(self):
173                         return self.state
174                     def set_state(self, state):
175                         self.state = state
176                     def alter_targets(self):
177                         return [], None
178                     def depends_on(self, nodes):
179                         return None
180                     def postprocess(self):
181                         pass
182                     def clear(self):
183                         pass
184                     def current(self, calc=None):
185                         return None
186                     def prepare(self):
187                         pass
188                     def retrieve_from_cache(self):
189                         return 0
190                     def build(self, **kw):
191                         return
192                     def built(self):
193                         pass
194                 return [MyNode('n1'), MyNode('n2')]
195         try:
196             self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
197             sconf.TryBuild(self.scons_env.SConfActionBuilder)
198         finally:
199             sconf.Finish()
200
201     def test_TryCompile(self):
202         """Test SConf.TryCompile
203         """
204         self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile )
205         
206     def test_TryLink(self):
207         """Test SConf.TryLink
208         """
209         self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink ) 
210
211     def test_TryRun(self):
212         """Test SConf.TryRun
213         """
214         def checks(sconf):
215             prog = """
216 #include <stdio.h>
217 int main() {
218   printf( "Hello" );
219   return 0;
220 }
221 """
222             res1 = sconf.TryRun( prog, ".c" ) 
223             res2 = sconf.TryRun( "not a c program", ".c" )
224             return (res1, res2)
225         
226         self._resetSConfState()
227         sconf = self.SConf.SConf(self.scons_env,
228                                  conf_dir=self.test.workpath('config.tests'),
229                                  log_file=self.test.workpath('config.log'))
230         try:
231             res = checks(sconf)
232             assert res[0][0] and res[0][1] == "Hello" 
233             assert not res[1][0] and res[1][1] == ""
234         finally:
235             sconf.Finish()
236
237         # test the caching mechanism
238         self._resetSConfState()
239         sconf = self.SConf.SConf(self.scons_env,
240                                  conf_dir=self.test.workpath('config.tests'),
241                                  log_file=self.test.workpath('config.log'))
242         try:
243             res = checks(sconf)
244             assert res[0][0] and res[0][1] == "Hello" 
245             assert not res[1][0] and res[1][1] == ""
246         finally:
247             sconf.Finish()
248         # we should have exactly one one error cached 
249         log = self.test.read( self.test.workpath('config.log') )
250         expr = re.compile( ".*(\(cached\))", re.DOTALL )
251         firstOcc = expr.match( log )
252         assert firstOcc != None 
253         secondOcc = expr.match( log, firstOcc.end(0) )
254         assert secondOcc == None 
255
256
257     def test_TryAction(self):
258         """Test SConf.TryAction
259         """
260         def actionOK(target, source, env):
261             open(str(target[0]), "w").write( "RUN OK" )
262             return None
263         def actionFAIL(target, source, env):
264             return 1
265         self._resetSConfState()
266         sconf = self.SConf.SConf(self.scons_env,
267                                   conf_dir=self.test.workpath('config.tests'),
268                                   log_file=self.test.workpath('config.log'))
269         try:
270             (ret, output) = sconf.TryAction(action=actionOK)
271             assert ret and output == "RUN OK"
272             (ret, output) = sconf.TryAction(action=actionFAIL)
273             assert not ret and output == ""
274         finally:
275             sconf.Finish()
276
277
278     def test_CheckHeader(self):
279         """Test SConf.CheckHeader()
280         """
281         self._resetSConfState()
282         sconf = self.SConf.SConf(self.scons_env,
283                                  conf_dir=self.test.workpath('config.tests'),
284                                  log_file=self.test.workpath('config.log'))
285         try:
286             # CheckHeader()
287             r = sconf.CheckHeader( "stdio.h", include_quotes="<>", language="C" )
288             assert r, "did not find stdio.h"
289             r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C" )
290             assert not r, "unexpectedly found HopefullyNoHeader.noh"
291             r = sconf.CheckHeader( "vector", include_quotes="<>", language="C++" )
292             assert r, "did not find vector"
293             r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C++" )
294             assert not r, "unexpectedly found HopefullyNoHeader.noh"
295
296         finally:
297             sconf.Finish()
298
299     def test_CheckCHeader(self):
300         """Test SConf.CheckCHeader()
301         """
302         self._resetSConfState()
303         sconf = self.SConf.SConf(self.scons_env,
304                                  conf_dir=self.test.workpath('config.tests'),
305                                  log_file=self.test.workpath('config.log'))
306
307         try:
308             # CheckCHeader()
309             r = sconf.CheckCHeader( "stdio.h", include_quotes="<>" )
310             assert r, "did not find stdio.h"
311             r = sconf.CheckCHeader( ["math.h", "stdio.h"], include_quotes="<>" )
312             assert r, "did not find stdio.h, #include math.h first"
313             r = sconf.CheckCHeader( "HopefullyNoCHeader.noh" )
314             assert not r, "unexpectedly found HopefullyNoCHeader.noh"
315
316         finally:
317             sconf.Finish()
318
319     def test_CheckCXXHeader(self):
320         """Test SConf.CheckCXXHeader()
321         """
322         self._resetSConfState()
323         sconf = self.SConf.SConf(self.scons_env,
324                                  conf_dir=self.test.workpath('config.tests'),
325                                  log_file=self.test.workpath('config.log'))
326
327         try:
328             # CheckCXXHeader()
329             r = sconf.CheckCXXHeader( "vector", include_quotes="<>" )
330             assert r, "did not find vector"
331             r = sconf.CheckCXXHeader( ["stdio.h", "vector"], include_quotes="<>" )
332             assert r, "did not find vector, #include stdio.h first"
333             r = sconf.CheckCXXHeader( "HopefullyNoCXXHeader.noh" )
334             assert not r, "unexpectedly found HopefullyNoCXXHeader.noh"
335
336         finally:
337             sconf.Finish()
338
339     def test_CheckLib(self):
340         """Test SConf.CheckLib()
341         """
342         self._resetSConfState()
343         sconf = self.SConf.SConf(self.scons_env,
344                                  conf_dir=self.test.workpath('config.tests'),
345                                  log_file=self.test.workpath('config.log'))
346
347         try:
348             # CheckLib()
349             r = sconf.CheckLib( existing_lib, "main", autoadd=0 )
350             assert r, "did not find %s" % existing_lib
351             r = sconf.CheckLib( "hopefullynolib", "main", autoadd=0 )
352             assert not r, "unexpectedly found hopefullynolib"
353
354             # CheckLib() with list of libs
355             r = sconf.CheckLib( [existing_lib], "main", autoadd=0 )
356             assert r, "did not find %s" % existing_lib
357             r = sconf.CheckLib( ["hopefullynolib"], "main", autoadd=0 )
358             assert not r, "unexpectedly found hopefullynolib"
359             # This is a check that a null list doesn't find functions
360             # that are in libraries that must be explicitly named.
361             # This works on POSIX systems where you have to -lm to
362             # get the math functions, but it fails on Visual Studio
363             # where you apparently get all those functions for free.
364             # Comment out this check until someone who understands
365             # Visual Studio better can come up with a corresponding
366             # test (if that ever really becomes necessary).
367             #r = sconf.CheckLib( [], "sin", autoadd=0 )
368             #assert not r, "unexpectedly found nonexistent library"
369             r = sconf.CheckLib( [existing_lib,"hopefullynolib"], "main", autoadd=0 )
370             assert r, "did not find %s,%s " % (existing_lib,r)
371             r = sconf.CheckLib( ["hopefullynolib",existing_lib], "main", autoadd=0 )
372             assert r, "did not find %s " % existing_lib
373
374             # CheckLib() with autoadd
375             def libs(env):
376                 return env.get('LIBS', [])
377
378             env = sconf.env.Copy()
379
380             try:
381                 r = sconf.CheckLib( existing_lib, "main", autoadd=1 )
382                 assert r, "did not find main in %s" % existing_lib
383                 expect = libs(env) + [existing_lib]
384                 got = libs(sconf.env)
385                 assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
386
387                 sconf.env = env.Copy()
388                 r = sconf.CheckLib( existing_lib, "main", autoadd=0 )
389                 assert r, "did not find main in %s" % existing_lib
390                 expect = libs(env)
391                 got = libs(sconf.env)
392                 assert got == expect, "before and after LIBS were not the same"
393             finally:
394                 sconf.env = env
395
396         finally:
397             sconf.Finish()
398
399     def test_CheckLibWithHeader(self):
400         """Test SConf.CheckLibWithHeader()
401         """
402         self._resetSConfState()
403         sconf = self.SConf.SConf(self.scons_env,
404                                  conf_dir=self.test.workpath('config.tests'),
405                                  log_file=self.test.workpath('config.log'))
406
407         try:
408             # CheckLibWithHeader()
409             r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 )
410             assert r, "did not find %s" % existing_lib
411             r = sconf.CheckLibWithHeader( existing_lib, ["stdio.h", "math.h"], "C", autoadd=0 )
412             assert r, "did not find %s, #include stdio.h first" % existing_lib
413             r = sconf.CheckLibWithHeader( "hopefullynolib", "math.h", "C", autoadd=0 )
414             assert not r, "unexpectedly found hopefullynolib"
415
416             # CheckLibWithHeader() with lists of libs
417             r = sconf.CheckLibWithHeader( [existing_lib], "math.h", "C", autoadd=0 )
418             assert r, "did not find %s" % existing_lib
419             r = sconf.CheckLibWithHeader( [existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 )
420             assert r, "did not find %s, #include stdio.h first" % existing_lib
421             # This is a check that a null list doesn't find functions
422             # that are in libraries that must be explicitly named.
423             # This works on POSIX systems where you have to -lm to
424             # get the math functions, but it fails on Visual Studio
425             # where you apparently get all those functions for free.
426             # Comment out this check until someone who understands
427             # Visual Studio better can come up with a corresponding
428             # test (if that ever really becomes necessary).
429             #r = sconf.CheckLibWithHeader( [], "math.h", "C", call="sin(3);", autoadd=0 )
430             #assert not r, "unexpectedly found non-existent library"
431             r = sconf.CheckLibWithHeader( ["hopefullynolib"], "math.h", "C", autoadd=0 )
432             assert not r, "unexpectedly found hopefullynolib"
433             r = sconf.CheckLibWithHeader( ["hopefullynolib",existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 )
434             assert r, "did not find %s, #include stdio.h first" % existing_lib
435             r = sconf.CheckLibWithHeader( [existing_lib,"hopefullynolib"], ["stdio.h", "math.h"], "C", autoadd=0 )
436             assert r, "did not find %s, #include stdio.h first" % existing_lib
437
438             # CheckLibWithHeader with autoadd
439             def libs(env):
440                 return env.get('LIBS', [])
441
442             env = sconf.env.Copy()
443
444             try:
445                 r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=1 )
446                 assert r, "did not find math.h with %s" % existing_lib
447                 expect = libs(env) + [existing_lib]
448                 got = libs(sconf.env)
449                 assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
450
451                 sconf.env = env.Copy()
452                 r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 )
453                 assert r, "did not find math.h with %s" % existing_lib
454                 expect = libs(env)
455                 got = libs(sconf.env)
456                 assert got == expect, "before and after LIBS were not the same"
457             finally:
458                 sconf.env = env
459
460         finally:
461             sconf.Finish()
462
463     def test_CheckFunc(self):
464         """Test SConf.CheckFunc()
465         """
466         self._resetSConfState()
467         sconf = self.SConf.SConf(self.scons_env,
468                                  conf_dir=self.test.workpath('config.tests'),
469                                  log_file=self.test.workpath('config.log'))
470
471         try:
472             # CheckFunc()
473             r = sconf.CheckFunc('strcpy')
474             assert r, "did not find strcpy"
475             r = sconf.CheckFunc('hopefullynofunction')
476             assert not r, "unexpectedly found hopefullynofunction"
477
478         finally:
479             sconf.Finish()
480
481     def test_(self):
482         """Test SConf.CheckType()
483         """
484         self._resetSConfState()
485         sconf = self.SConf.SConf(self.scons_env,
486                                  conf_dir=self.test.workpath('config.tests'),
487                                  log_file=self.test.workpath('config.log'))
488         try:
489             # CheckType()
490             r = sconf.CheckType('off_t', '#include <sys/types.h>\n')
491             assert r, "did not find off_t"
492             r = sconf.CheckType('hopefullynotypedef_not')
493             assert not r, "unexpectedly found hopefullynotypedef_not"
494
495         finally:
496             sconf.Finish()
497
498     def test_CustomChecks(self):
499         """Test Custom Checks
500         """
501         def CheckCustom(test):
502             test.Message( "Checking UserTest ... " )
503             prog = """
504 #include <stdio.h>
505
506 int main() {
507   printf( "Hello" );
508   return 0;
509 }
510 """
511             (ret, output) = test.TryRun( prog, ".c" )
512             test.Result( ret )
513             assert ret and output == "Hello" 
514             return ret
515         
516
517         self._resetSConfState()
518         sconf = self.SConf.SConf(self.scons_env,
519                                  custom_tests={'CheckCustom': CheckCustom},
520                                  conf_dir=self.test.workpath('config.tests'),
521                                  log_file=self.test.workpath('config.log'))
522         try:
523             ret = sconf.CheckCustom()
524             assert ret 
525         finally:
526             sconf.Finish()
527             
528
529 if __name__ == "__main__":
530     suite = unittest.makeSuite(SConfTestCase, 'test_')
531     res = unittest.TextTestRunner().run(suite)
532     if not res.wasSuccessful():
533         sys.exit(1)
534