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