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__"
42 import SCons.Taskmaster
47 from SCons.Debug import Trace
49 # Turn off the Conftest error logging
50 SCons.Conftest.LogInputFiles = 0
51 SCons.Conftest.LogErrorMessages = 0
55 build_types = ['clean', 'help']
57 def SetBuildType(type):
61 # to be set, if we are in dry-run mode
64 AUTO=0 # use SCons dependency scanning for up-to-date checks
65 FORCE=1 # force all tests to be rebuilt
66 CACHE=2 # force all tests to be taken from cache (raise an error, if necessary)
69 def SetCacheMode(mode):
70 """Set the Configure cache mode. mode must be one of "auto", "force",
80 raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
82 progress_display = SCons.Util.display # will be overwritten by SCons.Script
83 def SetProgressDisplay(display):
84 """Set the progress display to use (called from SCons.Script)"""
85 global progress_display
86 progress_display = display
90 _ac_build_counter = 0 # incremented, whenever TryBuild is called
91 _ac_config_logs = {} # all config.log files created in this build
92 _ac_config_hs = {} # all config.h files created in this build
93 sconf_global = None # current sconf object
95 def _createConfigH(target, source, env):
96 t = open(str(target[0]), "w")
97 defname = re.sub('[^A-Za-z0-9_]', '_', str(target[0]).upper())
98 t.write("""#ifndef %(DEFNAME)s_SEEN
99 #define %(DEFNAME)s_SEEN
101 """ % {'DEFNAME' : defname})
102 t.write(source[0].get_contents())
104 #endif /* %(DEFNAME)s_SEEN */
105 """ % {'DEFNAME' : defname})
108 def _stringConfigH(target, source, env):
109 return "scons: Configure: creating " + str(target[0])
111 def CreateConfigHBuilder(env):
112 """Called just before the building targets phase begins."""
113 if len(_ac_config_hs) == 0:
115 action = SCons.Action.Action(_createConfigH,
117 sconfigHBld = SCons.Builder.Builder(action=action)
118 env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} )
119 for k in _ac_config_hs.keys():
120 env.SConfigHBuilder(k, env.Value(_ac_config_hs[k]))
122 class SConfWarning(SCons.Warnings.Warning):
124 SCons.Warnings.enableWarningClass(SConfWarning)
126 # some error definitions
127 class SConfError(SCons.Errors.UserError):
128 def __init__(self,msg):
129 SCons.Errors.UserError.__init__(self,msg)
131 class ConfigureDryRunError(SConfError):
132 """Raised when a file or directory needs to be updated during a Configure
133 process, but the user requested a dry-run"""
134 def __init__(self,target):
135 if not isinstance(target, SCons.Node.FS.File):
136 msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target)
138 msg = 'Cannot update configure test "%s" within a dry-run.' % str(target)
139 SConfError.__init__(self,msg)
141 class ConfigureCacheError(SConfError):
142 """Raised when a use explicitely requested the cache feature, but the test
143 is run the first time."""
144 def __init__(self,target):
145 SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target))
147 # define actions for building text files
148 def _createSource( target, source, env ):
149 fd = open(str(target[0]), "w")
150 fd.write(source[0].get_contents())
152 def _stringSource( target, source, env ):
153 return (str(target[0]) + ' <-\n |' +
154 source[0].get_contents().replace( '\n', "\n |" ) )
156 # python 2.2 introduces bool
157 BooleanTypes = [int, bool]
159 class SConfBuildInfo(SCons.Node.FS.FileBuildInfo):
161 Special build info for targets of configure tests. Additional members
162 are result (did the builder succeed last time?) and string, which
163 contains messages of the original build phase.
165 result = None # -> 0/None -> no error, != 0 error
166 string = None # the stdout / stderr output when building the target
168 def set_build_result(self, result, string):
175 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
177 def __init__(self, orig):
179 self.s = StringIO.StringIO()
181 def write(self, str):
186 def writelines(self, lines):
192 Return everything written to orig since the Streamer was created.
194 return self.s.getvalue()
202 class SConfBuildTask(SCons.Taskmaster.AlwaysTask):
204 This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
205 correctly and knows about the current cache_mode.
207 def display(self, message):
208 if sconf_global.logstream:
209 sconf_global.logstream.write("scons: Configure: " + message + "\n")
211 def display_cached_string(self, bi):
213 Logs the original builder messages, given the SConfBuildInfo instance
216 if not isinstance(bi, SConfBuildInfo):
217 SCons.Warnings.warn(SConfWarning,
218 "The stored build information has an unexpected class: %s" % bi.__class__)
220 self.display("The original builder output was:\n" +
221 (" |" + str(bi.string)).replace("\n", "\n |"))
224 # check, if the reason was a ConfigureDryRunError or a
225 # ConfigureCacheError and if yes, reraise the exception
226 exc_type = self.exc_info()[0]
227 if issubclass(exc_type, SConfError):
229 elif issubclass(exc_type, SCons.Errors.BuildError):
230 # we ignore Build Errors (occurs, when a test doesn't pass)
231 # Clear the exception to prevent the contained traceback
232 # to build a reference cycle.
235 self.display('Caught exception while building "%s":\n' %
238 excepthook = sys.excepthook
239 except AttributeError:
240 # Earlier versions of Python don't have sys.excepthook...
241 def excepthook(type, value, tb):
242 traceback.print_tb(tb)
244 excepthook(*self.exc_info())
245 return SCons.Taskmaster.Task.failed(self)
247 def collect_node_states(self):
248 # returns (is_up_to_date, cached_error, cachable)
249 # where is_up_to_date is 1, if the node(s) are up_to_date
250 # cached_error is 1, if the node(s) are up_to_date, but the
252 # cachable is 0, if some nodes are not in our cache
257 for t in self.targets:
258 if T: Trace('%s' % (t))
259 bi = t.get_stored_info().binfo
260 if isinstance(bi, SConfBuildInfo):
261 if T: Trace(': SConfBuildInfo')
262 if cache_mode == CACHE:
263 t.set_state(SCons.Node.up_to_date)
264 if T: Trace(': set_state(up_to-date)')
266 if T: Trace(': get_state() %s' % t.get_state())
267 if T: Trace(': changed() %s' % t.changed())
268 if (t.get_state() != SCons.Node.up_to_date and t.changed()):
270 if T: Trace(': changed %s' % changed)
271 cached_error = cached_error or bi.result
273 if T: Trace(': else')
274 # the node hasn't been built in a SConf context or doesn't
277 changed = ( t.get_state() != SCons.Node.up_to_date )
278 if T: Trace(': changed %s' % changed)
280 return (not changed, cached_error, cachable)
283 if not self.targets[0].has_builder():
288 is_up_to_date, cached_error, cachable = self.collect_node_states()
290 if cache_mode == CACHE and not cachable:
291 raise ConfigureCacheError(self.targets[0])
292 elif cache_mode == FORCE:
295 if cached_error and is_up_to_date:
296 self.display("Building \"%s\" failed in a previous run and all "
297 "its sources are up to date." % str(self.targets[0]))
298 binfo = self.targets[0].get_stored_info().binfo
299 self.display_cached_string(binfo)
300 raise SCons.Errors.BuildError # will be 'caught' in self.failed
302 self.display("\"%s\" is up to date." % str(self.targets[0]))
303 binfo = self.targets[0].get_stored_info().binfo
304 self.display_cached_string(binfo)
306 raise ConfigureDryRunError(self.targets[0])
308 # note stdout and stderr are the same here
309 s = sys.stdout = sys.stderr = Streamer(sys.stdout)
311 env = self.targets[0].get_build_env()
312 if cache_mode == FORCE:
313 # Set up the Decider() to force rebuilds by saying
314 # that every source has changed. Note that we still
315 # call the environment's underlying source decider so
316 # that the correct .sconsign info will get calculated
317 # and keep the build state consistent.
318 def force_build(dependency, target, prev_ni,
319 env_decider=env.decide_source):
320 env_decider(dependency, target, prev_ni)
322 if env.decide_source.func_code is not force_build.func_code:
323 env.Decider(force_build)
324 env['PSTDOUT'] = env['PSTDERR'] = s
327 self.targets[0].build()
329 sys.stdout = sys.stderr = env['PSTDOUT'] = \
330 env['PSTDERR'] = sconf.logstream
331 except KeyboardInterrupt:
334 exc_value = sys.exc_info()[1]
335 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
337 for t in self.targets:
338 binfo = t.get_binfo()
339 binfo.__class__ = SConfBuildInfo
340 binfo.set_build_result(1, s.getvalue())
341 sconsign_entry = SCons.SConsign.SConsignEntry()
342 sconsign_entry.binfo = binfo
343 #sconsign_entry.ninfo = self.get_ninfo()
344 # We'd like to do this as follows:
345 # t.store_info(binfo)
346 # However, we need to store it as an SConfBuildInfo
347 # object, and store_info() will turn it into a
348 # regular FileNodeInfo if the target is itself a
350 sconsign = t.dir.sconsign()
351 sconsign.set_entry(t.name, sconsign_entry)
355 for t in self.targets:
356 binfo = t.get_binfo()
357 binfo.__class__ = SConfBuildInfo
358 binfo.set_build_result(0, s.getvalue())
359 sconsign_entry = SCons.SConsign.SConsignEntry()
360 sconsign_entry.binfo = binfo
361 #sconsign_entry.ninfo = self.get_ninfo()
362 # We'd like to do this as follows:
363 # t.store_info(binfo)
364 # However, we need to store it as an SConfBuildInfo
365 # object, and store_info() will turn it into a
366 # regular FileNodeInfo if the target is itself a
368 sconsign = t.dir.sconsign()
369 sconsign.set_entry(t.name, sconsign_entry)
373 """This is simply a class to represent a configure context. After
374 creating a SConf object, you can call any tests. After finished with your
375 tests, be sure to call the Finish() method, which returns the modified
377 Some words about caching: In most cases, it is not necessary to cache
378 Test results explicitely. Instead, we use the scons dependency checking
379 mechanism. For example, if one wants to compile a test program
380 (SConf.TryLink), the compiler is only called, if the program dependencies
381 have changed. However, if the program could not be compiled in a former
382 SConf run, we need to explicitely cache this error.
385 def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
386 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
387 """Constructor. Pass additional tests in the custom_tests-dictinary,
388 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
389 defines a custom test.
390 Note also the conf_dir and log_file arguments (you may want to
391 build tests in the VariantDir, not in the SourceDir)
395 SConfFS = SCons.Node.FS.default_fs or \
396 SCons.Node.FS.FS(env.fs.pathTop)
397 if sconf_global is not None:
398 raise (SCons.Errors.UserError,
399 "Only one SConf object may be active at one time")
401 if log_file is not None:
402 log_file = SConfFS.File(env.subst(log_file))
403 self.logfile = log_file
404 self.logstream = None
405 self.lastTarget = None
407 self.cached = 0 # will be set, if all test results are cached
412 'CheckCXX' : CheckCXX,
413 'CheckSHCC' : CheckSHCC,
414 'CheckSHCXX' : CheckSHCXX,
415 'CheckFunc' : CheckFunc,
416 'CheckType' : CheckType,
417 'CheckTypeSize' : CheckTypeSize,
418 'CheckDeclaration' : CheckDeclaration,
419 'CheckHeader' : CheckHeader,
420 'CheckCHeader' : CheckCHeader,
421 'CheckCXXHeader' : CheckCXXHeader,
422 'CheckLib' : CheckLib,
423 'CheckLibWithHeader' : CheckLibWithHeader,
425 self.AddTests(default_tests)
426 self.AddTests(custom_tests)
427 self.confdir = SConfFS.Dir(env.subst(conf_dir))
428 if config_h is not None:
429 config_h = SConfFS.File(config_h)
430 self.config_h = config_h
434 """Call this method after finished with your tests:
440 def Define(self, name, value = None, comment = None):
442 Define a pre processor symbol name, with the optional given value in the
443 current config header.
445 If value is None (default), then #define name is written. If value is not
446 none, then #define name value is written.
448 comment is a string which will be put as a C comment in the
449 header, to explain the meaning of the value (appropriate C comments /* and
450 */ will be put automatically."""
453 comment_str = "/* %s */" % comment
454 lines.append(comment_str)
456 if value is not None:
457 define_str = "#define %s %s" % (name, value)
459 define_str = "#define %s" % name
460 lines.append(define_str)
463 self.config_h_text = self.config_h_text + '\n'.join(lines)
465 def BuildNodes(self, nodes):
467 Tries to build the given nodes immediately. Returns 1 on success,
470 if self.logstream is not None:
471 # override stdout / stderr to write in log file
472 oldStdout = sys.stdout
473 sys.stdout = self.logstream
474 oldStderr = sys.stderr
475 sys.stderr = self.logstream
477 # the engine assumes the current path is the SConstruct directory ...
478 old_fs_dir = SConfFS.getcwd()
479 old_os_dir = os.getcwd()
480 SConfFS.chdir(SConfFS.Top, change_os_dir=1)
482 # Because we take responsibility here for writing out our
483 # own .sconsign info (see SConfBuildTask.execute(), above),
484 # we override the store_info() method with a null place-holder
485 # so we really control how it gets written.
487 n.store_info = n.do_not_store_info
492 # ToDo: use user options for calc
493 save_max_drift = SConfFS.get_max_drift()
494 SConfFS.set_max_drift(0)
495 tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
496 # we don't want to build tests in parallel
497 jobs = SCons.Job.Jobs(1, tm )
500 state = n.get_state()
501 if (state != SCons.Node.executed and
502 state != SCons.Node.up_to_date):
503 # the node could not be built. we return 0 in this case
506 SConfFS.set_max_drift(save_max_drift)
508 SConfFS.chdir(old_fs_dir, change_os_dir=0)
509 if self.logstream is not None:
510 # restore stdout / stderr
511 sys.stdout = oldStdout
512 sys.stderr = oldStderr
515 def pspawn_wrapper(self, sh, escape, cmd, args, env):
516 """Wrapper function for handling piped spawns.
518 This looks to the calling interface (in Action.py) like a "normal"
519 spawn, but associates the call with the PSPAWN variable from
520 the construction environment and with the streams to which we
521 want the output logged. This gets slid into the construction
522 environment as the SPAWN variable so Action.py doesn't have to
523 know or care whether it's spawning a piped command or not.
525 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
528 def TryBuild(self, builder, text = None, extension = ""):
529 """Low level TryBuild implementation. Normally you don't need to
530 call that - you can use TryCompile / TryLink / TryRun instead
532 global _ac_build_counter
534 # Make sure we have a PSPAWN value, and save the current
537 self.pspawn = self.env['PSPAWN']
539 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
541 save_spawn = self.env['SPAWN']
543 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
547 f = "conftest_" + str(_ac_build_counter)
548 pref = self.env.subst( builder.builder.prefix )
549 suff = self.env.subst( builder.builder.suffix )
550 target = self.confdir.File(pref + f + suff)
553 # Slide our wrapper into the construction environment as
554 # the SPAWN function.
555 self.env['SPAWN'] = self.pspawn_wrapper
556 sourcetext = self.env.Value(text)
559 textFile = self.confdir.File(f + extension)
560 textFileNode = self.env.SConfSourceBuilder(target=textFile,
562 nodesToBeBuilt.extend(textFileNode)
563 source = textFileNode
567 nodes = builder(target = target, source = source)
568 if not SCons.Util.is_List(nodes):
570 nodesToBeBuilt.extend(nodes)
571 result = self.BuildNodes(nodesToBeBuilt)
574 self.env['SPAWN'] = save_spawn
576 _ac_build_counter = _ac_build_counter + 1
578 self.lastTarget = nodes[0]
580 self.lastTarget = None
584 def TryAction(self, action, text = None, extension = ""):
585 """Tries to execute the given action with optional source file
586 contents <text> and optional source file extension <extension>,
587 Returns the status (0 : failed, 1 : ok) and the contents of the
590 builder = SCons.Builder.Builder(action=action)
591 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
592 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
593 del self.env['BUILDERS']['SConfActionBuilder']
595 outputStr = self.lastTarget.get_contents()
596 return (1, outputStr)
599 def TryCompile( self, text, extension):
600 """Compiles the program given in text to an env.Object, using extension
601 as file extension (e.g. '.c'). Returns 1, if compilation was
602 successful, 0 otherwise. The target is saved in self.lastTarget (for
605 return self.TryBuild(self.env.Object, text, extension)
607 def TryLink( self, text, extension ):
608 """Compiles the program given in text to an executable env.Program,
609 using extension as file extension (e.g. '.c'). Returns 1, if
610 compilation was successful, 0 otherwise. The target is saved in
611 self.lastTarget (for further processing).
613 return self.TryBuild(self.env.Program, text, extension )
615 def TryRun(self, text, extension ):
616 """Compiles and runs the program given in text, using extension
617 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
618 (0, '') otherwise. The target (a file containing the program's stdout)
619 is saved in self.lastTarget (for further processing).
621 ok = self.TryLink(text, extension)
623 prog = self.lastTarget
625 output = self.confdir.File(os.path.basename(pname)+'.out')
626 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
627 ok = self.BuildNodes(node)
629 outputStr = output.get_contents()
630 return( 1, outputStr)
634 """A wrapper around Tests (to ensure sanity)"""
635 def __init__(self, test, sconf):
638 def __call__(self, *args, **kw):
639 if not self.sconf.active:
640 raise (SCons.Errors.UserError,
641 "Test called after sconf.Finish()")
642 context = CheckContext(self.sconf)
643 ret = self.test(context, *args, **kw)
644 if self.sconf.config_h is not None:
645 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
646 context.Result("error: no result")
649 def AddTest(self, test_name, test_instance):
650 """Adds test_class to this SConf instance. It can be called with
651 self.test_name(...)"""
652 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
654 def AddTests(self, tests):
655 """Adds all the tests given in the tests dictionary to this SConf
658 for name in tests.keys():
659 self.AddTest(name, tests[name])
661 def _createDir( self, node ):
664 if not os.path.isdir( dirName ):
665 raise ConfigureDryRunError(dirName)
667 if not os.path.isdir( dirName ):
668 os.makedirs( dirName )
672 """Private method. Set up logstream, and set the environment
673 variables necessary for a piped build
675 global _ac_config_logs
679 self.lastEnvFs = self.env.fs
680 self.env.fs = SConfFS
681 self._createDir(self.confdir)
682 self.confdir.up().add_ignore( [self.confdir] )
684 if self.logfile is not None and not dryrun:
685 # truncate logfile, if SConf.Configure is called for the first time
687 if self.logfile in _ac_config_logs:
690 _ac_config_logs[self.logfile] = None
692 fp = open(str(self.logfile), log_mode)
693 self.logstream = SCons.Util.Unbuffered(fp)
694 # logfile may stay in a build directory, so we tell
695 # the build system not to override it with a eventually
696 # existing file with the same name in the source directory
697 self.logfile.dir.add_ignore( [self.logfile] )
699 tb = traceback.extract_stack()[-3-self.depth]
700 old_fs_dir = SConfFS.getcwd()
701 SConfFS.chdir(SConfFS.Top, change_os_dir=0)
702 self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' %
703 (tb[0], tb[1], str(self.confdir)) )
704 SConfFS.chdir(old_fs_dir)
706 self.logstream = None
707 # we use a special builder to create source files from TEXT
708 action = SCons.Action.Action(_createSource,
710 sconfSrcBld = SCons.Builder.Builder(action=action)
711 self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
712 self.config_h_text = _ac_config_hs.get(self.config_h, "")
714 # only one SConf instance should be active at a time ...
718 """Private method. Reset to non-piped spawn"""
719 global sconf_global, _ac_config_hs
722 raise SCons.Errors.UserError, "Finish may be called only once!"
723 if self.logstream is not None and not dryrun:
724 self.logstream.write("\n")
725 self.logstream.close()
726 self.logstream = None
727 # remove the SConfSourceBuilder from the environment
728 blds = self.env['BUILDERS']
729 del blds['SConfSourceBuilder']
730 self.env.Replace( BUILDERS=blds )
733 if not self.config_h is None:
734 _ac_config_hs[self.config_h] = self.config_h_text
735 self.env.fs = self.lastEnvFs
738 """Provides a context for configure tests. Defines how a test writes to the
741 A typical test is just a callable with an instance of CheckContext as
744 def CheckCustom(context, ...)
745 context.Message('Checking my weird test ... ')
746 ret = myWeirdTestFunction(...)
749 Often, myWeirdTestFunction will be one of
750 context.TryCompile/context.TryLink/context.TryRun. The results of
751 those are cached, for they are only rebuild, if the dependencies have
755 def __init__(self, sconf):
756 """Constructor. Pass the corresponding SConf instance."""
758 self.did_show_result = 0
763 self.headerfilename = None
764 self.config_h = "" # config_h text will be stored here
765 # we don't regenerate the config.h file after each test. That means,
766 # that tests won't be able to include the config.h file, and so
767 # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major
768 # issue, though. If it turns out, that we need to include config.h
769 # in tests, we must ensure, that the dependencies are worked out
770 # correctly. Note that we can't use Conftest.py's support for config.h,
771 # cause we will need to specify a builder for the config.h file ...
773 def Message(self, text):
774 """Inform about what we are doing right now, e.g.
775 'Checking for SOMETHING ... '
778 self.sconf.cached = 1
779 self.did_show_result = 0
781 def Result(self, res):
782 """Inform about the result of the test. res may be an integer or a
783 string. In case of an integer, the written text will be 'yes' or 'no'.
784 The result is only displayed when self.did_show_result is not set.
786 if type(res) in BooleanTypes:
791 elif isinstance(res, str):
794 raise TypeError, "Expected string, int or bool, got " + str(type(res))
796 if self.did_show_result == 0:
797 # Didn't show result yet, do it now.
798 self.Display(text + "\n")
799 self.did_show_result = 1
801 def TryBuild(self, *args, **kw):
802 return self.sconf.TryBuild(*args, **kw)
804 def TryAction(self, *args, **kw):
805 return self.sconf.TryAction(*args, **kw)
807 def TryCompile(self, *args, **kw):
808 return self.sconf.TryCompile(*args, **kw)
810 def TryLink(self, *args, **kw):
811 return self.sconf.TryLink(*args, **kw)
813 def TryRun(self, *args, **kw):
814 return self.sconf.TryRun(*args, **kw)
816 def __getattr__( self, attr ):
818 return self.sconf.env
819 elif( attr == 'lastTarget' ):
820 return self.sconf.lastTarget
822 raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
824 #### Stuff used by Conftest.py (look there for explanations).
826 def BuildProg(self, text, ext):
827 self.sconf.cached = 1
828 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
829 return not self.TryBuild(self.env.Program, text, ext)
831 def CompileProg(self, text, ext):
832 self.sconf.cached = 1
833 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
834 return not self.TryBuild(self.env.Object, text, ext)
836 def CompileSharedObject(self, text, ext):
837 self.sconf.cached = 1
838 # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc.
839 return not self.TryBuild(self.env.SharedObject, text, ext)
841 def RunProg(self, text, ext):
842 self.sconf.cached = 1
843 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
844 st, out = self.TryRun(text, ext)
847 def AppendLIBS(self, lib_name_list):
848 oldLIBS = self.env.get( 'LIBS', [] )
849 self.env.Append(LIBS = lib_name_list)
852 def PrependLIBS(self, lib_name_list):
853 oldLIBS = self.env.get( 'LIBS', [] )
854 self.env.Prepend(LIBS = lib_name_list)
857 def SetLIBS(self, val):
858 oldLIBS = self.env.get( 'LIBS', [] )
859 self.env.Replace(LIBS = val)
862 def Display(self, msg):
863 if self.sconf.cached:
864 # We assume that Display is called twice for each test here
865 # once for the Checking for ... message and once for the result.
866 # The self.sconf.cached flag can only be set between those calls
867 msg = "(cached) " + msg
868 self.sconf.cached = 0
869 progress_display(msg, append_newline=0)
870 self.Log("scons: Configure: " + msg + "\n")
873 if self.sconf.logstream is not None:
874 self.sconf.logstream.write(msg)
876 #### End of stuff used by Conftest.py.
879 def SConf(*args, **kw):
880 if kw.get(build_type, True):
881 kw['_depth'] = kw.get('_depth', 0) + 1
882 for bt in build_types:
887 return SConfBase(*args, **kw)
889 return SCons.Util.Null()
892 def CheckFunc(context, function_name, header = None, language = None):
893 res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language)
894 context.did_show_result = 1
897 def CheckType(context, type_name, includes = "", language = None):
898 res = SCons.Conftest.CheckType(context, type_name,
899 header = includes, language = language)
900 context.did_show_result = 1
903 def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
904 res = SCons.Conftest.CheckTypeSize(context, type_name,
905 header = includes, language = language,
907 context.did_show_result = 1
910 def CheckDeclaration(context, declaration, includes = "", language = None):
911 res = SCons.Conftest.CheckDeclaration(context, declaration,
914 context.did_show_result = 1
917 def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
918 # used by CheckHeader and CheckLibWithHeader to produce C - #include
919 # statements from the specified header (list)
920 if not SCons.Util.is_List(headers):
924 lastHeader = headers[-1]
925 headers = headers[:-1]
929 l.append("#include %s%s%s\n"
930 % (include_quotes[0], s, include_quotes[1]))
931 return ''.join(l), lastHeader
933 def CheckHeader(context, header, include_quotes = '<>', language = None):
935 A test for a C or C++ header file.
937 prog_prefix, hdr_to_check = \
938 createIncludesFromHeaders(header, 1, include_quotes)
939 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
941 include_quotes = include_quotes)
942 context.did_show_result = 1
945 def CheckCC(context):
946 res = SCons.Conftest.CheckCC(context)
947 context.did_show_result = 1
950 def CheckCXX(context):
951 res = SCons.Conftest.CheckCXX(context)
952 context.did_show_result = 1
955 def CheckSHCC(context):
956 res = SCons.Conftest.CheckSHCC(context)
957 context.did_show_result = 1
960 def CheckSHCXX(context):
961 res = SCons.Conftest.CheckSHCXX(context)
962 context.did_show_result = 1
965 # Bram: Make this function obsolete? CheckHeader() is more generic.
967 def CheckCHeader(context, header, include_quotes = '""'):
969 A test for a C header file.
971 return CheckHeader(context, header, include_quotes, language = "C")
974 # Bram: Make this function obsolete? CheckHeader() is more generic.
976 def CheckCXXHeader(context, header, include_quotes = '""'):
978 A test for a C++ header file.
980 return CheckHeader(context, header, include_quotes, language = "C++")
983 def CheckLib(context, library = None, symbol = "main",
984 header = None, language = None, autoadd = 1):
986 A test for a library. See also CheckLibWithHeader.
987 Note that library may also be None to test whether the given symbol
988 compiles without flags.
994 if not SCons.Util.is_List(library):
997 # ToDo: accept path for the library
998 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
999 language = language, autoadd = autoadd)
1000 context.did_show_result = 1
1004 # Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H.
1006 def CheckLibWithHeader(context, libs, header, language,
1007 call = None, autoadd = 1):
1008 # ToDo: accept path for library. Support system header files.
1010 Another (more sophisticated) test for a library.
1011 Checks, if library and header is available for language (may be 'C'
1012 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
1013 As in CheckLib, we support library=None, to test if the call compiles
1014 without extra link flags.
1016 prog_prefix, dummy = \
1017 createIncludesFromHeaders(header, 0)
1021 if not SCons.Util.is_List(libs):
1024 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1025 call = call, language = language, autoadd = autoadd)
1026 context.did_show_result = 1
1031 # indent-tabs-mode:nil
1033 # vim: set expandtab tabstop=4 shiftwidth=4: