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