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