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