3 Autoconf-like configuration support.
9 # Permission is hereby granted, free of charge, to any person obtaining
10 # a copy of this software and associated documentation files (the
11 # "Software"), to deal in the Software without restriction, including
12 # without limitation the rights to use, copy, modify, merge, publish,
13 # distribute, sublicense, and/or sell copies of the Software, and to
14 # permit persons to whom the Software is furnished to do so, subject to
15 # the following conditions:
17 # The above copyright notice and this permission notice shall be included
18 # in all copies or substantial portions of the Software.
20 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
21 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
22 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
44 import SCons.Taskmaster
49 from SCons.Debug import Trace
51 # Turn off the Conftest error logging
52 SCons.Conftest.LogInputFiles = 0
53 SCons.Conftest.LogErrorMessages = 0
57 build_types = ['clean', 'help']
59 def SetBuildType(type):
63 # to be set, if we are in dry-run mode
66 AUTO=0 # use SCons dependency scanning for up-to-date checks
67 FORCE=1 # force all tests to be rebuilt
68 CACHE=2 # force all tests to be taken from cache (raise an error, if necessary)
71 def SetCacheMode(mode):
72 """Set the Configure cache mode. mode must be one of "auto", "force",
82 raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
84 progress_display = SCons.Util.display # will be overwritten by SCons.Script
85 def SetProgressDisplay(display):
86 """Set the progress display to use (called from SCons.Script)"""
87 global progress_display
88 progress_display = display
92 _ac_build_counter = 0 # incremented, whenever TryBuild is called
93 _ac_config_logs = {} # all config.log files created in this build
94 _ac_config_hs = {} # all config.h files created in this build
95 sconf_global = None # current sconf object
97 def _createConfigH(target, source, env):
98 t = open(str(target[0]), "w")
99 defname = re.sub('[^A-Za-z0-9_]', '_', string.upper(str(target[0])))
100 t.write("""#ifndef %(DEFNAME)s_SEEN
101 #define %(DEFNAME)s_SEEN
103 """ % {'DEFNAME' : defname})
104 t.write(source[0].get_contents())
106 #endif /* %(DEFNAME)s_SEEN */
107 """ % {'DEFNAME' : defname})
110 def _stringConfigH(target, source, env):
111 return "scons: Configure: creating " + str(target[0])
113 def CreateConfigHBuilder(env):
114 """Called just before the building targets phase begins."""
115 if len(_ac_config_hs) == 0:
117 action = SCons.Action.Action(_createConfigH,
119 sconfigHBld = SCons.Builder.Builder(action=action)
120 env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} )
121 for k in _ac_config_hs.keys():
122 env.SConfigHBuilder(k, env.Value(_ac_config_hs[k]))
124 class SConfWarning(SCons.Warnings.Warning):
126 SCons.Warnings.enableWarningClass(SConfWarning)
128 # some error definitions
129 class SConfError(SCons.Errors.UserError):
130 def __init__(self,msg):
131 SCons.Errors.UserError.__init__(self,msg)
133 class ConfigureDryRunError(SConfError):
134 """Raised when a file or directory needs to be updated during a Configure
135 process, but the user requested a dry-run"""
136 def __init__(self,target):
137 if not isinstance(target, SCons.Node.FS.File):
138 msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target)
140 msg = 'Cannot update configure test "%s" within a dry-run.' % str(target)
141 SConfError.__init__(self,msg)
143 class ConfigureCacheError(SConfError):
144 """Raised when a use explicitely requested the cache feature, but the test
145 is run the first time."""
146 def __init__(self,target):
147 SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target))
149 # define actions for building text files
150 def _createSource( target, source, env ):
151 fd = open(str(target[0]), "w")
152 fd.write(source[0].get_contents())
154 def _stringSource( target, source, env ):
155 return (str(target[0]) + ' <-\n |' +
156 string.replace( source[0].get_contents(),
159 # python 2.2 introduces types.BooleanType
160 BooleanTypes = [types.IntType]
161 if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
163 class SConfBuildInfo(SCons.Node.FS.FileBuildInfo):
165 Special build info for targets of configure tests. Additional members
166 are result (did the builder succeed last time?) and string, which
167 contains messages of the original build phase.
169 result = None # -> 0/None -> no error, != 0 error
170 string = None # the stdout / stderr output when building the target
172 def set_build_result(self, result, string):
179 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
181 def __init__(self, orig):
183 self.s = StringIO.StringIO()
185 def write(self, str):
190 def writelines(self, lines):
196 Return everything written to orig since the Streamer was created.
198 return self.s.getvalue()
206 class SConfBuildTask(SCons.Taskmaster.AlwaysTask):
208 This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
209 correctly and knows about the current cache_mode.
211 def display(self, message):
212 if sconf_global.logstream:
213 sconf_global.logstream.write("scons: Configure: " + message + "\n")
215 def display_cached_string(self, bi):
217 Logs the original builder messages, given the SConfBuildInfo instance
220 if not isinstance(bi, SConfBuildInfo):
221 SCons.Warnings.warn(SConfWarning,
222 "The stored build information has an unexpected class: %s" % bi.__class__)
224 self.display("The original builder output was:\n" +
225 string.replace(" |" + str(bi.string),
229 # check, if the reason was a ConfigureDryRunError or a
230 # ConfigureCacheError and if yes, reraise the exception
231 exc_type = self.exc_info()[0]
232 if issubclass(exc_type, SConfError):
234 elif issubclass(exc_type, SCons.Errors.BuildError):
235 # we ignore Build Errors (occurs, when a test doesn't pass)
236 # Clear the exception to prevent the contained traceback
237 # to build a reference cycle.
240 self.display('Caught exception while building "%s":\n' %
243 excepthook = sys.excepthook
244 except AttributeError:
245 # Earlier versions of Python don't have sys.excepthook...
246 def excepthook(type, value, tb):
247 traceback.print_tb(tb)
249 apply(excepthook, self.exc_info())
250 return SCons.Taskmaster.Task.failed(self)
252 def collect_node_states(self):
253 # returns (is_up_to_date, cached_error, cachable)
254 # where is_up_to_date is 1, if the node(s) are up_to_date
255 # cached_error is 1, if the node(s) are up_to_date, but the
257 # cachable is 0, if some nodes are not in our cache
262 for t in self.targets:
263 if T: Trace('%s' % (t))
264 bi = t.get_stored_info().binfo
265 if isinstance(bi, SConfBuildInfo):
266 if T: Trace(': SConfBuildInfo')
267 if cache_mode == CACHE:
268 t.set_state(SCons.Node.up_to_date)
269 if T: Trace(': set_state(up_to-date)')
271 if T: Trace(': get_state() %s' % t.get_state())
272 if T: Trace(': changed() %s' % t.changed())
273 if (t.get_state() != SCons.Node.up_to_date and t.changed()):
275 if T: Trace(': changed %s' % changed)
276 cached_error = cached_error or bi.result
278 if T: Trace(': else')
279 # the node hasn't been built in a SConf context or doesn't
282 changed = ( t.get_state() != SCons.Node.up_to_date )
283 if T: Trace(': changed %s' % changed)
285 return (not changed, cached_error, cachable)
288 if not self.targets[0].has_builder():
293 is_up_to_date, cached_error, cachable = self.collect_node_states()
295 if cache_mode == CACHE and not cachable:
296 raise ConfigureCacheError(self.targets[0])
297 elif cache_mode == FORCE:
300 if cached_error and is_up_to_date:
301 self.display("Building \"%s\" failed in a previous run and all "
302 "its sources are up to date." % str(self.targets[0]))
303 binfo = self.targets[0].get_stored_info().binfo
304 self.display_cached_string(binfo)
305 raise SCons.Errors.BuildError # will be 'caught' in self.failed
307 self.display("\"%s\" is up to date." % str(self.targets[0]))
308 binfo = self.targets[0].get_stored_info().binfo
309 self.display_cached_string(binfo)
311 raise ConfigureDryRunError(self.targets[0])
313 # note stdout and stderr are the same here
314 s = sys.stdout = sys.stderr = Streamer(sys.stdout)
316 env = self.targets[0].get_build_env()
317 if cache_mode == FORCE:
318 # Set up the Decider() to force rebuilds by saying
319 # that every source has changed. Note that we still
320 # call the environment's underlying source decider so
321 # that the correct .sconsign info will get calculated
322 # and keep the build state consistent.
323 def force_build(dependency, target, prev_ni,
324 env_decider=env.decide_source):
325 env_decider(dependency, target, prev_ni)
327 env.Decider(force_build)
328 env['PSTDOUT'] = env['PSTDERR'] = s
331 self.targets[0].build()
333 sys.stdout = sys.stderr = env['PSTDOUT'] = \
334 env['PSTDERR'] = sconf.logstream
335 except KeyboardInterrupt:
338 exc_value = sys.exc_info()[1]
339 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
341 for t in self.targets:
342 binfo = t.get_binfo()
343 binfo.__class__ = SConfBuildInfo
344 binfo.set_build_result(1, s.getvalue())
345 sconsign_entry = SCons.SConsign.SConsignEntry()
346 sconsign_entry.binfo = binfo
347 #sconsign_entry.ninfo = self.get_ninfo()
348 # We'd like to do this as follows:
349 # t.store_info(binfo)
350 # However, we need to store it as an SConfBuildInfo
351 # object, and store_info() will turn it into a
352 # regular FileNodeInfo if the target is itself a
354 sconsign = t.dir.sconsign()
355 sconsign.set_entry(t.name, sconsign_entry)
359 for t in self.targets:
360 binfo = t.get_binfo()
361 binfo.__class__ = SConfBuildInfo
362 binfo.set_build_result(0, 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
372 sconsign = t.dir.sconsign()
373 sconsign.set_entry(t.name, sconsign_entry)
377 """This is simply a class to represent a configure context. After
378 creating a SConf object, you can call any tests. After finished with your
379 tests, be sure to call the Finish() method, which returns the modified
381 Some words about caching: In most cases, it is not necessary to cache
382 Test results explicitely. Instead, we use the scons dependency checking
383 mechanism. For example, if one wants to compile a test program
384 (SConf.TryLink), the compiler is only called, if the program dependencies
385 have changed. However, if the program could not be compiled in a former
386 SConf run, we need to explicitely cache this error.
389 def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
390 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
391 """Constructor. Pass additional tests in the custom_tests-dictinary,
392 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
393 defines a custom test.
394 Note also the conf_dir and log_file arguments (you may want to
395 build tests in the VariantDir, not in the SourceDir)
399 SConfFS = SCons.Node.FS.default_fs or \
400 SCons.Node.FS.FS(env.fs.pathTop)
401 if sconf_global is not None:
402 raise (SCons.Errors.UserError,
403 "Only one SConf object may be active at one time")
405 if log_file is not None:
406 log_file = SConfFS.File(env.subst(log_file))
407 self.logfile = log_file
408 self.logstream = None
409 self.lastTarget = None
411 self.cached = 0 # will be set, if all test results are cached
416 'CheckCXX' : CheckCXX,
417 'CheckSHCC' : CheckSHCC,
418 'CheckSHCXX' : CheckSHCXX,
419 'CheckFunc' : CheckFunc,
420 'CheckType' : CheckType,
421 'CheckTypeSize' : CheckTypeSize,
422 'CheckDeclaration' : CheckDeclaration,
423 'CheckHeader' : CheckHeader,
424 'CheckCHeader' : CheckCHeader,
425 'CheckCXXHeader' : CheckCXXHeader,
426 'CheckLib' : CheckLib,
427 'CheckLibWithHeader' : CheckLibWithHeader,
429 self.AddTests(default_tests)
430 self.AddTests(custom_tests)
431 self.confdir = SConfFS.Dir(env.subst(conf_dir))
432 if config_h is not None:
433 config_h = SConfFS.File(config_h)
434 self.config_h = config_h
438 """Call this method after finished with your tests:
444 def Define(self, name, value = None, comment = None):
446 Define a pre processor symbol name, with the optional given value in the
447 current config header.
449 If value is None (default), then #define name is written. If value is not
450 none, then #define name value is written.
452 comment is a string which will be put as a C comment in the
453 header, to explain the meaning of the value (appropriate C comments /* and
454 */ will be put automatically."""
457 comment_str = "/* %s */" % comment
458 lines.append(comment_str)
460 if value is not None:
461 define_str = "#define %s %s" % (name, value)
463 define_str = "#define %s" % name
464 lines.append(define_str)
467 self.config_h_text = self.config_h_text + string.join(lines, '\n')
469 def BuildNodes(self, nodes):
471 Tries to build the given nodes immediately. Returns 1 on success,
474 if self.logstream is not None:
475 # override stdout / stderr to write in log file
476 oldStdout = sys.stdout
477 sys.stdout = self.logstream
478 oldStderr = sys.stderr
479 sys.stderr = self.logstream
481 # the engine assumes the current path is the SConstruct directory ...
482 old_fs_dir = SConfFS.getcwd()
483 old_os_dir = os.getcwd()
484 SConfFS.chdir(SConfFS.Top, change_os_dir=1)
486 # Because we take responsibility here for writing out our
487 # own .sconsign info (see SConfBuildTask.execute(), above),
488 # we override the store_info() method with a null place-holder
489 # so we really control how it gets written.
491 n.store_info = n.do_not_store_info
496 # ToDo: use user options for calc
497 save_max_drift = SConfFS.get_max_drift()
498 SConfFS.set_max_drift(0)
499 tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
500 # we don't want to build tests in parallel
501 jobs = SCons.Job.Jobs(1, tm )
504 state = n.get_state()
505 if (state != SCons.Node.executed and
506 state != SCons.Node.up_to_date):
507 # the node could not be built. we return 0 in this case
510 SConfFS.set_max_drift(save_max_drift)
512 SConfFS.chdir(old_fs_dir, change_os_dir=0)
513 if self.logstream is not None:
514 # restore stdout / stderr
515 sys.stdout = oldStdout
516 sys.stderr = oldStderr
519 def pspawn_wrapper(self, sh, escape, cmd, args, env):
520 """Wrapper function for handling piped spawns.
522 This looks to the calling interface (in Action.py) like a "normal"
523 spawn, but associates the call with the PSPAWN variable from
524 the construction environment and with the streams to which we
525 want the output logged. This gets slid into the construction
526 environment as the SPAWN variable so Action.py doesn't have to
527 know or care whether it's spawning a piped command or not.
529 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
532 def TryBuild(self, builder, text = None, extension = ""):
533 """Low level TryBuild implementation. Normally you don't need to
534 call that - you can use TryCompile / TryLink / TryRun instead
536 global _ac_build_counter
538 # Make sure we have a PSPAWN value, and save the current
541 self.pspawn = self.env['PSPAWN']
543 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
545 save_spawn = self.env['SPAWN']
547 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
551 f = "conftest_" + str(_ac_build_counter)
552 pref = self.env.subst( builder.builder.prefix )
553 suff = self.env.subst( builder.builder.suffix )
554 target = self.confdir.File(pref + f + suff)
557 # Slide our wrapper into the construction environment as
558 # the SPAWN function.
559 self.env['SPAWN'] = self.pspawn_wrapper
560 sourcetext = self.env.Value(text)
563 textFile = self.confdir.File(f + extension)
564 textFileNode = self.env.SConfSourceBuilder(target=textFile,
566 nodesToBeBuilt.extend(textFileNode)
567 source = textFileNode
571 nodes = builder(target = target, source = source)
572 if not SCons.Util.is_List(nodes):
574 nodesToBeBuilt.extend(nodes)
575 result = self.BuildNodes(nodesToBeBuilt)
578 self.env['SPAWN'] = save_spawn
580 _ac_build_counter = _ac_build_counter + 1
582 self.lastTarget = nodes[0]
584 self.lastTarget = None
588 def TryAction(self, action, text = None, extension = ""):
589 """Tries to execute the given action with optional source file
590 contents <text> and optional source file extension <extension>,
591 Returns the status (0 : failed, 1 : ok) and the contents of the
594 builder = SCons.Builder.Builder(action=action)
595 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
596 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
597 del self.env['BUILDERS']['SConfActionBuilder']
599 outputStr = self.lastTarget.get_contents()
600 return (1, outputStr)
603 def TryCompile( self, text, extension):
604 """Compiles the program given in text to an env.Object, using extension
605 as file extension (e.g. '.c'). Returns 1, if compilation was
606 successful, 0 otherwise. The target is saved in self.lastTarget (for
609 return self.TryBuild(self.env.Object, text, extension)
611 def TryLink( self, text, extension ):
612 """Compiles the program given in text to an executable env.Program,
613 using extension as file extension (e.g. '.c'). Returns 1, if
614 compilation was successful, 0 otherwise. The target is saved in
615 self.lastTarget (for further processing).
617 return self.TryBuild(self.env.Program, text, extension )
619 def TryRun(self, text, extension ):
620 """Compiles and runs the program given in text, using extension
621 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
622 (0, '') otherwise. The target (a file containing the program's stdout)
623 is saved in self.lastTarget (for further processing).
625 ok = self.TryLink(text, extension)
627 prog = self.lastTarget
629 output = SConfFS.File(pname+'.out')
630 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
631 ok = self.BuildNodes(node)
633 outputStr = output.get_contents()
634 return( 1, outputStr)
638 """A wrapper around Tests (to ensure sanity)"""
639 def __init__(self, test, sconf):
642 def __call__(self, *args, **kw):
643 if not self.sconf.active:
644 raise (SCons.Errors.UserError,
645 "Test called after sconf.Finish()")
646 context = CheckContext(self.sconf)
647 ret = apply(self.test, (context,) + args, kw)
648 if self.sconf.config_h is not None:
649 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
650 context.Result("error: no result")
653 def AddTest(self, test_name, test_instance):
654 """Adds test_class to this SConf instance. It can be called with
655 self.test_name(...)"""
656 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
658 def AddTests(self, tests):
659 """Adds all the tests given in the tests dictionary to this SConf
662 for name in tests.keys():
663 self.AddTest(name, tests[name])
665 def _createDir( self, node ):
668 if not os.path.isdir( dirName ):
669 raise ConfigureDryRunError(dirName)
671 if not os.path.isdir( dirName ):
672 os.makedirs( dirName )
676 """Private method. Set up logstream, and set the environment
677 variables necessary for a piped build
679 global _ac_config_logs
683 self.lastEnvFs = self.env.fs
684 self.env.fs = SConfFS
685 self._createDir(self.confdir)
686 self.confdir.up().add_ignore( [self.confdir] )
688 if self.logfile is not None and not dryrun:
689 # truncate logfile, if SConf.Configure is called for the first time
691 if _ac_config_logs.has_key(self.logfile):
694 _ac_config_logs[self.logfile] = None
696 fp = open(str(self.logfile), log_mode)
697 self.logstream = SCons.Util.Unbuffered(fp)
698 # logfile may stay in a build directory, so we tell
699 # the build system not to override it with a eventually
700 # existing file with the same name in the source directory
701 self.logfile.dir.add_ignore( [self.logfile] )
703 tb = traceback.extract_stack()[-3-self.depth]
704 old_fs_dir = SConfFS.getcwd()
705 SConfFS.chdir(SConfFS.Top, change_os_dir=0)
706 self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' %
707 (tb[0], tb[1], str(self.confdir)) )
708 SConfFS.chdir(old_fs_dir)
710 self.logstream = None
711 # we use a special builder to create source files from TEXT
712 action = SCons.Action.Action(_createSource,
714 sconfSrcBld = SCons.Builder.Builder(action=action)
715 self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
716 self.config_h_text = _ac_config_hs.get(self.config_h, "")
718 # only one SConf instance should be active at a time ...
722 """Private method. Reset to non-piped spawn"""
723 global sconf_global, _ac_config_hs
726 raise SCons.Errors.UserError, "Finish may be called only once!"
727 if self.logstream is not None and not dryrun:
728 self.logstream.write("\n")
729 self.logstream.close()
730 self.logstream = None
731 # remove the SConfSourceBuilder from the environment
732 blds = self.env['BUILDERS']
733 del blds['SConfSourceBuilder']
734 self.env.Replace( BUILDERS=blds )
737 if not self.config_h is None:
738 _ac_config_hs[self.config_h] = self.config_h_text
739 self.env.fs = self.lastEnvFs
742 """Provides a context for configure tests. Defines how a test writes to the
745 A typical test is just a callable with an instance of CheckContext as
748 def CheckCustom(context, ...)
749 context.Message('Checking my weird test ... ')
750 ret = myWeirdTestFunction(...)
753 Often, myWeirdTestFunction will be one of
754 context.TryCompile/context.TryLink/context.TryRun. The results of
755 those are cached, for they are only rebuild, if the dependencies have
759 def __init__(self, sconf):
760 """Constructor. Pass the corresponding SConf instance."""
762 self.did_show_result = 0
767 self.headerfilename = None
768 self.config_h = "" # config_h text will be stored here
769 # we don't regenerate the config.h file after each test. That means,
770 # that tests won't be able to include the config.h file, and so
771 # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major
772 # issue, though. If it turns out, that we need to include config.h
773 # in tests, we must ensure, that the dependencies are worked out
774 # correctly. Note that we can't use Conftest.py's support for config.h,
775 # cause we will need to specify a builder for the config.h file ...
777 def Message(self, text):
778 """Inform about what we are doing right now, e.g.
779 'Checking for SOMETHING ... '
782 self.sconf.cached = 1
783 self.did_show_result = 0
785 def Result(self, res):
786 """Inform about the result of the test. res may be an integer or a
787 string. In case of an integer, the written text will be 'yes' or 'no'.
788 The result is only displayed when self.did_show_result is not set.
790 if type(res) in BooleanTypes:
795 elif type(res) == types.StringType:
798 raise TypeError, "Expected string, int or bool, got " + str(type(res))
800 if self.did_show_result == 0:
801 # Didn't show result yet, do it now.
802 self.Display(text + "\n")
803 self.did_show_result = 1
805 def TryBuild(self, *args, **kw):
806 return apply(self.sconf.TryBuild, args, kw)
808 def TryAction(self, *args, **kw):
809 return apply(self.sconf.TryAction, args, kw)
811 def TryCompile(self, *args, **kw):
812 return apply(self.sconf.TryCompile, args, kw)
814 def TryLink(self, *args, **kw):
815 return apply(self.sconf.TryLink, args, kw)
817 def TryRun(self, *args, **kw):
818 return apply(self.sconf.TryRun, args, kw)
820 def __getattr__( self, attr ):
822 return self.sconf.env
823 elif( attr == 'lastTarget' ):
824 return self.sconf.lastTarget
826 raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
828 #### Stuff used by Conftest.py (look there for explanations).
830 def BuildProg(self, text, ext):
831 self.sconf.cached = 1
832 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
833 return not self.TryBuild(self.env.Program, text, ext)
835 def CompileProg(self, text, ext):
836 self.sconf.cached = 1
837 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
838 return not self.TryBuild(self.env.Object, text, ext)
840 def CompileSharedObject(self, text, ext):
841 self.sconf.cached = 1
842 # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc.
843 return not self.TryBuild(self.env.SharedObject, text, ext)
845 def RunProg(self, text, ext):
846 self.sconf.cached = 1
847 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
848 st, out = self.TryRun(text, ext)
851 def AppendLIBS(self, lib_name_list):
852 oldLIBS = self.env.get( 'LIBS', [] )
853 self.env.Append(LIBS = lib_name_list)
856 def PrependLIBS(self, lib_name_list):
857 oldLIBS = self.env.get( 'LIBS', [] )
858 self.env.Prepend(LIBS = lib_name_list)
861 def SetLIBS(self, val):
862 oldLIBS = self.env.get( 'LIBS', [] )
863 self.env.Replace(LIBS = val)
866 def Display(self, msg):
867 if self.sconf.cached:
868 # We assume that Display is called twice for each test here
869 # once for the Checking for ... message and once for the result.
870 # The self.sconf.cached flag can only be set between those calls
871 msg = "(cached) " + msg
872 self.sconf.cached = 0
873 progress_display(msg, append_newline=0)
874 self.Log("scons: Configure: " + msg + "\n")
877 if self.sconf.logstream is not None:
878 self.sconf.logstream.write(msg)
880 #### End of stuff used by Conftest.py.
883 def SConf(*args, **kw):
884 if kw.get(build_type, True):
885 kw['_depth'] = kw.get('_depth', 0) + 1
886 for bt in build_types:
891 return apply(SConfBase, args, kw)
893 return SCons.Util.Null()
896 def CheckFunc(context, function_name, header = None, language = None):
897 res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language)
898 context.did_show_result = 1
901 def CheckType(context, type_name, includes = "", language = None):
902 res = SCons.Conftest.CheckType(context, type_name,
903 header = includes, language = language)
904 context.did_show_result = 1
907 def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
908 res = SCons.Conftest.CheckTypeSize(context, type_name,
909 header = includes, language = language,
911 context.did_show_result = 1
914 def CheckDeclaration(context, declaration, includes = "", language = None):
915 res = SCons.Conftest.CheckDeclaration(context, declaration,
918 context.did_show_result = 1
921 def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
922 # used by CheckHeader and CheckLibWithHeader to produce C - #include
923 # statements from the specified header (list)
924 if not SCons.Util.is_List(headers):
928 lastHeader = headers[-1]
929 headers = headers[:-1]
933 l.append("#include %s%s%s\n"
934 % (include_quotes[0], s, include_quotes[1]))
935 return string.join(l, ''), lastHeader
937 def CheckHeader(context, header, include_quotes = '<>', language = None):
939 A test for a C or C++ header file.
941 prog_prefix, hdr_to_check = \
942 createIncludesFromHeaders(header, 1, include_quotes)
943 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
945 include_quotes = include_quotes)
946 context.did_show_result = 1
949 def CheckCC(context):
950 res = SCons.Conftest.CheckCC(context)
951 context.did_show_result = 1
954 def CheckCXX(context):
955 res = SCons.Conftest.CheckCXX(context)
956 context.did_show_result = 1
959 def CheckSHCC(context):
960 res = SCons.Conftest.CheckSHCC(context)
961 context.did_show_result = 1
964 def CheckSHCXX(context):
965 res = SCons.Conftest.CheckSHCXX(context)
966 context.did_show_result = 1
969 # Bram: Make this function obsolete? CheckHeader() is more generic.
971 def CheckCHeader(context, header, include_quotes = '""'):
973 A test for a C header file.
975 return CheckHeader(context, header, include_quotes, language = "C")
978 # Bram: Make this function obsolete? CheckHeader() is more generic.
980 def CheckCXXHeader(context, header, include_quotes = '""'):
982 A test for a C++ header file.
984 return CheckHeader(context, header, include_quotes, language = "C++")
987 def CheckLib(context, library = None, symbol = "main",
988 header = None, language = None, autoadd = 1):
990 A test for a library. See also CheckLibWithHeader.
991 Note that library may also be None to test whether the given symbol
992 compiles without flags.
998 if not SCons.Util.is_List(library):
1001 # ToDo: accept path for the library
1002 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
1003 language = language, autoadd = autoadd)
1004 context.did_show_result = 1
1008 # Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H.
1010 def CheckLibWithHeader(context, libs, header, language,
1011 call = None, autoadd = 1):
1012 # ToDo: accept path for library. Support system header files.
1014 Another (more sophisticated) test for a library.
1015 Checks, if library and header is available for language (may be 'C'
1016 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
1017 As in CheckLib, we support library=None, to test if the call compiles
1018 without extra link flags.
1020 prog_prefix, dummy = \
1021 createIncludesFromHeaders(header, 0)
1025 if not SCons.Util.is_List(libs):
1028 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1029 call = call, language = language, autoadd = autoadd)
1030 context.did_show_result = 1
1035 # indent-tabs-mode:nil
1037 # vim: set expandtab tabstop=4 shiftwidth=4: