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 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):
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 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 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
1013