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