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