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, 2009, 2010 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 4720 2010/03/24 03:14:11 jars" 
  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.AlwaysTask):
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 if cache_mode == FORCE: 318 # Set up the Decider() to force rebuilds by saying 319 # that every source has changed. Note that we still 320 # call the environment's underlying source decider so 321 # that the correct .sconsign info will get calculated 322 # and keep the build state consistent. 323 def force_build(dependency, target, prev_ni, 324 env_decider=env.decide_source): 325 env_decider(dependency, target, prev_ni) 326 return True
327 if env.decide_source.func_code is not force_build.func_code: 328 env.Decider(force_build) 329 env['PSTDOUT'] = env['PSTDERR'] = s 330 try: 331 sconf.cached = 0 332 self.targets[0].build() 333 finally: 334 sys.stdout = sys.stderr = env['PSTDOUT'] = \ 335 env['PSTDERR'] = sconf.logstream 336 except KeyboardInterrupt: 337 raise 338 except SystemExit: 339 exc_value = sys.exc_info()[1] 340 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code) 341 except Exception, e: 342 for t in self.targets: 343 binfo = t.get_binfo() 344 binfo.__class__ = SConfBuildInfo 345 binfo.set_build_result(1, s.getvalue()) 346 sconsign_entry = SCons.SConsign.SConsignEntry() 347 sconsign_entry.binfo = binfo 348 #sconsign_entry.ninfo = self.get_ninfo() 349 # We'd like to do this as follows: 350 # t.store_info(binfo) 351 # However, we need to store it as an SConfBuildInfo 352 # object, and store_info() will turn it into a 353 # regular FileNodeInfo if the target is itself a 354 # regular File. 355 sconsign = t.dir.sconsign() 356 sconsign.set_entry(t.name, sconsign_entry) 357 sconsign.merge() 358 raise e 359 else: 360 for t in self.targets: 361 binfo = t.get_binfo() 362 binfo.__class__ = SConfBuildInfo 363 binfo.set_build_result(0, s.getvalue()) 364 sconsign_entry = SCons.SConsign.SConsignEntry() 365 sconsign_entry.binfo = binfo 366 #sconsign_entry.ninfo = self.get_ninfo() 367 # We'd like to do this as follows: 368 # t.store_info(binfo) 369 # However, we need to store it as an SConfBuildInfo 370 # object, and store_info() will turn it into a 371 # regular FileNodeInfo if the target is itself a 372 # regular File. 373 sconsign = t.dir.sconsign() 374 sconsign.set_entry(t.name, sconsign_entry) 375 sconsign.merge() 376
377 -class SConfBase:
378 """This is simply a class to represent a configure context. After 379 creating a SConf object, you can call any tests. After finished with your 380 tests, be sure to call the Finish() method, which returns the modified 381 environment. 382 Some words about caching: In most cases, it is not necessary to cache 383 Test results explicitely. Instead, we use the scons dependency checking 384 mechanism. For example, if one wants to compile a test program 385 (SConf.TryLink), the compiler is only called, if the program dependencies 386 have changed. However, if the program could not be compiled in a former 387 SConf run, we need to explicitely cache this error. 388 """ 389
390 - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR', 391 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
392 """Constructor. Pass additional tests in the custom_tests-dictinary, 393 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest 394 defines a custom test. 395 Note also the conf_dir and log_file arguments (you may want to 396 build tests in the VariantDir, not in the SourceDir) 397 """ 398 global SConfFS 399 if not SConfFS: 400 SConfFS = SCons.Node.FS.default_fs or \ 401 SCons.Node.FS.FS(env.fs.pathTop) 402 if sconf_global is not None: 403 raise (SCons.Errors.UserError, 404 "Only one SConf object may be active at one time") 405 self.env = env 406 if log_file is not None: 407 log_file = SConfFS.File(env.subst(log_file)) 408 self.logfile = log_file 409 self.logstream = None 410 self.lastTarget = None 411 self.depth = _depth 412 self.cached = 0 # will be set, if all test results are cached 413 414 # add default tests 415 default_tests = { 416 'CheckCC' : CheckCC, 417 'CheckCXX' : CheckCXX, 418 'CheckSHCC' : CheckSHCC, 419 'CheckSHCXX' : CheckSHCXX, 420 'CheckFunc' : CheckFunc, 421 'CheckType' : CheckType, 422 'CheckTypeSize' : CheckTypeSize, 423 'CheckDeclaration' : CheckDeclaration, 424 'CheckHeader' : CheckHeader, 425 'CheckCHeader' : CheckCHeader, 426 'CheckCXXHeader' : CheckCXXHeader, 427 'CheckLib' : CheckLib, 428 'CheckLibWithHeader' : CheckLibWithHeader, 429 } 430 self.AddTests(default_tests) 431 self.AddTests(custom_tests) 432 self.confdir = SConfFS.Dir(env.subst(conf_dir)) 433 if config_h is not None: 434 config_h = SConfFS.File(config_h) 435 self.config_h = config_h 436 self._startup()
437
438 - def Finish(self):
439 """Call this method after finished with your tests: 440 env = sconf.Finish() 441 """ 442 self._shutdown() 443 return self.env
444
445 - def Define(self, name, value = None, comment = None):
446 """ 447 Define a pre processor symbol name, with the optional given value in the 448 current config header. 449 450 If value is None (default), then #define name is written. If value is not 451 none, then #define name value is written. 452 453 comment is a string which will be put as a C comment in the 454 header, to explain the meaning of the value (appropriate C comments /* and 455 */ will be put automatically.""" 456 lines = [] 457 if comment: 458 comment_str = "/* %s */" % comment 459 lines.append(comment_str) 460 461 if value is not None: 462 define_str = "#define %s %s" % (name, value) 463 else: 464 define_str = "#define %s" % name 465 lines.append(define_str) 466 lines.append('') 467 468 self.config_h_text = self.config_h_text + string.join(lines, '\n')
469
470 - def BuildNodes(self, nodes):
471 """ 472 Tries to build the given nodes immediately. Returns 1 on success, 473 0 on error. 474 """ 475 if self.logstream is not None: 476 # override stdout / stderr to write in log file 477 oldStdout = sys.stdout 478 sys.stdout = self.logstream 479 oldStderr = sys.stderr 480 sys.stderr = self.logstream 481 482 # the engine assumes the current path is the SConstruct directory ... 483 old_fs_dir = SConfFS.getcwd() 484 old_os_dir = os.getcwd() 485 SConfFS.chdir(SConfFS.Top, change_os_dir=1) 486 487 # Because we take responsibility here for writing out our 488 # own .sconsign info (see SConfBuildTask.execute(), above), 489 # we override the store_info() method with a null place-holder 490 # so we really control how it gets written. 491 for n in nodes: 492 n.store_info = n.do_not_store_info 493 494 ret = 1 495 496 try: 497 # ToDo: use user options for calc 498 save_max_drift = SConfFS.get_max_drift() 499 SConfFS.set_max_drift(0) 500 tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask) 501 # we don't want to build tests in parallel 502 jobs = SCons.Job.Jobs(1, tm ) 503 jobs.run() 504 for n in nodes: 505 state = n.get_state() 506 if (state != SCons.Node.executed and 507 state != SCons.Node.up_to_date): 508 # the node could not be built. we return 0 in this case 509 ret = 0 510 finally: 511 SConfFS.set_max_drift(save_max_drift) 512 os.chdir(old_os_dir) 513 SConfFS.chdir(old_fs_dir, change_os_dir=0) 514 if self.logstream is not None: 515 # restore stdout / stderr 516 sys.stdout = oldStdout 517 sys.stderr = oldStderr 518 return ret
519
520 - def pspawn_wrapper(self, sh, escape, cmd, args, env):
521 """Wrapper function for handling piped spawns. 522 523 This looks to the calling interface (in Action.py) like a "normal" 524 spawn, but associates the call with the PSPAWN variable from 525 the construction environment and with the streams to which we 526 want the output logged. This gets slid into the construction 527 environment as the SPAWN variable so Action.py doesn't have to 528 know or care whether it's spawning a piped command or not. 529 """ 530 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
531 532
533 - def TryBuild(self, builder, text = None, extension = ""):
534 """Low level TryBuild implementation. Normally you don't need to 535 call that - you can use TryCompile / TryLink / TryRun instead 536 """ 537 global _ac_build_counter 538 539 # Make sure we have a PSPAWN value, and save the current 540 # SPAWN value. 541 try: 542 self.pspawn = self.env['PSPAWN'] 543 except KeyError: 544 raise SCons.Errors.UserError('Missing PSPAWN construction variable.') 545 try: 546 save_spawn = self.env['SPAWN'] 547 except KeyError: 548 raise SCons.Errors.UserError('Missing SPAWN construction variable.') 549 550 nodesToBeBuilt = [] 551 552 f = "conftest_" + str(_ac_build_counter) 553 pref = self.env.subst( builder.builder.prefix ) 554 suff = self.env.subst( builder.builder.suffix ) 555 target = self.confdir.File(pref + f + suff) 556 557 try: 558 # Slide our wrapper into the construction environment as 559 # the SPAWN function. 560 self.env['SPAWN'] = self.pspawn_wrapper 561 sourcetext = self.env.Value(text) 562 563 if text is not None: 564 textFile = self.confdir.File(f + extension) 565 textFileNode = self.env.SConfSourceBuilder(target=textFile, 566 source=sourcetext) 567 nodesToBeBuilt.extend(textFileNode) 568 source = textFileNode 569 else: 570 source = None 571 572 nodes = builder(target = target, source = source) 573 if not SCons.Util.is_List(nodes): 574 nodes = [nodes] 575 nodesToBeBuilt.extend(nodes) 576 result = self.BuildNodes(nodesToBeBuilt) 577 578 finally: 579 self.env['SPAWN'] = save_spawn 580 581 _ac_build_counter = _ac_build_counter + 1 582 if result: 583 self.lastTarget = nodes[0] 584 else: 585 self.lastTarget = None 586 587 return result
588
589 - def TryAction(self, action, text = None, extension = ""):
590 """Tries to execute the given action with optional source file 591 contents <text> and optional source file extension <extension>, 592 Returns the status (0 : failed, 1 : ok) and the contents of the 593 output file. 594 """ 595 builder = SCons.Builder.Builder(action=action) 596 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) 597 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) 598 del self.env['BUILDERS']['SConfActionBuilder'] 599 if ok: 600 outputStr = self.lastTarget.get_contents() 601 return (1, outputStr) 602 return (0, "")
603
604 - def TryCompile( self, text, extension):
605 """Compiles the program given in text to an env.Object, using extension 606 as file extension (e.g. '.c'). Returns 1, if compilation was 607 successful, 0 otherwise. The target is saved in self.lastTarget (for 608 further processing). 609 """ 610 return self.TryBuild(self.env.Object, text, extension)
611 619
620 - def TryRun(self, text, extension ):
621 """Compiles and runs the program given in text, using extension 622 as file extension (e.g. '.c'). Returns (1, outputStr) on success, 623 (0, '') otherwise. The target (a file containing the program's stdout) 624 is saved in self.lastTarget (for further processing). 625 """ 626 ok = self.TryLink(text, extension) 627 if( ok ): 628 prog = self.lastTarget 629 pname = prog.path 630 output = self.confdir.File(os.path.basename(pname)+'.out') 631 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ]) 632 ok = self.BuildNodes(node) 633 if ok: 634 outputStr = output.get_contents() 635 return( 1, outputStr) 636 return (0, "")
637
638 - class TestWrapper:
639 """A wrapper around Tests (to ensure sanity)"""
640 - def __init__(self, test, sconf):
641 self.test = test 642 self.sconf = sconf
643 - def __call__(self, *args, **kw):
644 if not self.sconf.active: 645 raise (SCons.Errors.UserError, 646 "Test called after sconf.Finish()") 647 context = CheckContext(self.sconf) 648 ret = apply(self.test, (context,) + args, kw) 649 if self.sconf.config_h is not None: 650 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h 651 context.Result("error: no result") 652 return ret
653
654 - def AddTest(self, test_name, test_instance):
655 """Adds test_class to this SConf instance. It can be called with 656 self.test_name(...)""" 657 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
658
659 - def AddTests(self, tests):
660 """Adds all the tests given in the tests dictionary to this SConf 661 instance 662 """ 663 for name in tests.keys(): 664 self.AddTest(name, tests[name])
665
666 - def _createDir( self, node ):
667 dirName = str(node) 668 if dryrun: 669 if not os.path.isdir( dirName ): 670 raise ConfigureDryRunError(dirName) 671 else: 672 if not os.path.isdir( dirName ): 673 os.makedirs( dirName ) 674 node._exists = 1
675
676 - def _startup(self):
677 """Private method. Set up logstream, and set the environment 678 variables necessary for a piped build 679 """ 680 global _ac_config_logs 681 global sconf_global 682 global SConfFS 683 684 self.lastEnvFs = self.env.fs 685 self.env.fs = SConfFS 686 self._createDir(self.confdir) 687 self.confdir.up().add_ignore( [self.confdir] ) 688 689 if self.logfile is not None and not dryrun: 690 # truncate logfile, if SConf.Configure is called for the first time 691 # in a build 692 if _ac_config_logs.has_key(self.logfile): 693 log_mode = "a" 694 else: 695 _ac_config_logs[self.logfile] = None 696 log_mode = "w" 697 fp = open(str(self.logfile), log_mode) 698 self.logstream = SCons.Util.Unbuffered(fp) 699 # logfile may stay in a build directory, so we tell 700 # the build system not to override it with a eventually 701 # existing file with the same name in the source directory 702 self.logfile.dir.add_ignore( [self.logfile] ) 703 704 tb = traceback.extract_stack()[-3-self.depth] 705 old_fs_dir = SConfFS.getcwd() 706 SConfFS.chdir(SConfFS.Top, change_os_dir=0) 707 self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % 708 (tb[0], tb[1], str(self.confdir)) ) 709 SConfFS.chdir(old_fs_dir) 710 else: 711 self.logstream = None 712 # we use a special builder to create source files from TEXT 713 action = SCons.Action.Action(_createSource, 714 _stringSource) 715 sconfSrcBld = SCons.Builder.Builder(action=action) 716 self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) 717 self.config_h_text = _ac_config_hs.get(self.config_h, "") 718 self.active = 1 719 # only one SConf instance should be active at a time ... 720 sconf_global = self
721
722 - def _shutdown(self):
723 """Private method. Reset to non-piped spawn""" 724 global sconf_global, _ac_config_hs 725 726 if not self.active: 727 raise SCons.Errors.UserError, "Finish may be called only once!" 728 if self.logstream is not None and not dryrun: 729 self.logstream.write("\n") 730 self.logstream.close() 731 self.logstream = None 732 # remove the SConfSourceBuilder from the environment 733 blds = self.env['BUILDERS'] 734 del blds['SConfSourceBuilder'] 735 self.env.Replace( BUILDERS=blds ) 736 self.active = 0 737 sconf_global = None 738 if not self.config_h is None: 739 _ac_config_hs[self.config_h] = self.config_h_text 740 self.env.fs = self.lastEnvFs
741
742 -class CheckContext:
743 """Provides a context for configure tests. Defines how a test writes to the 744 screen and log file. 745 746 A typical test is just a callable with an instance of CheckContext as 747 first argument: 748 749 def CheckCustom(context, ...) 750 context.Message('Checking my weird test ... ') 751 ret = myWeirdTestFunction(...) 752 context.Result(ret) 753 754 Often, myWeirdTestFunction will be one of 755 context.TryCompile/context.TryLink/context.TryRun. The results of 756 those are cached, for they are only rebuild, if the dependencies have 757 changed. 758 """ 759
760 - def __init__(self, sconf):
761 """Constructor. Pass the corresponding SConf instance.""" 762 self.sconf = sconf 763 self.did_show_result = 0 764 765 # for Conftest.py: 766 self.vardict = {} 767 self.havedict = {} 768 self.headerfilename = None 769 self.config_h = "" # config_h text will be stored here
770 # we don't regenerate the config.h file after each test. That means, 771 # that tests won't be able to include the config.h file, and so 772 # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major 773 # issue, though. If it turns out, that we need to include config.h 774 # in tests, we must ensure, that the dependencies are worked out 775 # correctly. Note that we can't use Conftest.py's support for config.h, 776 # cause we will need to specify a builder for the config.h file ... 777
778 - def Message(self, text):
779 """Inform about what we are doing right now, e.g. 780 'Checking for SOMETHING ... ' 781 """ 782 self.Display(text) 783 self.sconf.cached = 1 784 self.did_show_result = 0
785
786 - def Result(self, res):
787 """Inform about the result of the test. res may be an integer or a 788 string. In case of an integer, the written text will be 'yes' or 'no'. 789 The result is only displayed when self.did_show_result is not set. 790 """ 791 if type(res) in BooleanTypes: 792 if res: 793 text = "yes" 794 else: 795 text = "no" 796 elif type(res) == types.StringType: 797 text = res 798 else: 799 raise TypeError, "Expected string, int or bool, got " + str(type(res)) 800 801 if self.did_show_result == 0: 802 # Didn't show result yet, do it now. 803 self.Display(text + "\n") 804 self.did_show_result = 1
805
806 - def TryBuild(self, *args, **kw):
807 return apply(self.sconf.TryBuild, args, kw)
808
809 - def TryAction(self, *args, **kw):
810 return apply(self.sconf.TryAction, args, kw)
811
812 - def TryCompile(self, *args, **kw):
813 return apply(self.sconf.TryCompile, args, kw)
814 817
818 - def TryRun(self, *args, **kw):
819 return apply(self.sconf.TryRun, args, kw)
820
821 - def __getattr__( self, attr ):
822 if( attr == 'env' ): 823 return self.sconf.env 824 elif( attr == 'lastTarget' ): 825 return self.sconf.lastTarget 826 else: 827 raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
828 829 #### Stuff used by Conftest.py (look there for explanations). 830
831 - def BuildProg(self, text, ext):
832 self.sconf.cached = 1 833 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. 834 return not self.TryBuild(self.env.Program, text, ext)
835
836 - def CompileProg(self, text, ext):
837 self.sconf.cached = 1 838 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. 839 return not self.TryBuild(self.env.Object, text, ext)
840
841 - def CompileSharedObject(self, text, ext):
842 self.sconf.cached = 1 843 # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc. 844 return not self.TryBuild(self.env.SharedObject, text, ext)
845
846 - def RunProg(self, text, ext):
847 self.sconf.cached = 1 848 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. 849 st, out = self.TryRun(text, ext) 850 return not st, out
851
852 - def AppendLIBS(self, lib_name_list):
853 oldLIBS = self.env.get( 'LIBS', [] ) 854 self.env.Append(LIBS = lib_name_list) 855 return oldLIBS
856
857 - def PrependLIBS(self, lib_name_list):
858 oldLIBS = self.env.get( 'LIBS', [] ) 859 self.env.Prepend(LIBS = lib_name_list) 860 return oldLIBS
861
862 - def SetLIBS(self, val):
863 oldLIBS = self.env.get( 'LIBS', [] ) 864 self.env.Replace(LIBS = val) 865 return oldLIBS
866
867 - def Display(self, msg):
868 if self.sconf.cached: 869 # We assume that Display is called twice for each test here 870 # once for the Checking for ... message and once for the result. 871 # The self.sconf.cached flag can only be set between those calls 872 msg = "(cached) " + msg 873 self.sconf.cached = 0 874 progress_display(msg, append_newline=0) 875 self.Log("scons: Configure: " + msg + "\n")
876
877 - def Log(self, msg):
878 if self.sconf.logstream is not None: 879 self.sconf.logstream.write(msg)
880 881 #### End of stuff used by Conftest.py. 882 883
884 -def SConf(*args, **kw):
885 if kw.get(build_type, True): 886 kw['_depth'] = kw.get('_depth', 0) + 1 887 for bt in build_types: 888 try: 889 del kw[bt] 890 except KeyError: 891 pass 892 return apply(SConfBase, args, kw) 893 else: 894 return SCons.Util.Null()
895 896
897 -def CheckFunc(context, function_name, header = None, language = None):
898 res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language) 899 context.did_show_result = 1 900 return not res
901
902 -def CheckType(context, type_name, includes = "", language = None):
903 res = SCons.Conftest.CheckType(context, type_name, 904 header = includes, language = language) 905 context.did_show_result = 1 906 return not res
907
908 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
909 res = SCons.Conftest.CheckTypeSize(context, type_name, 910 header = includes, language = language, 911 expect = expect) 912 context.did_show_result = 1 913 return res
914
915 -def CheckDeclaration(context, declaration, includes = "", language = None):
916 res = SCons.Conftest.CheckDeclaration(context, declaration, 917 includes = includes, 918 language = language) 919 context.did_show_result = 1 920 return not res
921
922 -def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
923 # used by CheckHeader and CheckLibWithHeader to produce C - #include 924 # statements from the specified header (list) 925 if not SCons.Util.is_List(headers): 926 headers = [headers] 927 l = [] 928 if leaveLast: 929 lastHeader = headers[-1] 930 headers = headers[:-1] 931 else: 932 lastHeader = None 933 for s in headers: 934 l.append("#include %s%s%s\n" 935 % (include_quotes[0], s, include_quotes[1])) 936 return string.join(l, ''), lastHeader
937
938 -def CheckHeader(context, header, include_quotes = '<>', language = None):
939 """ 940 A test for a C or C++ header file. 941 """ 942 prog_prefix, hdr_to_check = \ 943 createIncludesFromHeaders(header, 1, include_quotes) 944 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix, 945 language = language, 946 include_quotes = include_quotes) 947 context.did_show_result = 1 948 return not res
949
950 -def CheckCC(context):
951 res = SCons.Conftest.CheckCC(context) 952 context.did_show_result = 1 953 return not res
954
955 -def CheckCXX(context):
956 res = SCons.Conftest.CheckCXX(context) 957 context.did_show_result = 1 958 return not res
959
960 -def CheckSHCC(context):
961 res = SCons.Conftest.CheckSHCC(context) 962 context.did_show_result = 1 963 return not res
964
965 -def CheckSHCXX(context):
966 res = SCons.Conftest.CheckSHCXX(context) 967 context.did_show_result = 1 968 return not res
969 970 # Bram: Make this function obsolete? CheckHeader() is more generic. 971
972 -def CheckCHeader(context, header, include_quotes = '""'):
973 """ 974 A test for a C header file. 975 """ 976 return CheckHeader(context, header, include_quotes, language = "C")
977 978 979 # Bram: Make this function obsolete? CheckHeader() is more generic. 980
981 -def CheckCXXHeader(context, header, include_quotes = '""'):
982 """ 983 A test for a C++ header file. 984 """ 985 return CheckHeader(context, header, include_quotes, language = "C++")
986 987
988 -def CheckLib(context, library = None, symbol = "main", 989 header = None, language = None, autoadd = 1):
990 """ 991 A test for a library. See also CheckLibWithHeader. 992 Note that library may also be None to test whether the given symbol 993 compiles without flags. 994 """ 995 996 if library == []: 997 library = [None] 998 999 if not SCons.Util.is_List(library): 1000 library = [library] 1001 1002 # ToDo: accept path for the library 1003 res = SCons.Conftest.CheckLib(context, library, symbol, header = header, 1004 language = language, autoadd = autoadd) 1005 context.did_show_result = 1 1006 return not res
1007 1008 # XXX 1009 # Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H. 1010
1011 -def CheckLibWithHeader(context, libs, header, language, 1012 call = None, autoadd = 1):
1013 # ToDo: accept path for library. Support system header files. 1014 """ 1015 Another (more sophisticated) test for a library. 1016 Checks, if library and header is available for language (may be 'C' 1017 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'. 1018 As in CheckLib, we support library=None, to test if the call compiles 1019 without extra link flags. 1020 """ 1021 prog_prefix, dummy = \ 1022 createIncludesFromHeaders(header, 0) 1023 if libs == []: 1024 libs = [None] 1025 1026 if not SCons.Util.is_List(libs): 1027 libs = [libs] 1028 1029 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix, 1030 call = call, language = language, autoadd = autoadd) 1031 context.did_show_result = 1 1032 return not res
1033 1034 # Local Variables: 1035 # tab-width:4 1036 # indent-tabs-mode:nil 1037 # End: 1038 # vim: set expandtab tabstop=4 shiftwidth=4: 1039