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, 2011, 2012, 2013, 2014 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  2014/03/02 14:18:15 garyo" 
  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 if not hasattr(n, 'attributes'): 487 n.attributes = SCons.Node.Node.Attrs() 488 n.attributes.keep_targetinfo = 1 489 490 ret = 1 491 492 try: 493 # ToDo: use user options for calc 494 save_max_drift = SConfFS.get_max_drift() 495 SConfFS.set_max_drift(0) 496 tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask) 497 # we don't want to build tests in parallel 498 jobs = SCons.Job.Jobs(1, tm ) 499 jobs.run() 500 for n in nodes: 501 state = n.get_state() 502 if (state != SCons.Node.executed and 503 state != SCons.Node.up_to_date): 504 # the node could not be built. we return 0 in this case 505 ret = 0 506 finally: 507 SConfFS.set_max_drift(save_max_drift) 508 os.chdir(old_os_dir) 509 SConfFS.chdir(old_fs_dir, change_os_dir=0) 510 if self.logstream is not None: 511 # restore stdout / stderr 512 sys.stdout = oldStdout 513 sys.stderr = oldStderr 514 return ret
515
516 - def pspawn_wrapper(self, sh, escape, cmd, args, env):
517 """Wrapper function for handling piped spawns. 518 519 This looks to the calling interface (in Action.py) like a "normal" 520 spawn, but associates the call with the PSPAWN variable from 521 the construction environment and with the streams to which we 522 want the output logged. This gets slid into the construction 523 environment as the SPAWN variable so Action.py doesn't have to 524 know or care whether it's spawning a piped command or not. 525 """ 526 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
527 528
529 - def TryBuild(self, builder, text = None, extension = ""):
530 """Low level TryBuild implementation. Normally you don't need to 531 call that - you can use TryCompile / TryLink / TryRun instead 532 """ 533 global _ac_build_counter 534 535 # Make sure we have a PSPAWN value, and save the current 536 # SPAWN value. 537 try: 538 self.pspawn = self.env['PSPAWN'] 539 except KeyError: 540 raise SCons.Errors.UserError('Missing PSPAWN construction variable.') 541 try: 542 save_spawn = self.env['SPAWN'] 543 except KeyError: 544 raise SCons.Errors.UserError('Missing SPAWN construction variable.') 545 546 nodesToBeBuilt = [] 547 548 f = "conftest_" + str(_ac_build_counter) 549 pref = self.env.subst( builder.builder.prefix ) 550 suff = self.env.subst( builder.builder.suffix ) 551 target = self.confdir.File(pref + f + suff) 552 553 try: 554 # Slide our wrapper into the construction environment as 555 # the SPAWN function. 556 self.env['SPAWN'] = self.pspawn_wrapper 557 sourcetext = self.env.Value(text) 558 559 if text is not None: 560 textFile = self.confdir.File(f + extension) 561 textFileNode = self.env.SConfSourceBuilder(target=textFile, 562 source=sourcetext) 563 nodesToBeBuilt.extend(textFileNode) 564 source = textFileNode 565 else: 566 source = None 567 568 nodes = builder(target = target, source = source) 569 if not SCons.Util.is_List(nodes): 570 nodes = [nodes] 571 nodesToBeBuilt.extend(nodes) 572 result = self.BuildNodes(nodesToBeBuilt) 573 574 finally: 575 self.env['SPAWN'] = save_spawn 576 577 _ac_build_counter = _ac_build_counter + 1 578 if result: 579 self.lastTarget = nodes[0] 580 else: 581 self.lastTarget = None 582 583 return result
584
585 - def TryAction(self, action, text = None, extension = ""):
586 """Tries to execute the given action with optional source file 587 contents <text> and optional source file extension <extension>, 588 Returns the status (0 : failed, 1 : ok) and the contents of the 589 output file. 590 """ 591 builder = SCons.Builder.Builder(action=action) 592 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) 593 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) 594 del self.env['BUILDERS']['SConfActionBuilder'] 595 if ok: 596 outputStr = self.lastTarget.get_contents() 597 return (1, outputStr) 598 return (0, "")
599
600 - def TryCompile( self, text, extension):
601 """Compiles the program given in text to an env.Object, using extension 602 as file extension (e.g. '.c'). Returns 1, if compilation was 603 successful, 0 otherwise. The target is saved in self.lastTarget (for 604 further processing). 605 """ 606 return self.TryBuild(self.env.Object, text, extension)
607 615
616 - def TryRun(self, text, extension ):
617 """Compiles and runs the program given in text, using extension 618 as file extension (e.g. '.c'). Returns (1, outputStr) on success, 619 (0, '') otherwise. The target (a file containing the program's stdout) 620 is saved in self.lastTarget (for further processing). 621 """ 622 ok = self.TryLink(text, extension) 623 if( ok ): 624 prog = self.lastTarget 625 pname = prog.path 626 output = self.confdir.File(os.path.basename(pname)+'.out') 627 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ]) 628 ok = self.BuildNodes(node) 629 if ok: 630 outputStr = output.get_contents() 631 return( 1, outputStr) 632 return (0, "")
633
634 - class TestWrapper(object):
635 """A wrapper around Tests (to ensure sanity)"""
636 - def __init__(self, test, sconf):
637 self.test = test 638 self.sconf = sconf
639 - def __call__(self, *args, **kw):
640 if not self.sconf.active: 641 raise SCons.Errors.UserError 642 context = CheckContext(self.sconf) 643 ret = self.test(context, *args, **kw) 644 if self.sconf.config_h is not None: 645 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h 646 context.Result("error: no result") 647 return ret
648
649 - def AddTest(self, test_name, test_instance):
650 """Adds test_class to this SConf instance. It can be called with 651 self.test_name(...)""" 652 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
653
654 - def AddTests(self, tests):
655 """Adds all the tests given in the tests dictionary to this SConf 656 instance 657 """ 658 for name in tests.keys(): 659 self.AddTest(name, tests[name])
660
661 - def _createDir( self, node ):
662 dirName = str(node) 663 if dryrun: 664 if not os.path.isdir( dirName ): 665 raise ConfigureDryRunError(dirName) 666 else: 667 if not os.path.isdir( dirName ): 668 os.makedirs( dirName ) 669 node._exists = 1
670
671 - def _startup(self):
672 """Private method. Set up logstream, and set the environment 673 variables necessary for a piped build 674 """ 675 global _ac_config_logs 676 global sconf_global 677 global SConfFS 678 679 self.lastEnvFs = self.env.fs 680 self.env.fs = SConfFS 681 self._createDir(self.confdir) 682 self.confdir.up().add_ignore( [self.confdir] ) 683 684 if self.logfile is not None and not dryrun: 685 # truncate logfile, if SConf.Configure is called for the first time 686 # in a build 687 if self.logfile in _ac_config_logs: 688 log_mode = "a" 689 else: 690 _ac_config_logs[self.logfile] = None 691 log_mode = "w" 692 fp = open(str(self.logfile), log_mode) 693 self.logstream = SCons.Util.Unbuffered(fp) 694 # logfile may stay in a build directory, so we tell 695 # the build system not to override it with a eventually 696 # existing file with the same name in the source directory 697 self.logfile.dir.add_ignore( [self.logfile] ) 698 699 tb = traceback.extract_stack()[-3-self.depth] 700 old_fs_dir = SConfFS.getcwd() 701 SConfFS.chdir(SConfFS.Top, change_os_dir=0) 702 self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % 703 (tb[0], tb[1], str(self.confdir)) ) 704 SConfFS.chdir(old_fs_dir) 705 else: 706 self.logstream = None 707 # we use a special builder to create source files from TEXT 708 action = SCons.Action.Action(_createSource, 709 _stringSource) 710 sconfSrcBld = SCons.Builder.Builder(action=action) 711 self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) 712 self.config_h_text = _ac_config_hs.get(self.config_h, "") 713 self.active = 1 714 # only one SConf instance should be active at a time ... 715 sconf_global = self
716
717 - def _shutdown(self):
718 """Private method. Reset to non-piped spawn""" 719 global sconf_global, _ac_config_hs 720 721 if not self.active: 722 raise SCons.Errors.UserError("Finish may be called only once!") 723 if self.logstream is not None and not dryrun: 724 self.logstream.write("\n") 725 self.logstream.close() 726 self.logstream = None 727 # remove the SConfSourceBuilder from the environment 728 blds = self.env['BUILDERS'] 729 del blds['SConfSourceBuilder'] 730 self.env.Replace( BUILDERS=blds ) 731 self.active = 0 732 sconf_global = None 733 if not self.config_h is None: 734 _ac_config_hs[self.config_h] = self.config_h_text 735 self.env.fs = self.lastEnvFs
736
737 -class CheckContext(object):
738 """Provides a context for configure tests. Defines how a test writes to the 739 screen and log file. 740 741 A typical test is just a callable with an instance of CheckContext as 742 first argument: 743 744 def CheckCustom(context, ...) 745 context.Message('Checking my weird test ... ') 746 ret = myWeirdTestFunction(...) 747 context.Result(ret) 748 749 Often, myWeirdTestFunction will be one of 750 context.TryCompile/context.TryLink/context.TryRun. The results of 751 those are cached, for they are only rebuild, if the dependencies have 752 changed. 753 """ 754
755 - def __init__(self, sconf):
756 """Constructor. Pass the corresponding SConf instance.""" 757 self.sconf = sconf 758 self.did_show_result = 0 759 760 # for Conftest.py: 761 self.vardict = {} 762 self.havedict = {} 763 self.headerfilename = None 764 self.config_h = "" # config_h text will be stored here
765 # we don't regenerate the config.h file after each test. That means, 766 # that tests won't be able to include the config.h file, and so 767 # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major 768 # issue, though. If it turns out, that we need to include config.h 769 # in tests, we must ensure, that the dependencies are worked out 770 # correctly. Note that we can't use Conftest.py's support for config.h, 771 # cause we will need to specify a builder for the config.h file ... 772
773 - def Message(self, text):
774 """Inform about what we are doing right now, e.g. 775 'Checking for SOMETHING ... ' 776 """ 777 self.Display(text) 778 self.sconf.cached = 1 779 self.did_show_result = 0
780
781 - def Result(self, res):
782 """Inform about the result of the test. If res is not a string, displays 783 'yes' or 'no' depending on whether res is evaluated as true or false. 784 The result is only displayed when self.did_show_result is not set. 785 """ 786 if isinstance(res, str): 787 text = res 788 elif res: 789 text = "yes" 790 else: 791 text = "no" 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