Toplevel directory cleanup
[senf.git] / tools / scons-1.2.0 / engine / SCons / SConf.py
1 """SCons.SConf
2
3 Autoconf-like configuration support.
4 """
5
6 #
7 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
8 #
9 # Permission is hereby granted, free of charge, to any person obtaining
10 # a copy of this software and associated documentation files (the
11 # "Software"), to deal in the Software without restriction, including
12 # without limitation the rights to use, copy, modify, merge, publish,
13 # distribute, sublicense, and/or sell copies of the Software, and to
14 # permit persons to whom the Software is furnished to do so, subject to
15 # the following conditions:
16 #
17 # The above copyright notice and this permission notice shall be included
18 # in all copies or substantial portions of the Software.
19 #
20 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
21 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
22 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #
28
29 __revision__ = "src/engine/SCons/SConf.py 3842 2008/12/20 22:59:52 scons"
30
31 import os
32 import re
33 import string
34 import StringIO
35 import sys
36 import traceback
37 import types
38
39 import SCons.Action
40 import SCons.Builder
41 import SCons.Errors
42 import SCons.Job
43 import SCons.Node.FS
44 import SCons.Taskmaster
45 import SCons.Util
46 import SCons.Warnings
47 import SCons.Conftest
48
49 from SCons.Debug import Trace
50
51 # Turn off the Conftest error logging
52 SCons.Conftest.LogInputFiles = 0
53 SCons.Conftest.LogErrorMessages = 0
54
55 # Set
56 build_type = None
57 build_types = ['clean', 'help']
58
59 def SetBuildType(type):
60     global build_type
61     build_type = type
62
63 # to be set, if we are in dry-run mode
64 dryrun = 0
65
66 AUTO=0  # use SCons dependency scanning for up-to-date checks
67 FORCE=1 # force all tests to be rebuilt
68 CACHE=2 # force all tests to be taken from cache (raise an error, if necessary)
69 cache_mode = AUTO
70
71 def SetCacheMode(mode):
72     """Set the Configure cache mode. mode must be one of "auto", "force",
73     or "cache"."""
74     global cache_mode
75     if mode == "auto":
76         cache_mode = AUTO
77     elif mode == "force":
78         cache_mode = FORCE
79     elif mode == "cache":
80         cache_mode = CACHE
81     else:
82         raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
83
84 progress_display = SCons.Util.display # will be overwritten by SCons.Script
85 def SetProgressDisplay(display):
86     """Set the progress display to use (called from SCons.Script)"""
87     global progress_display
88     progress_display = display
89
90 SConfFS = None
91
92 _ac_build_counter = 0 # incremented, whenever TryBuild is called
93 _ac_config_logs = {}  # all config.log files created in this build
94 _ac_config_hs   = {}  # all config.h files created in this build
95 sconf_global = None   # current sconf object
96
97 def _createConfigH(target, source, env):
98     t = open(str(target[0]), "w")
99     defname = re.sub('[^A-Za-z0-9_]', '_', string.upper(str(target[0])))
100     t.write("""#ifndef %(DEFNAME)s_SEEN
101 #define %(DEFNAME)s_SEEN
102
103 """ % {'DEFNAME' : defname})
104     t.write(source[0].get_contents())
105     t.write("""
106 #endif /* %(DEFNAME)s_SEEN */
107 """ % {'DEFNAME' : defname})
108     t.close()
109
110 def _stringConfigH(target, source, env):
111     return "scons: Configure: creating " + str(target[0])
112
113 def CreateConfigHBuilder(env):
114     """Called just before the building targets phase begins."""
115     if len(_ac_config_hs) == 0:
116         return
117     action = SCons.Action.Action(_createConfigH,
118                                  _stringConfigH)
119     sconfigHBld = SCons.Builder.Builder(action=action)
120     env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} )
121     for k in _ac_config_hs.keys():
122         env.SConfigHBuilder(k, env.Value(_ac_config_hs[k]))
123     
124 class SConfWarning(SCons.Warnings.Warning):
125     pass
126 SCons.Warnings.enableWarningClass(SConfWarning)
127
128 # some error definitions
129 class SConfError(SCons.Errors.UserError):
130     def __init__(self,msg):
131         SCons.Errors.UserError.__init__(self,msg)
132
133 class ConfigureDryRunError(SConfError):
134     """Raised when a file or directory needs to be updated during a Configure
135     process, but the user requested a dry-run"""
136     def __init__(self,target):
137         if not isinstance(target, SCons.Node.FS.File):
138             msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target)
139         else:
140             msg = 'Cannot update configure test "%s" within a dry-run.' % str(target)
141         SConfError.__init__(self,msg)
142
143 class ConfigureCacheError(SConfError):
144     """Raised when a use explicitely requested the cache feature, but the test
145     is run the first time."""
146     def __init__(self,target):
147         SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target))
148
149 # define actions for building text files
150 def _createSource( target, source, env ):
151     fd = open(str(target[0]), "w")
152     fd.write(source[0].get_contents())
153     fd.close()
154 def _stringSource( target, source, env ):
155     return (str(target[0]) + ' <-\n  |' +
156             string.replace( source[0].get_contents(),
157                             '\n', "\n  |" ) )
158
159 # python 2.2 introduces types.BooleanType
160 BooleanTypes = [types.IntType]
161 if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
162
163 class SConfBuildInfo(SCons.Node.FS.FileBuildInfo):
164     """
165     Special build info for targets of configure tests. Additional members
166     are result (did the builder succeed last time?) and string, which
167     contains messages of the original build phase.
168     """
169     result = None # -> 0/None -> no error, != 0 error
170     string = None # the stdout / stderr output when building the target
171
172     def set_build_result(self, result, string):
173         self.result = result
174         self.string = string
175
176
177 class Streamer:
178     """
179     'Sniffer' for a file-like writable object. Similar to the unix tool tee.
180     """
181     def __init__(self, orig):
182         self.orig = orig
183         self.s = StringIO.StringIO()
184
185     def write(self, str):
186         if self.orig:
187             self.orig.write(str)
188         self.s.write(str)
189
190     def writelines(self, lines):
191         for l in lines:
192             self.write(l + '\n')
193
194     def getvalue(self):
195         """
196         Return everything written to orig since the Streamer was created.
197         """
198         return self.s.getvalue()
199
200     def flush(self):
201         if self.orig:
202             self.orig.flush()
203         self.s.flush()
204         
205
206 class SConfBuildTask(SCons.Taskmaster.Task):
207     """
208     This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
209     correctly and knows about the current cache_mode.
210     """
211     def display(self, message):
212         if sconf_global.logstream:
213             sconf_global.logstream.write("scons: Configure: " + message + "\n")
214
215     def display_cached_string(self, bi):
216         """
217         Logs the original builder messages, given the SConfBuildInfo instance
218         bi.
219         """
220         if not isinstance(bi, SConfBuildInfo):
221             SCons.Warnings.warn(SConfWarning,
222               "The stored build information has an unexpected class: %s" % bi.__class__)
223         else:
224             self.display("The original builder output was:\n" +
225                          string.replace("  |" + str(bi.string),
226                                         "\n", "\n  |"))
227
228     def failed(self):
229         # check, if the reason was a ConfigureDryRunError or a
230         # ConfigureCacheError and if yes, reraise the exception
231         exc_type = self.exc_info()[0]
232         if issubclass(exc_type, SConfError):
233             raise
234         elif issubclass(exc_type, SCons.Errors.BuildError):
235             # we ignore Build Errors (occurs, when a test doesn't pass)
236             # Clear the exception to prevent the contained traceback
237             # to build a reference cycle.
238             self.exc_clear()
239         else:
240             self.display('Caught exception while building "%s":\n' %
241                          self.targets[0])
242             try:
243                 excepthook = sys.excepthook
244             except AttributeError:
245                 # Earlier versions of Python don't have sys.excepthook...
246                 def excepthook(type, value, tb):
247                     traceback.print_tb(tb)
248                     print type, value
249             apply(excepthook, self.exc_info())
250         return SCons.Taskmaster.Task.failed(self)
251
252     def collect_node_states(self):
253         # returns (is_up_to_date, cached_error, cachable)
254         # where is_up_to_date is 1, if the node(s) are up_to_date
255         #       cached_error  is 1, if the node(s) are up_to_date, but the
256         #                           build will fail
257         #       cachable      is 0, if some nodes are not in our cache
258         T = 0
259         changed = False
260         cached_error = False
261         cachable = True
262         for t in self.targets:
263             if T: Trace('%s' % (t))
264             bi = t.get_stored_info().binfo
265             if isinstance(bi, SConfBuildInfo):
266                 if T: Trace(': SConfBuildInfo')
267                 if cache_mode == CACHE:
268                     t.set_state(SCons.Node.up_to_date)
269                     if T: Trace(': set_state(up_to-date)')
270                 else:
271                     if T: Trace(': get_state() %s' % t.get_state())
272                     if T: Trace(': changed() %s' % t.changed())
273                     if (t.get_state() != SCons.Node.up_to_date and t.changed()):
274                         changed = True
275                     if T: Trace(': changed %s' % changed)
276                 cached_error = cached_error or bi.result
277             else:
278                 if T: Trace(': else')
279                 # the node hasn't been built in a SConf context or doesn't
280                 # exist
281                 cachable = False
282                 changed = ( t.get_state() != SCons.Node.up_to_date )
283                 if T: Trace(': changed %s' % changed)
284         if T: Trace('\n')
285         return (not changed, cached_error, cachable)
286
287     def execute(self):
288         if not self.targets[0].has_builder():
289             return
290
291         sconf = sconf_global
292
293         is_up_to_date, cached_error, cachable = self.collect_node_states()
294
295         if cache_mode == CACHE and not cachable:
296             raise ConfigureCacheError(self.targets[0])
297         elif cache_mode == FORCE:
298             is_up_to_date = 0
299
300         if cached_error and is_up_to_date:
301             self.display("Building \"%s\" failed in a previous run and all "
302                          "its sources are up to date." % str(self.targets[0]))
303             binfo = self.targets[0].get_stored_info().binfo
304             self.display_cached_string(binfo)
305             raise SCons.Errors.BuildError # will be 'caught' in self.failed
306         elif is_up_to_date:            
307             self.display("\"%s\" is up to date." % str(self.targets[0]))
308             binfo = self.targets[0].get_stored_info().binfo
309             self.display_cached_string(binfo)
310         elif dryrun:
311             raise ConfigureDryRunError(self.targets[0])
312         else:
313             # note stdout and stderr are the same here
314             s = sys.stdout = sys.stderr = Streamer(sys.stdout)
315             try:
316                 env = self.targets[0].get_build_env()
317                 env['PSTDOUT'] = env['PSTDERR'] = s
318                 try:
319                     sconf.cached = 0
320                     self.targets[0].build()
321                 finally:
322                     sys.stdout = sys.stderr = env['PSTDOUT'] = \
323                                  env['PSTDERR'] = sconf.logstream
324             except KeyboardInterrupt:
325                 raise
326             except SystemExit:
327                 exc_value = sys.exc_info()[1]
328                 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
329             except Exception, e:
330                 for t in self.targets:
331                     binfo = t.get_binfo()
332                     binfo.__class__ = SConfBuildInfo
333                     binfo.set_build_result(1, s.getvalue())
334                     sconsign_entry = SCons.SConsign.SConsignEntry()
335                     sconsign_entry.binfo = binfo
336                     #sconsign_entry.ninfo = self.get_ninfo()
337                     # We'd like to do this as follows:
338                     #    t.store_info(binfo)
339                     # However, we need to store it as an SConfBuildInfo
340                     # object, and store_info() will turn it into a
341                     # regular FileNodeInfo if the target is itself a
342                     # regular File.
343                     sconsign = t.dir.sconsign()
344                     sconsign.set_entry(t.name, sconsign_entry)
345                     sconsign.merge()
346                 raise e
347             else:
348                 for t in self.targets:
349                     binfo = t.get_binfo()
350                     binfo.__class__ = SConfBuildInfo
351                     binfo.set_build_result(0, s.getvalue())
352                     sconsign_entry = SCons.SConsign.SConsignEntry()
353                     sconsign_entry.binfo = binfo
354                     #sconsign_entry.ninfo = self.get_ninfo()
355                     # We'd like to do this as follows:
356                     #    t.store_info(binfo)
357                     # However, we need to store it as an SConfBuildInfo
358                     # object, and store_info() will turn it into a
359                     # regular FileNodeInfo if the target is itself a
360                     # regular File.
361                     sconsign = t.dir.sconsign()
362                     sconsign.set_entry(t.name, sconsign_entry)
363                     sconsign.merge()
364
365 class SConfBase:
366     """This is simply a class to represent a configure context. After
367     creating a SConf object, you can call any tests. After finished with your
368     tests, be sure to call the Finish() method, which returns the modified
369     environment.
370     Some words about caching: In most cases, it is not necessary to cache
371     Test results explicitely. Instead, we use the scons dependency checking
372     mechanism. For example, if one wants to compile a test program
373     (SConf.TryLink), the compiler is only called, if the program dependencies
374     have changed. However, if the program could not be compiled in a former
375     SConf run, we need to explicitely cache this error.
376     """
377
378     def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
379                  log_file='$CONFIGURELOG', config_h = None, _depth = 0): 
380         """Constructor. Pass additional tests in the custom_tests-dictinary,
381         e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
382         defines a custom test.
383         Note also the conf_dir and log_file arguments (you may want to
384         build tests in the VariantDir, not in the SourceDir)
385         """
386         global SConfFS
387         if not SConfFS:
388             SConfFS = SCons.Node.FS.default_fs or \
389                       SCons.Node.FS.FS(env.fs.pathTop)
390         if not sconf_global is None:
391             raise (SCons.Errors.UserError,
392                    "Only one SConf object may be active at one time")
393         self.env = env
394         if log_file != None:
395             log_file = SConfFS.File(env.subst(log_file))
396         self.logfile = log_file
397         self.logstream = None
398         self.lastTarget = None
399         self.depth = _depth
400         self.cached = 0 # will be set, if all test results are cached
401
402         # add default tests
403         default_tests = {
404                  'CheckCC'            : CheckCC,
405                  'CheckCXX'           : CheckCXX,
406                  'CheckSHCC'          : CheckSHCC,
407                  'CheckSHCXX'         : CheckSHCXX,
408                  'CheckFunc'          : CheckFunc,
409                  'CheckType'          : CheckType,
410                  'CheckTypeSize'      : CheckTypeSize,
411                  'CheckDeclaration'   : CheckDeclaration,
412                  'CheckHeader'        : CheckHeader,
413                  'CheckCHeader'       : CheckCHeader,
414                  'CheckCXXHeader'     : CheckCXXHeader,
415                  'CheckLib'           : CheckLib,
416                  'CheckLibWithHeader' : CheckLibWithHeader,
417                }
418         self.AddTests(default_tests)
419         self.AddTests(custom_tests)
420         self.confdir = SConfFS.Dir(env.subst(conf_dir))
421         if not config_h is None:
422             config_h = SConfFS.File(config_h)
423         self.config_h = config_h
424         self._startup()
425
426     def Finish(self):
427         """Call this method after finished with your tests:
428                 env = sconf.Finish()
429         """
430         self._shutdown()
431         return self.env
432
433     def Define(self, name, value = None, comment = None):
434         """
435         Define a pre processor symbol name, with the optional given value in the
436         current config header.
437
438         If value is None (default), then #define name is written. If value is not
439         none, then #define name value is written.
440         
441         comment is a string which will be put as a C comment in the
442         header, to explain the meaning of the value (appropriate C comments /* and
443         */ will be put automatically."""
444         lines = []
445         if comment:
446             comment_str = "/* %s */" % comment
447             lines.append(comment_str)
448
449         if value is not None:
450             define_str = "#define %s %s" % (name, value)
451         else:
452             define_str = "#define %s" % name
453         lines.append(define_str)
454         lines.append('')
455
456         self.config_h_text = self.config_h_text + string.join(lines, '\n')
457
458     def BuildNodes(self, nodes):
459         """
460         Tries to build the given nodes immediately. Returns 1 on success,
461         0 on error.
462         """
463         if self.logstream != None:
464             # override stdout / stderr to write in log file
465             oldStdout = sys.stdout
466             sys.stdout = self.logstream
467             oldStderr = sys.stderr
468             sys.stderr = self.logstream
469
470         # the engine assumes the current path is the SConstruct directory ...
471         old_fs_dir = SConfFS.getcwd()
472         old_os_dir = os.getcwd()
473         SConfFS.chdir(SConfFS.Top, change_os_dir=1)
474
475         # Because we take responsibility here for writing out our
476         # own .sconsign info (see SConfBuildTask.execute(), above),
477         # we override the store_info() method with a null place-holder
478         # so we really control how it gets written.
479         for n in nodes:
480             n.store_info = n.do_not_store_info
481
482         ret = 1
483
484         try:
485             # ToDo: use user options for calc
486             save_max_drift = SConfFS.get_max_drift()
487             SConfFS.set_max_drift(0)
488             tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
489             # we don't want to build tests in parallel
490             jobs = SCons.Job.Jobs(1, tm )
491             jobs.run()
492             for n in nodes:
493                 state = n.get_state()
494                 if (state != SCons.Node.executed and
495                     state != SCons.Node.up_to_date):
496                     # the node could not be built. we return 0 in this case
497                     ret = 0
498         finally:
499             SConfFS.set_max_drift(save_max_drift)
500             os.chdir(old_os_dir)
501             SConfFS.chdir(old_fs_dir, change_os_dir=0)
502             if self.logstream != None:
503                 # restore stdout / stderr
504                 sys.stdout = oldStdout
505                 sys.stderr = oldStderr
506         return ret
507
508     def pspawn_wrapper(self, sh, escape, cmd, args, env):
509         """Wrapper function for handling piped spawns.
510
511         This looks to the calling interface (in Action.py) like a "normal"
512         spawn, but associates the call with the PSPAWN variable from
513         the construction environment and with the streams to which we
514         want the output logged.  This gets slid into the construction
515         environment as the SPAWN variable so Action.py doesn't have to
516         know or care whether it's spawning a piped command or not.
517         """
518         return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
519
520
521     def TryBuild(self, builder, text = None, extension = ""):
522         """Low level TryBuild implementation. Normally you don't need to
523         call that - you can use TryCompile / TryLink / TryRun instead
524         """
525         global _ac_build_counter
526
527         # Make sure we have a PSPAWN value, and save the current
528         # SPAWN value.
529         try:
530             self.pspawn = self.env['PSPAWN']
531         except KeyError:
532             raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
533         try:
534             save_spawn = self.env['SPAWN']
535         except KeyError:
536             raise SCons.Errors.UserError('Missing SPAWN construction variable.')
537
538         nodesToBeBuilt = []
539
540         f = "conftest_" + str(_ac_build_counter)
541         pref = self.env.subst( builder.builder.prefix )
542         suff = self.env.subst( builder.builder.suffix )
543         target = self.confdir.File(pref + f + suff)
544
545         try:
546             # Slide our wrapper into the construction environment as
547             # the SPAWN function.
548             self.env['SPAWN'] = self.pspawn_wrapper
549             sourcetext = self.env.Value(text)
550
551             if text != None:
552                 textFile = self.confdir.File(f + extension)
553                 textFileNode = self.env.SConfSourceBuilder(target=textFile,
554                                                            source=sourcetext)
555                 nodesToBeBuilt.extend(textFileNode)
556                 source = textFileNode
557             else:
558                 source = None
559
560             nodes = builder(target = target, source = source)
561             if not SCons.Util.is_List(nodes):
562                 nodes = [nodes]
563             nodesToBeBuilt.extend(nodes)
564             result = self.BuildNodes(nodesToBeBuilt)
565
566         finally:
567             self.env['SPAWN'] = save_spawn
568
569         _ac_build_counter = _ac_build_counter + 1
570         if result:
571             self.lastTarget = nodes[0]
572         else:
573             self.lastTarget = None
574
575         return result
576
577     def TryAction(self, action, text = None, extension = ""):
578         """Tries to execute the given action with optional source file
579         contents <text> and optional source file extension <extension>,
580         Returns the status (0 : failed, 1 : ok) and the contents of the
581         output file.
582         """
583         builder = SCons.Builder.Builder(action=action)
584         self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
585         ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
586         del self.env['BUILDERS']['SConfActionBuilder']
587         if ok:
588             outputStr = self.lastTarget.get_contents()
589             return (1, outputStr)
590         return (0, "")
591
592     def TryCompile( self, text, extension):
593         """Compiles the program given in text to an env.Object, using extension
594         as file extension (e.g. '.c'). Returns 1, if compilation was
595         successful, 0 otherwise. The target is saved in self.lastTarget (for
596         further processing).
597         """
598         return self.TryBuild(self.env.Object, text, extension)
599
600     def TryLink( self, text, extension ):
601         """Compiles the program given in text to an executable env.Program,
602         using extension as file extension (e.g. '.c'). Returns 1, if
603         compilation was successful, 0 otherwise. The target is saved in
604         self.lastTarget (for further processing).
605         """
606         return self.TryBuild(self.env.Program, text, extension )
607
608     def TryRun(self, text, extension ):
609         """Compiles and runs the program given in text, using extension
610         as file extension (e.g. '.c'). Returns (1, outputStr) on success,
611         (0, '') otherwise. The target (a file containing the program's stdout)
612         is saved in self.lastTarget (for further processing).
613         """
614         ok = self.TryLink(text, extension)
615         if( ok ):
616             prog = self.lastTarget
617             pname = str(prog)
618             output = SConfFS.File(pname+'.out')
619             node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
620             ok = self.BuildNodes(node)
621             if ok:
622                 outputStr = output.get_contents()
623                 return( 1, outputStr)
624         return (0, "")
625
626     class TestWrapper:
627         """A wrapper around Tests (to ensure sanity)"""
628         def __init__(self, test, sconf):
629             self.test = test
630             self.sconf = sconf
631         def __call__(self, *args, **kw):
632             if not self.sconf.active:
633                 raise (SCons.Errors.UserError,
634                        "Test called after sconf.Finish()")
635             context = CheckContext(self.sconf)
636             ret = apply(self.test, (context,) +  args, kw)
637             if not self.sconf.config_h is None:
638                 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
639             context.Result("error: no result")
640             return ret
641
642     def AddTest(self, test_name, test_instance):
643         """Adds test_class to this SConf instance. It can be called with
644         self.test_name(...)"""
645         setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
646
647     def AddTests(self, tests):
648         """Adds all the tests given in the tests dictionary to this SConf
649         instance
650         """
651         for name in tests.keys():
652             self.AddTest(name, tests[name])
653
654     def _createDir( self, node ):
655         dirName = str(node)
656         if dryrun:
657             if not os.path.isdir( dirName ):
658                 raise ConfigureDryRunError(dirName)
659         else:
660             if not os.path.isdir( dirName ):
661                 os.makedirs( dirName )
662                 node._exists = 1
663
664     def _startup(self):
665         """Private method. Set up logstream, and set the environment
666         variables necessary for a piped build
667         """
668         global _ac_config_logs
669         global sconf_global
670         global SConfFS
671         
672         self.lastEnvFs = self.env.fs
673         self.env.fs = SConfFS
674         self._createDir(self.confdir)
675         self.confdir.up().add_ignore( [self.confdir] )
676
677         if self.logfile != None and not dryrun:
678             # truncate logfile, if SConf.Configure is called for the first time
679             # in a build
680             if _ac_config_logs.has_key(self.logfile):
681                 log_mode = "a"
682             else:
683                 _ac_config_logs[self.logfile] = None
684                 log_mode = "w"
685             fp = open(str(self.logfile), log_mode)
686             self.logstream = SCons.Util.Unbuffered(fp)
687             # logfile may stay in a build directory, so we tell
688             # the build system not to override it with a eventually
689             # existing file with the same name in the source directory
690             self.logfile.dir.add_ignore( [self.logfile] )
691
692             tb = traceback.extract_stack()[-3-self.depth]
693             old_fs_dir = SConfFS.getcwd()
694             SConfFS.chdir(SConfFS.Top, change_os_dir=0)
695             self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' %
696                                  (tb[0], tb[1], str(self.confdir)) )
697             SConfFS.chdir(old_fs_dir)
698         else: 
699             self.logstream = None
700         # we use a special builder to create source files from TEXT
701         action = SCons.Action.Action(_createSource,
702                                      _stringSource)
703         sconfSrcBld = SCons.Builder.Builder(action=action)
704         self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
705         self.config_h_text = _ac_config_hs.get(self.config_h, "")
706         self.active = 1
707         # only one SConf instance should be active at a time ...
708         sconf_global = self
709
710     def _shutdown(self):
711         """Private method. Reset to non-piped spawn"""
712         global sconf_global, _ac_config_hs
713
714         if not self.active:
715             raise SCons.Errors.UserError, "Finish may be called only once!"
716         if self.logstream != None and not dryrun:
717             self.logstream.write("\n")
718             self.logstream.close()
719             self.logstream = None
720         # remove the SConfSourceBuilder from the environment
721         blds = self.env['BUILDERS']
722         del blds['SConfSourceBuilder']
723         self.env.Replace( BUILDERS=blds )
724         self.active = 0
725         sconf_global = None
726         if not self.config_h is None:
727             _ac_config_hs[self.config_h] = self.config_h_text
728         self.env.fs = self.lastEnvFs
729
730 class CheckContext:
731     """Provides a context for configure tests. Defines how a test writes to the
732     screen and log file.
733
734     A typical test is just a callable with an instance of CheckContext as
735     first argument:
736
737     def CheckCustom(context, ...)
738     context.Message('Checking my weird test ... ')
739     ret = myWeirdTestFunction(...)
740     context.Result(ret)
741
742     Often, myWeirdTestFunction will be one of
743     context.TryCompile/context.TryLink/context.TryRun. The results of
744     those are cached, for they are only rebuild, if the dependencies have
745     changed.
746     """
747
748     def __init__(self, sconf):
749         """Constructor. Pass the corresponding SConf instance."""
750         self.sconf = sconf
751         self.did_show_result = 0
752
753         # for Conftest.py:
754         self.vardict = {}
755         self.havedict = {}
756         self.headerfilename = None
757         self.config_h = "" # config_h text will be stored here
758         # we don't regenerate the config.h file after each test. That means,
759         # that tests won't be able to include the config.h file, and so
760         # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major
761         # issue, though. If it turns out, that we need to include config.h
762         # in tests, we must ensure, that the dependencies are worked out
763         # correctly. Note that we can't use Conftest.py's support for config.h,
764         # cause we will need to specify a builder for the config.h file ...
765
766     def Message(self, text):
767         """Inform about what we are doing right now, e.g.
768         'Checking for SOMETHING ... '
769         """
770         self.Display(text)
771         self.sconf.cached = 1
772         self.did_show_result = 0
773
774     def Result(self, res):
775         """Inform about the result of the test. res may be an integer or a
776         string. In case of an integer, the written text will be 'ok' or
777         'failed'.
778         The result is only displayed when self.did_show_result is not set.
779         """
780         if type(res) in BooleanTypes:
781             if res:
782                 text = "yes"
783             else:
784                 text = "no"
785         elif type(res) == types.StringType:
786             text = res
787         else:
788             raise TypeError, "Expected string, int or bool, got " + str(type(res))
789
790         if self.did_show_result == 0:
791             # Didn't show result yet, do it now.
792             self.Display(text + "\n")
793             self.did_show_result = 1
794
795     def TryBuild(self, *args, **kw):
796         return apply(self.sconf.TryBuild, args, kw)
797
798     def TryAction(self, *args, **kw):
799         return apply(self.sconf.TryAction, args, kw)
800
801     def TryCompile(self, *args, **kw):
802         return apply(self.sconf.TryCompile, args, kw)
803
804     def TryLink(self, *args, **kw):
805         return apply(self.sconf.TryLink, args, kw)
806
807     def TryRun(self, *args, **kw):
808         return apply(self.sconf.TryRun, args, kw)
809
810     def __getattr__( self, attr ):
811         if( attr == 'env' ):
812             return self.sconf.env
813         elif( attr == 'lastTarget' ):
814             return self.sconf.lastTarget
815         else:
816             raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
817
818     #### Stuff used by Conftest.py (look there for explanations).
819
820     def BuildProg(self, text, ext):
821         self.sconf.cached = 1
822         # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
823         return not self.TryBuild(self.env.Program, text, ext)
824
825     def CompileProg(self, text, ext):
826         self.sconf.cached = 1
827         # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
828         return not self.TryBuild(self.env.Object, text, ext)
829
830     def CompileSharedObject(self, text, ext):
831         self.sconf.cached = 1
832         # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc.
833         return not self.TryBuild(self.env.SharedObject, text, ext)
834
835     def RunProg(self, text, ext):
836         self.sconf.cached = 1
837         # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
838         st, out = self.TryRun(text, ext)
839         return not st, out
840
841     def AppendLIBS(self, lib_name_list):
842         oldLIBS = self.env.get( 'LIBS', [] )
843         self.env.Append(LIBS = lib_name_list)
844         return oldLIBS
845
846     def SetLIBS(self, val):
847         oldLIBS = self.env.get( 'LIBS', [] )
848         self.env.Replace(LIBS = val)
849         return oldLIBS
850
851     def Display(self, msg):
852         if self.sconf.cached:
853             # We assume that Display is called twice for each test here
854             # once for the Checking for ... message and once for the result.
855             # The self.sconf.cached flag can only be set between those calls
856             msg = "(cached) " + msg
857             self.sconf.cached = 0
858         progress_display(msg, append_newline=0)
859         self.Log("scons: Configure: " + msg + "\n")
860
861     def Log(self, msg):
862         if self.sconf.logstream != None:
863             self.sconf.logstream.write(msg)
864
865     #### End of stuff used by Conftest.py.
866
867
868 def SConf(*args, **kw):
869     if kw.get(build_type, True):
870         kw['_depth'] = kw.get('_depth', 0) + 1
871         for bt in build_types:
872             try:
873                 del kw[bt]
874             except KeyError:
875                 pass
876         return apply(SConfBase, args, kw)
877     else:
878         return SCons.Util.Null()
879
880
881 def CheckFunc(context, function_name, header = None, language = None):
882     res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language)
883     context.did_show_result = 1
884     return not res
885
886 def CheckType(context, type_name, includes = "", language = None):
887     res = SCons.Conftest.CheckType(context, type_name,
888                                         header = includes, language = language)
889     context.did_show_result = 1
890     return not res
891
892 def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
893     res = SCons.Conftest.CheckTypeSize(context, type_name,
894                                        header = includes, language = language, 
895                                        expect = expect)
896     context.did_show_result = 1
897     return res
898
899 def CheckDeclaration(context, declaration, includes = "", language = None):
900     res = SCons.Conftest.CheckDeclaration(context, declaration,
901                                           includes = includes, 
902                                           language = language)
903     context.did_show_result = 1
904     return not res
905
906 def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
907     # used by CheckHeader and CheckLibWithHeader to produce C - #include
908     # statements from the specified header (list)
909     if not SCons.Util.is_List(headers):
910         headers = [headers]
911     l = []
912     if leaveLast:
913         lastHeader = headers[-1]
914         headers = headers[:-1]
915     else:
916         lastHeader = None
917     for s in headers:
918         l.append("#include %s%s%s\n"
919                  % (include_quotes[0], s, include_quotes[1]))
920     return string.join(l, ''), lastHeader
921
922 def CheckHeader(context, header, include_quotes = '<>', language = None):
923     """
924     A test for a C or C++ header file.
925     """
926     prog_prefix, hdr_to_check = \
927                  createIncludesFromHeaders(header, 1, include_quotes)
928     res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
929                                      language = language,
930                                      include_quotes = include_quotes)
931     context.did_show_result = 1
932     return not res
933
934 def CheckCC(context):
935     res = SCons.Conftest.CheckCC(context)
936     return not res
937
938 def CheckCXX(context):
939     res = SCons.Conftest.CheckCXX(context)
940     return not res
941
942 def CheckSHCC(context):
943     res = SCons.Conftest.CheckSHCC(context)
944     return not res
945
946 def CheckSHCXX(context):
947     res = SCons.Conftest.CheckSHCXX(context)
948     return not res
949
950 # Bram: Make this function obsolete?  CheckHeader() is more generic.
951
952 def CheckCHeader(context, header, include_quotes = '""'):
953     """
954     A test for a C header file.
955     """
956     return CheckHeader(context, header, include_quotes, language = "C")
957
958
959 # Bram: Make this function obsolete?  CheckHeader() is more generic.
960
961 def CheckCXXHeader(context, header, include_quotes = '""'):
962     """
963     A test for a C++ header file.
964     """
965     return CheckHeader(context, header, include_quotes, language = "C++")
966
967
968 def CheckLib(context, library = None, symbol = "main",
969              header = None, language = None, autoadd = 1):
970     """
971     A test for a library. See also CheckLibWithHeader.
972     Note that library may also be None to test whether the given symbol
973     compiles without flags.
974     """
975
976     if library == []:
977         library = [None]
978
979     if not SCons.Util.is_List(library):
980         library = [library]
981     
982     # ToDo: accept path for the library
983     res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
984                                         language = language, autoadd = autoadd)
985     context.did_show_result = 1
986     return not res
987
988 # XXX
989 # Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H.
990
991 def CheckLibWithHeader(context, libs, header, language,
992                        call = None, autoadd = 1):
993     # ToDo: accept path for library. Support system header files.
994     """
995     Another (more sophisticated) test for a library.
996     Checks, if library and header is available for language (may be 'C'
997     or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
998     As in CheckLib, we support library=None, to test if the call compiles
999     without extra link flags.
1000     """
1001     prog_prefix, dummy = \
1002                  createIncludesFromHeaders(header, 0)
1003     if libs == []:
1004         libs = [None]
1005
1006     if not SCons.Util.is_List(libs):
1007         libs = [libs]
1008
1009     res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1010             call = call, language = language, autoadd = autoadd)
1011     context.did_show_result = 1
1012     return not res