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 2928 2008/04/29 22:44:09 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):
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 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 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