SCons.SConf

Package SCons :: Module SConf |
[hide private]
[frames] | no frames]

# Source Code for Module SCons.SConf

      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 2725 2008/03/31 12:52:02 knight"
     30
     31  import SCons.compat
     32
     33  import os
     34  import re
     35  import string
     36  import StringIO
     37  import sys
     38  import traceback
     39  import types
     40
     41  import SCons.Action
     42  import SCons.Builder
     43  import SCons.Errors
     44  import SCons.Job
     45  import SCons.Node.FS
     46  import SCons.Taskmaster
     47  import SCons.Util
     48  import SCons.Warnings
     49  import SCons.Conftest
     50
     51  from SCons.Debug import Trace
     52
     53  # Turn off the Conftest error logging
     54  SCons.Conftest.LogInputFiles = 0
     55  SCons.Conftest.LogErrorMessages = 0
     56
     57  # Set
     58  build_type = None
     59  build_types = ['clean', 'help']
     60
     61 -def SetBuildType(type):
     62      global build_type
     63      build_type = type
     64
     65  # to be set, if we are in dry-run mode
     66  dryrun = 0
     67
     68  AUTO=0  # use SCons dependency scanning for up-to-date checks
     69  FORCE=1 # force all tests to be rebuilt
     70  CACHE=2 # force all tests to be taken from cache (raise an error, if necessary)
     71  cache_mode = AUTO
     72
     73 -def SetCacheMode(mode):
     74      """Set the Configure cache mode. mode must be one of "auto", "force",
     75      or "cache"."""
     76      global cache_mode
     77      if mode == "auto":
     78          cache_mode = AUTO
     79      elif mode == "force":
     80          cache_mode = FORCE
     81      elif mode == "cache":
     82          cache_mode = CACHE
     83      else:
     84          raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
     85
     86  progress_display = SCons.Util.display # will be overwritten by SCons.Script
     87 -def SetProgressDisplay(display):
     88      """Set the progress display to use (called from SCons.Script)"""
     89      global progress_display
     90      progress_display = display
     91
     92  SConfFS = None
     93
     94  _ac_build_counter = 0 # incremented, whenever TryBuild is called
     95  _ac_config_logs = {}  # all config.log files created in this build
     96  _ac_config_hs   = {}  # all config.h files created in this build
     97  sconf_global = None   # current sconf object
     98
     99 -def _createConfigH(target, source, env):
    100      t = open(str(target[0]), "w")
    101      defname = re.sub('[^A-Za-z0-9_]', '_', string.upper(str(target[0])))
    102      t.write("""#ifndef %(DEFNAME)s_SEEN
    103  #define %(DEFNAME)s_SEEN
    104
    105  """ % {'DEFNAME' : defname})
    106      t.write(source[0].get_contents())
    107      t.write("""
    108  #endif /* %(DEFNAME)s_SEEN */
    109  """ % {'DEFNAME' : defname})
    110      t.close()
    111
    112 -def _stringConfigH(target, source, env):
    113      return "scons: Configure: creating " + str(target[0])
    114
    115 -def CreateConfigHBuilder(env):
    116      """Called just before the building targets phase begins."""
    117      if len(_ac_config_hs) == 0:
    118          return
    119      action = SCons.Action.Action(_createConfigH,
    120                                   _stringConfigH)
    121      sconfigHBld = SCons.Builder.Builder(action=action)
    122      env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} )
    123      for k in _ac_config_hs.keys():
    124          env.SConfigHBuilder(k, env.Value(_ac_config_hs[k]))
    125
    126 -class SConfWarning(SCons.Warnings.Warning):
    127      pass
    128  SCons.Warnings.enableWarningClass(SConfWarning)
    129
    130  # some error definitions
    131 -class SConfError(SCons.Errors.UserError):
    132 -    def __init__(self,msg):
    133          SCons.Errors.UserError.__init__(self,msg)
    134
    135 -class ConfigureDryRunError(SConfError):
    136      """Raised when a file or directory needs to be updated during a Configure
    137      process, but the user requested a dry-run"""
    138 -    def __init__(self,target):
    139          if not isinstance(target, SCons.Node.FS.File):
    140              msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target)
    141          else:
    142              msg = 'Cannot update configure test "%s" within a dry-run.' % str(target)
    143          SConfError.__init__(self,msg)
    144
    145 -class ConfigureCacheError(SConfError):
    146      """Raised when a use explicitely requested the cache feature, but the test
    147      is run the first time."""
    148 -    def __init__(self,target):
    149          SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target))
    150
    151  # define actions for building text files
    152 -def _createSource( target, source, env ):
    153      fd = open(str(target[0]), "w")
    154      fd.write(source[0].get_contents())
    155      fd.close()
    156 -def _stringSource( target, source, env ):
    157      return (str(target[0]) + ' <-\n  |' +
    158              string.replace( source[0].get_contents(),
    159                              '\n', "\n  |" ) )
    160
    161  # python 2.2 introduces types.BooleanType
    162  BooleanTypes = [types.IntType]
    163  if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
    164
    165 -class SConfBuildInfo(SCons.Node.FS.FileBuildInfo):
    166      """
    167      Special build info for targets of configure tests. Additional members
    168      are result (did the builder succeed last time?) and string, which
    169      contains messages of the original build phase.
    170      """
    171      result = None # -> 0/None -> no error, != 0 error
    172      string = None # the stdout / stderr output when building the target
    173
    174 -    def set_build_result(self, result, string):
    175          self.result = result
    176          self.string = string
    177
    178
    179 -class Streamer:
    180      """
    181      'Sniffer' for a file-like writable object. Similar to the unix tool tee.
    182      """
    183 -    def __init__(self, orig):
    184          self.orig = orig
    185          self.s = StringIO.StringIO()
    186
    187 -    def write(self, str):
    188          if self.orig:
    189              self.orig.write(str)
    190          self.s.write(str)
    191
    192 -    def writelines(self, lines):
    193          for l in lines:
    194              self.write(l + '\n')
    195
    196 -    def getvalue(self):
    197          """
    198          Return everything written to orig since the Streamer was created.
    199          """
    200          return self.s.getvalue()
    201
    202 -    def flush(self):
    203          if self.orig:
    204              self.orig.flush()
    205          self.s.flush()
    206
    207
    208 -class SConfBuildTask(SCons.Taskmaster.Task):
    209      """
    210      This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
    211      correctly and knows about the current cache_mode.
    212      """
    213 -    def display(self, message):
    214          if sconf_global.logstream:
    215              sconf_global.logstream.write("scons: Configure: " + message + "\n")
    216
    217 -    def display_cached_string(self, bi):
    218          """
    219          Logs the original builder messages, given the SConfBuildInfo instance
    220          bi.
    221          """
    222          if not isinstance(bi, SConfBuildInfo):
    223              SCons.Warnings.warn(SConfWarning,
    224                "The stored build information has an unexpected class: %s" % bi.__class__)
    225          else:
    226              self.display("The original builder output was:\n" +
    227                           string.replace("  |" + str(bi.string),
    228                                          "\n", "\n  |"))
    229
    230 -    def failed(self):
    231          # check, if the reason was a ConfigureDryRunError or a
    232          # ConfigureCacheError and if yes, reraise the exception
    233          exc_type = self.exc_info()[0]
    234          if issubclass(exc_type, SConfError):
    235              raise
    236          elif issubclass(exc_type, SCons.Errors.BuildError):
    237              # we ignore Build Errors (occurs, when a test doesn't pass)
    238              pass
    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                   'CheckFunc'          : CheckFunc,
    405                   'CheckType'          : CheckType,
    406                   'CheckTypeSize'      : CheckTypeSize,
    407                   'CheckDeclaration'   : CheckDeclaration,
    408                   'CheckHeader'        : CheckHeader,
    409                   'CheckCHeader'       : CheckCHeader,
    410                   'CheckCXXHeader'     : CheckCXXHeader,
    411                   'CheckLib'           : CheckLib,
    412                   'CheckLibWithHeader' : CheckLibWithHeader,
    413                 }
    414          self.AddTests(default_tests)
    415          self.AddTests(custom_tests)
    416          self.confdir = SConfFS.Dir(env.subst(conf_dir))
    417          if not config_h is None:
    418              config_h = SConfFS.File(config_h)
    419          self.config_h = config_h
    420          self._startup()
    421
    422 -    def Finish(self):
    423          """Call this method after finished with your tests:
    424                  env = sconf.Finish()
    425          """
    426          self._shutdown()
    427          return self.env
    428
    429 -    def Define(self, name, value = None, comment = None):
    430          """
    431          Define a pre processor symbol name, with the optional given value in the
    432          current config header.
    433
    434          If value is None (default), then #define name is written. If value is not
    435          none, then #define name value is written.
    436
    437          comment is a string which will be put as a C comment in the
    438          header, to explain the meaning of the value (appropriate C comments /* and
    439          */ will be put automatically."""
    440          lines = []
    441          if comment:
    442              comment_str = "/* %s */" % comment
    443              lines.append(comment_str)
    444
    445          if value is not None:
    446              define_str = "#define %s %s" % (name, value)
    447          else:
    448              define_str = "#define %s" % name
    449          lines.append(define_str)
    450          lines.append('')
    451
    452          self.config_h_text = self.config_h_text + string.join(lines, '\n')
    453
    454 -    def BuildNodes(self, nodes):
    455          """
    456          Tries to build the given nodes immediately. Returns 1 on success,
    457          0 on error.
    458          """
    459          if self.logstream != None:
    460              # override stdout / stderr to write in log file
    461              oldStdout = sys.stdout
    462              sys.stdout = self.logstream
    463              oldStderr = sys.stderr
    464              sys.stderr = self.logstream
    465
    466          # the engine assumes the current path is the SConstruct directory ...
    467          old_fs_dir = SConfFS.getcwd()
    468          old_os_dir = os.getcwd()
    469          SConfFS.chdir(SConfFS.Top, change_os_dir=1)
    470
    471          # Because we take responsibility here for writing out our
    472          # own .sconsign info (see SConfBuildTask.execute(), above),
    473          # we override the store_info() method with a null place-holder
    474          # so we really control how it gets written.
    475          for n in nodes:
    476              n.store_info = n.do_not_store_info
    477
    478          ret = 1
    479
    480          try:
    481              # ToDo: use user options for calc
    482              save_max_drift = SConfFS.get_max_drift()
    483              SConfFS.set_max_drift(0)
    484              tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
    485              # we don't want to build tests in parallel
    486              jobs = SCons.Job.Jobs(1, tm )
    487              jobs.run()
    488              for n in nodes:
    489                  state = n.get_state()
    490                  if (state != SCons.Node.executed and
    491                      state != SCons.Node.up_to_date):
    492                      # the node could not be built. we return 0 in this case
    493                      ret = 0
    494          finally:
    495              SConfFS.set_max_drift(save_max_drift)
    496              os.chdir(old_os_dir)
    497              SConfFS.chdir(old_fs_dir, change_os_dir=0)
    498              if self.logstream != None:
    499                  # restore stdout / stderr
    500                  sys.stdout = oldStdout
    501                  sys.stderr = oldStderr
    502          return ret
    503
    504 -    def pspawn_wrapper(self, sh, escape, cmd, args, env):
    505          """Wrapper function for handling piped spawns.
    506
    507          This looks to the calling interface (in Action.py) like a "normal"
    508          spawn, but associates the call with the PSPAWN variable from
    509          the construction environment and with the streams to which we
    510          want the output logged.  This gets slid into the construction
    511          environment as the SPAWN variable so Action.py doesn't have to
    512          know or care whether it's spawning a piped command or not.
    513          """
    514          return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
    515
    516
    517 -    def TryBuild(self, builder, text = None, extension = ""):
    518          """Low level TryBuild implementation. Normally you don't need to
    519          call that - you can use TryCompile / TryLink / TryRun instead
    520          """
    521          global _ac_build_counter
    522
    523          # Make sure we have a PSPAWN value, and save the current
    524          # SPAWN value.
    525          try:
    526              self.pspawn = self.env['PSPAWN']
    527          except KeyError:
    528              raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
    529          try:
    530              save_spawn = self.env['SPAWN']
    531          except KeyError:
    532              raise SCons.Errors.UserError('Missing SPAWN construction variable.')
    533
    534          nodesToBeBuilt = []
    535
    536          f = "conftest_" + str(_ac_build_counter)
    537          pref = self.env.subst( builder.builder.prefix )
    538          suff = self.env.subst( builder.builder.suffix )
    539          target = self.confdir.File(pref + f + suff)
    540
    541          try:
    542              # Slide our wrapper into the construction environment as
    543              # the SPAWN function.
    544              self.env['SPAWN'] = self.pspawn_wrapper
    545              sourcetext = self.env.Value(text)
    546
    547              if text != None:
    548                  textFile = self.confdir.File(f + extension)
    549                  textFileNode = self.env.SConfSourceBuilder(target=textFile,
    550                                                             source=sourcetext)
    551                  nodesToBeBuilt.extend(textFileNode)
    552                  source = textFileNode
    553              else:
    554                  source = None
    555
    556              nodes = builder(target = target, source = source)
    557              if not SCons.Util.is_List(nodes):
    558                  nodes = [nodes]
    559              nodesToBeBuilt.extend(nodes)
    560              result = self.BuildNodes(nodesToBeBuilt)
    561
    562          finally:
    563              self.env['SPAWN'] = save_spawn
    564
    565          _ac_build_counter = _ac_build_counter + 1
    566          if result:
    567              self.lastTarget = nodes[0]
    568          else:
    569              self.lastTarget = None
    570
    571          return result
    572
    573 -    def TryAction(self, action, text = None, extension = ""):
    574          """Tries to execute the given action with optional source file
    575          contents <text> and optional source file extension <extension>,
    576          Returns the status (0 : failed, 1 : ok) and the contents of the
    577          output file.
    578          """
    579          builder = SCons.Builder.Builder(action=action)
    580          self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
    581          ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
    582          del self.env['BUILDERS']['SConfActionBuilder']
    583          if ok:
    584              outputStr = self.lastTarget.get_contents()
    585              return (1, outputStr)
    586          return (0, "")
    587
    588 -    def TryCompile( self, text, extension):
    589          """Compiles the program given in text to an env.Object, using extension
    590          as file extension (e.g. '.c'). Returns 1, if compilation was
    591          successful, 0 otherwise. The target is saved in self.lastTarget (for
    592          further processing).
    593          """
    594          return self.TryBuild(self.env.Object, text, extension)
    595
    596 -    def TryLink( self, text, extension ):
    597          """Compiles the program given in text to an executable env.Program,
    598          using extension as file extension (e.g. '.c'). Returns 1, if
    599          compilation was successful, 0 otherwise. The target is saved in
    600          self.lastTarget (for further processing).
    601          """
    602          return self.TryBuild(self.env.Program, text, extension )
    603
    604 -    def TryRun(self, text, extension ):
    605          """Compiles and runs the program given in text, using extension
    606          as file extension (e.g. '.c'). Returns (1, outputStr) on success,
    607          (0, '') otherwise. The target (a file containing the program's stdout)
    608          is saved in self.lastTarget (for further processing).
    609          """
    610          ok = self.TryLink(text, extension)
    611          if( ok ):
    612              prog = self.lastTarget
    613              pname = str(prog)
    614              output = SConfFS.File(pname+'.out')
    615              node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
    616              ok = self.BuildNodes(node)
    617              if ok:
    618                  outputStr = output.get_contents()
    619                  return( 1, outputStr)
    620          return (0, "")
    621
    622 -    class TestWrapper:
    623          """A wrapper around Tests (to ensure sanity)"""
    624 -        def __init__(self, test, sconf):
    625              self.test = test
    626              self.sconf = sconf
    627 -        def __call__(self, *args, **kw):
    628              if not self.sconf.active:
    629                  raise (SCons.Errors.UserError,
    630                         "Test called after sconf.Finish()")
    631              context = CheckContext(self.sconf)
    632              ret = apply(self.test, (context,) +  args, kw)
    633              if not self.sconf.config_h is None:
    634                  self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
    635              context.Result("error: no result")
    636              return ret
    637
    638 -    def AddTest(self, test_name, test_instance):
    639          """Adds test_class to this SConf instance. It can be called with
    640          self.test_name(...)"""
    641          setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
    642
    643 -    def AddTests(self, tests):
    644          """Adds all the tests given in the tests dictionary to this SConf
    645          instance
    646          """
    647          for name in tests.keys():
    648              self.AddTest(name, tests[name])
    649
    650 -    def _createDir( self, node ):
    651          dirName = str(node)
    652          if dryrun:
    653              if not os.path.isdir( dirName ):
    654                  raise ConfigureDryRunError(dirName)
    655          else:
    656              if not os.path.isdir( dirName ):
    657                  os.makedirs( dirName )
    658                  node._exists = 1
    659
    660 -    def _startup(self):
    661          """Private method. Set up logstream, and set the environment
    662          variables necessary for a piped build
    663          """
    664          global _ac_config_logs
    665          global sconf_global
    666          global SConfFS
    667
    668          self.lastEnvFs = self.env.fs
    669          self.env.fs = SConfFS
    670          self._createDir(self.confdir)
    671          self.confdir.up().add_ignore( [self.confdir] )
    672
    673          if self.logfile != None and not dryrun:
    674              # truncate logfile, if SConf.Configure is called for the first time
    675              # in a build
    676              if _ac_config_logs.has_key(self.logfile):
    677                  log_mode = "a"
    678              else:
    679                  _ac_config_logs[self.logfile] = None
    680                  log_mode = "w"
    681              fp = open(str(self.logfile), log_mode)
    682              self.logstream = SCons.Util.Unbuffered(fp)
    683              # logfile may stay in a build directory, so we tell
    684              # the build system not to override it with a eventually
    685              # existing file with the same name in the source directory
    686              self.logfile.dir.add_ignore( [self.logfile] )
    687
    688              tb = traceback.extract_stack()[-3-self.depth]
    689              old_fs_dir = SConfFS.getcwd()
    690              SConfFS.chdir(SConfFS.Top, change_os_dir=0)
    691              self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' %
    692                                   (tb[0], tb[1], str(self.confdir)) )
    693              SConfFS.chdir(old_fs_dir)
    694          else:
    695              self.logstream = None
    696          # we use a special builder to create source files from TEXT
    697          action = SCons.Action.Action(_createSource,
    698                                       _stringSource)
    699          sconfSrcBld = SCons.Builder.Builder(action=action)
    700          self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
    701          self.config_h_text = _ac_config_hs.get(self.config_h, "")
    702          self.active = 1
    703          # only one SConf instance should be active at a time ...
    704          sconf_global = self
    705
    706 -    def _shutdown(self):
    707          """Private method. Reset to non-piped spawn"""
    708          global sconf_global, _ac_config_hs
    709
    710          if not self.active:
    711              raise SCons.Errors.UserError, "Finish may be called only once!"
    712          if self.logstream != None and not dryrun:
    713              self.logstream.write("\n")
    714              self.logstream.close()
    715              self.logstream = None
    716          # remove the SConfSourceBuilder from the environment
    717          blds = self.env['BUILDERS']
    718          del blds['SConfSourceBuilder']
    719          self.env.Replace( BUILDERS=blds )
    720          self.active = 0
    721          sconf_global = None
    722          if not self.config_h is None:
    723              _ac_config_hs[self.config_h] = self.config_h_text
    724          self.env.fs = self.lastEnvFs
    725
    726 -class CheckContext:
    727      """Provides a context for configure tests. Defines how a test writes to the
    728      screen and log file.
    729
    730      A typical test is just a callable with an instance of CheckContext as
    731      first argument:
    732
    733      def CheckCustom(context, ...)
    734      context.Message('Checking my weird test ... ')
    735      ret = myWeirdTestFunction(...)
    736      context.Result(ret)
    737
    738      Often, myWeirdTestFunction will be one of
    739      context.TryCompile/context.TryLink/context.TryRun. The results of
    740      those are cached, for they are only rebuild, if the dependencies have
    741      changed.
    742      """
    743
    744 -    def __init__(self, sconf):
    745          """Constructor. Pass the corresponding SConf instance."""
    746          self.sconf = sconf
    747          self.did_show_result = 0
    748
    749          # for Conftest.py:
    750          self.vardict = {}
    751          self.havedict = {}
    752          self.headerfilename = None
    753          self.config_h = "" # config_h text will be stored here
    754          # we don't regenerate the config.h file after each test. That means,
    755          # that tests won't be able to include the config.h file, and so
    756          # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major
    757          # issue, though. If it turns out, that we need to include config.h
    758          # in tests, we must ensure, that the dependencies are worked out
    759          # correctly. Note that we can't use Conftest.py's support for config.h,
    760          # cause we will need to specify a builder for the config.h file ...
    761
    762 -    def Message(self, text):
    763          """Inform about what we are doing right now, e.g.
    764          'Checking for SOMETHING ... '
    765          """
    766          self.Display(text)
    767          self.sconf.cached = 1
    768          self.did_show_result = 0
    769
    770 -    def Result(self, res):
    771          """Inform about the result of the test. res may be an integer or a
    772          string. In case of an integer, the written text will be 'ok' or
    773          'failed'.
    774          The result is only displayed when self.did_show_result is not set.
    775          """
    776          if type(res) in BooleanTypes:
    777              if res:
    778                  text = "yes"
    779              else:
    780                  text = "no"
    781          elif type(res) == types.StringType:
    782              text = res
    783          else:
    784              raise TypeError, "Expected string, int or bool, got " + str(type(res))
    785
    786          if self.did_show_result == 0:
    787              # Didn't show result yet, do it now.
    788              self.Display(text + "\n")
    789              self.did_show_result = 1
    790
    791 -    def TryBuild(self, *args, **kw):
    792          return apply(self.sconf.TryBuild, args, kw)
    793
    794 -    def TryAction(self, *args, **kw):
    795          return apply(self.sconf.TryAction, args, kw)
    796
    797 -    def TryCompile(self, *args, **kw):
    798          return apply(self.sconf.TryCompile, args, kw)
    799
    800 -    def TryLink(self, *args, **kw):
    801          return apply(self.sconf.TryLink, args, kw)
    802
    803 -    def TryRun(self, *args, **kw):
    804          return apply(self.sconf.TryRun, args, kw)
    805
    806 -    def __getattr__( self, attr ):
    807          if( attr == 'env' ):
    808              return self.sconf.env
    809          elif( attr == 'lastTarget' ):
    810              return self.sconf.lastTarget
    811          else:
    812              raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
    813
    814      #### Stuff used by Conftest.py (look there for explanations).
    815
    816 -    def BuildProg(self, text, ext):
    817          self.sconf.cached = 1
    818          # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
    819          return not self.TryBuild(self.env.Program, text, ext)
    820
    821 -    def CompileProg(self, text, ext):
    822          self.sconf.cached = 1
    823          # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
    824          return not self.TryBuild(self.env.Object, text, ext)
    825
    826 -    def RunProg(self, text, ext):
    827          self.sconf.cached = 1
    828          # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
    829          st, out = self.TryRun(text, ext)
    830          return not st, out
    831
    832 -    def AppendLIBS(self, lib_name_list):
    833          oldLIBS = self.env.get( 'LIBS', [] )
    834          self.env.Append(LIBS = lib_name_list)
    835          return oldLIBS
    836
    837 -    def SetLIBS(self, val):
    838          oldLIBS = self.env.get( 'LIBS', [] )
    839          self.env.Replace(LIBS = val)
    840          return oldLIBS
    841
    842 -    def Display(self, msg):
    843          if self.sconf.cached:
    844              # We assume that Display is called twice for each test here
    845              # once for the Checking for ... message and once for the result.
    846              # The self.sconf.cached flag can only be set between those calls
    847              msg = "(cached) " + msg
    848              self.sconf.cached = 0
    849          progress_display(msg, append_newline=0)
    850          self.Log("scons: Configure: " + msg + "\n")
    851
    852 -    def Log(self, msg):
    853          if self.sconf.logstream != None:
    854              self.sconf.logstream.write(msg)
    855
    856      #### End of stuff used by Conftest.py.
    857
    858
    859 -def SConf(*args, **kw):
    860      if kw.get(build_type, True):
    861          kw['_depth'] = kw.get('_depth', 0) + 1
    862          for bt in build_types:
    863              try:
    864                  del kw[bt]
    865              except KeyError:
    866                  pass
    867          return apply(SConfBase, args, kw)
    868      else:
    869          return SCons.Util.Null()
    870
    871
    872 -def CheckFunc(context, function_name, header = None, language = None):
    873      res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language)
    874      context.did_show_result = 1
    875      return not res
    876
    877 -def CheckType(context, type_name, includes = "", language = None):
    878      res = SCons.Conftest.CheckType(context, type_name,
    879                                          header = includes, language = language)
    880      context.did_show_result = 1
    881      return not res
    882
    883 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
    884      res = SCons.Conftest.CheckTypeSize(context, type_name,
    885                                         header = includes, language = language,
    886                                         expect = expect)
    887      context.did_show_result = 1
    888      return res
    889
    890 -def CheckDeclaration(context, declaration, includes = "", language = None):
    891      res = SCons.Conftest.CheckDeclaration(context, declaration,
    892                                            includes = includes,
    893                                            language = language)
    894      context.did_show_result = 1
    895      return not res
    896
    897 -def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
    898      # used by CheckHeader and CheckLibWithHeader to produce C - #include
    899      # statements from the specified header (list)
    900      if not SCons.Util.is_List(headers):
    901          headers = [headers]
    902      l = []
    903      if leaveLast:
    904          lastHeader = headers[-1]
    905          headers = headers[:-1]
    906      else:
    907          lastHeader = None
    908      for s in headers:
    909          l.append("#include %s%s%s\n"
    910                   % (include_quotes[0], s, include_quotes[1]))
    911      return string.join(l, ''), lastHeader
    912
    913 -def CheckHeader(context, header, include_quotes = '<>', language = None):
    914      """
    915      A test for a C or C++ header file.
    916      """
    917      prog_prefix, hdr_to_check = \
    918                   createIncludesFromHeaders(header, 1, include_quotes)
    919      res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
    920                                       language = language,
    921                                       include_quotes = include_quotes)
    922      context.did_show_result = 1
    923      return not res
    924
    925  # Bram: Make this function obsolete?  CheckHeader() is more generic.
    926
    927 -def CheckCHeader(context, header, include_quotes = '""'):
    928      """
    929      A test for a C header file.
    930      """
    931      return CheckHeader(context, header, include_quotes, language = "C")
    932
    933
    934  # Bram: Make this function obsolete?  CheckHeader() is more generic.
    935
    936 -def CheckCXXHeader(context, header, include_quotes = '""'):
    937      """
    938      A test for a C++ header file.
    939      """
    940      return CheckHeader(context, header, include_quotes, language = "C++")
    941
    942
    943 -def CheckLib(context, library = None, symbol = "main",
    944               header = None, language = None, autoadd = 1):
    945      """
    946      A test for a library. See also CheckLibWithHeader.
    947      Note that library may also be None to test whether the given symbol
    948      compiles without flags.
    949      """
    950
    951      if library == []:
    952          library = [None]
    953
    954      if not SCons.Util.is_List(library):
    955          library = [library]
    956
    957      # ToDo: accept path for the library
    958      res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
    959                                          language = language, autoadd = autoadd)
    960      context.did_show_result = 1
    961      return not res
    962
    963  # XXX
    964  # Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H.
    965
    966 -def CheckLibWithHeader(context, libs, header, language,
    967                         call = None, autoadd = 1):
    968      # ToDo: accept path for library. Support system header files.
    969      """
    970      Another (more sophisticated) test for a library.
    971      Checks, if library and header is available for language (may be 'C'
    972      or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
    973      As in CheckLib, we support library=None, to test if the call compiles
    974      without extra link flags.
    975      """
    976      prog_prefix, dummy = \
    977                   createIncludesFromHeaders(header, 0)
    978      if libs == []:
    979          libs = [None]
    980
    981      if not SCons.Util.is_List(libs):
    982          libs = [libs]
    983
    984      res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
    985              call = call, language = language, autoadd = autoadd)
    986      context.did_show_result = 1
    987      return not res
    988