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_]', '_', str(target[0]).upper())
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 source[0].get_contents().replace( '\n', "\n |" ) )
158 # python 2.2 introduces bool
159 BooleanTypes = [int, bool]
161 class SConfBuildInfo(SCons.Node.FS.FileBuildInfo):
163 Special build info for targets of configure tests. Additional members
164 are result (did the builder succeed last time?) and string, which
165 contains messages of the original build phase.
167 result = None # -> 0/None -> no error, != 0 error
168 string = None # the stdout / stderr output when building the target
170 def set_build_result(self, result, string):
177 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
179 def __init__(self, orig):
181 self.s = io.StringIO()
183 def write(self, str):
188 def writelines(self, lines):
194 Return everything written to orig since the Streamer was created.
196 return self.s.getvalue()
204 class SConfBuildTask(SCons.Taskmaster.AlwaysTask):
206 This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
207 correctly and knows about the current cache_mode.
209 def display(self, message):
210 if sconf_global.logstream:
211 sconf_global.logstream.write("scons: Configure: " + message + "\n")
213 def display_cached_string(self, bi):
215 Logs the original builder messages, given the SConfBuildInfo instance
218 if not isinstance(bi, SConfBuildInfo):
219 SCons.Warnings.warn(SConfWarning,
220 "The stored build information has an unexpected class: %s" % bi.__class__)
222 self.display("The original builder output was:\n" +
223 (" |" + str(bi.string)).replace("\n", "\n |"))
226 # check, if the reason was a ConfigureDryRunError or a
227 # ConfigureCacheError and if yes, reraise the exception
228 exc_type = self.exc_info()[0]
229 if issubclass(exc_type, SConfError):
231 elif issubclass(exc_type, SCons.Errors.BuildError):
232 # we ignore Build Errors (occurs, when a test doesn't pass)
233 # Clear the exception to prevent the contained traceback
234 # to build a reference cycle.
237 self.display('Caught exception while building "%s":\n' %
240 excepthook = sys.excepthook
241 except AttributeError:
242 # Earlier versions of Python don't have sys.excepthook...
243 def excepthook(type, value, tb):
244 traceback.print_tb(tb)
246 excepthook(*self.exc_info())
247 return SCons.Taskmaster.Task.failed(self)
249 def collect_node_states(self):
250 # returns (is_up_to_date, cached_error, cachable)
251 # where is_up_to_date is 1, if the node(s) are up_to_date
252 # cached_error is 1, if the node(s) are up_to_date, but the
254 # cachable is 0, if some nodes are not in our cache
259 for t in self.targets:
260 if T: Trace('%s' % (t))
261 bi = t.get_stored_info().binfo
262 if isinstance(bi, SConfBuildInfo):
263 if T: Trace(': SConfBuildInfo')
264 if cache_mode == CACHE:
265 t.set_state(SCons.Node.up_to_date)
266 if T: Trace(': set_state(up_to-date)')
268 if T: Trace(': get_state() %s' % t.get_state())
269 if T: Trace(': changed() %s' % t.changed())
270 if (t.get_state() != SCons.Node.up_to_date and t.changed()):
272 if T: Trace(': changed %s' % changed)
273 cached_error = cached_error or bi.result
275 if T: Trace(': else')
276 # the node hasn't been built in a SConf context or doesn't
279 changed = ( t.get_state() != SCons.Node.up_to_date )
280 if T: Trace(': changed %s' % changed)
282 return (not changed, cached_error, cachable)
285 if not self.targets[0].has_builder():
290 is_up_to_date, cached_error, cachable = self.collect_node_states()
292 if cache_mode == CACHE and not cachable:
293 raise ConfigureCacheError(self.targets[0])
294 elif cache_mode == FORCE:
297 if cached_error and is_up_to_date:
298 self.display("Building \"%s\" failed in a previous run and all "
299 "its sources are up to date." % str(self.targets[0]))
300 binfo = self.targets[0].get_stored_info().binfo
301 self.display_cached_string(binfo)
302 raise SCons.Errors.BuildError # will be 'caught' in self.failed
304 self.display("\"%s\" is up to date." % str(self.targets[0]))
305 binfo = self.targets[0].get_stored_info().binfo
306 self.display_cached_string(binfo)
308 raise ConfigureDryRunError(self.targets[0])
310 # note stdout and stderr are the same here
311 s = sys.stdout = sys.stderr = Streamer(sys.stdout)
313 env = self.targets[0].get_build_env()
314 if cache_mode == FORCE:
315 # Set up the Decider() to force rebuilds by saying
316 # that every source has changed. Note that we still
317 # call the environment's underlying source decider so
318 # that the correct .sconsign info will get calculated
319 # and keep the build state consistent.
320 def force_build(dependency, target, prev_ni,
321 env_decider=env.decide_source):
322 env_decider(dependency, target, prev_ni)
324 if env.decide_source.func_code is not force_build.func_code:
325 env.Decider(force_build)
326 env['PSTDOUT'] = env['PSTDERR'] = s
329 self.targets[0].build()
331 sys.stdout = sys.stderr = env['PSTDOUT'] = \
332 env['PSTDERR'] = sconf.logstream
333 except KeyboardInterrupt:
336 exc_value = sys.exc_info()[1]
337 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
339 for t in self.targets:
340 binfo = t.get_binfo()
341 binfo.__class__ = SConfBuildInfo
342 binfo.set_build_result(1, s.getvalue())
343 sconsign_entry = SCons.SConsign.SConsignEntry()
344 sconsign_entry.binfo = binfo
345 #sconsign_entry.ninfo = self.get_ninfo()
346 # We'd like to do this as follows:
347 # t.store_info(binfo)
348 # However, we need to store it as an SConfBuildInfo
349 # object, and store_info() will turn it into a
350 # regular FileNodeInfo if the target is itself a
352 sconsign = t.dir.sconsign()
353 sconsign.set_entry(t.name, sconsign_entry)
357 for t in self.targets:
358 binfo = t.get_binfo()
359 binfo.__class__ = SConfBuildInfo
360 binfo.set_build_result(0, s.getvalue())
361 sconsign_entry = SCons.SConsign.SConsignEntry()
362 sconsign_entry.binfo = binfo
363 #sconsign_entry.ninfo = self.get_ninfo()
364 # We'd like to do this as follows:
365 # t.store_info(binfo)
366 # However, we need to store it as an SConfBuildInfo
367 # object, and store_info() will turn it into a
368 # regular FileNodeInfo if the target is itself a
370 sconsign = t.dir.sconsign()
371 sconsign.set_entry(t.name, sconsign_entry)
375 """This is simply a class to represent a configure context. After
376 creating a SConf object, you can call any tests. After finished with your
377 tests, be sure to call the Finish() method, which returns the modified
379 Some words about caching: In most cases, it is not necessary to cache
380 Test results explicitely. Instead, we use the scons dependency checking
381 mechanism. For example, if one wants to compile a test program
382 (SConf.TryLink), the compiler is only called, if the program dependencies
383 have changed. However, if the program could not be compiled in a former
384 SConf run, we need to explicitely cache this error.
387 def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
388 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
389 """Constructor. Pass additional tests in the custom_tests-dictinary,
390 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
391 defines a custom test.
392 Note also the conf_dir and log_file arguments (you may want to
393 build tests in the VariantDir, not in the SourceDir)
397 SConfFS = SCons.Node.FS.default_fs or \
398 SCons.Node.FS.FS(env.fs.pathTop)
399 if sconf_global is not None:
400 raise SCons.Errors.UserError
402 if log_file is not None:
403 log_file = SConfFS.File(env.subst(log_file))
404 self.logfile = log_file
405 self.logstream = None
406 self.lastTarget = None
408 self.cached = 0 # will be set, if all test results are cached
413 'CheckCXX' : CheckCXX,
414 'CheckSHCC' : CheckSHCC,
415 'CheckSHCXX' : CheckSHCXX,
416 'CheckFunc' : CheckFunc,
417 'CheckType' : CheckType,
418 'CheckTypeSize' : CheckTypeSize,
419 'CheckDeclaration' : CheckDeclaration,
420 'CheckHeader' : CheckHeader,
421 'CheckCHeader' : CheckCHeader,
422 'CheckCXXHeader' : CheckCXXHeader,
423 'CheckLib' : CheckLib,
424 'CheckLibWithHeader' : CheckLibWithHeader,
426 self.AddTests(default_tests)
427 self.AddTests(custom_tests)
428 self.confdir = SConfFS.Dir(env.subst(conf_dir))
429 if config_h is not None:
430 config_h = SConfFS.File(config_h)
431 self.config_h = config_h
435 """Call this method after finished with your tests:
441 def Define(self, name, value = None, comment = None):
443 Define a pre processor symbol name, with the optional given value in the
444 current config header.
446 If value is None (default), then #define name is written. If value is not
447 none, then #define name value is written.
449 comment is a string which will be put as a C comment in the
450 header, to explain the meaning of the value (appropriate C comments /* and
451 */ will be put automatically."""
454 comment_str = "/* %s */" % comment
455 lines.append(comment_str)
457 if value is not None:
458 define_str = "#define %s %s" % (name, value)
460 define_str = "#define %s" % name
461 lines.append(define_str)
464 self.config_h_text = self.config_h_text + '\n'.join(lines)
466 def BuildNodes(self, nodes):
468 Tries to build the given nodes immediately. Returns 1 on success,
471 if self.logstream is not None:
472 # override stdout / stderr to write in log file
473 oldStdout = sys.stdout
474 sys.stdout = self.logstream
475 oldStderr = sys.stderr
476 sys.stderr = self.logstream
478 # the engine assumes the current path is the SConstruct directory ...
479 old_fs_dir = SConfFS.getcwd()
480 old_os_dir = os.getcwd()
481 SConfFS.chdir(SConfFS.Top, change_os_dir=1)
483 # Because we take responsibility here for writing out our
484 # own .sconsign info (see SConfBuildTask.execute(), above),
485 # we override the store_info() method with a null place-holder
486 # so we really control how it gets written.
488 n.store_info = n.do_not_store_info
493 # ToDo: use user options for calc
494 save_max_drift = SConfFS.get_max_drift()
495 SConfFS.set_max_drift(0)
496 tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
497 # we don't want to build tests in parallel
498 jobs = SCons.Job.Jobs(1, tm )
501 state = n.get_state()
502 if (state != SCons.Node.executed and
503 state != SCons.Node.up_to_date):
504 # the node could not be built. we return 0 in this case
507 SConfFS.set_max_drift(save_max_drift)
509 SConfFS.chdir(old_fs_dir, change_os_dir=0)
510 if self.logstream is not None:
511 # restore stdout / stderr
512 sys.stdout = oldStdout
513 sys.stderr = oldStderr
516 def pspawn_wrapper(self, sh, escape, cmd, args, env):
517 """Wrapper function for handling piped spawns.
519 This looks to the calling interface (in Action.py) like a "normal"
520 spawn, but associates the call with the PSPAWN variable from
521 the construction environment and with the streams to which we
522 want the output logged. This gets slid into the construction
523 environment as the SPAWN variable so Action.py doesn't have to
524 know or care whether it's spawning a piped command or not.
526 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
529 def TryBuild(self, builder, text = None, extension = ""):
530 """Low level TryBuild implementation. Normally you don't need to
531 call that - you can use TryCompile / TryLink / TryRun instead
533 global _ac_build_counter
535 # Make sure we have a PSPAWN value, and save the current
538 self.pspawn = self.env['PSPAWN']
540 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
542 save_spawn = self.env['SPAWN']
544 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
548 f = "conftest_" + str(_ac_build_counter)
549 pref = self.env.subst( builder.builder.prefix )
550 suff = self.env.subst( builder.builder.suffix )
551 target = self.confdir.File(pref + f + suff)
554 # Slide our wrapper into the construction environment as
555 # the SPAWN function.
556 self.env['SPAWN'] = self.pspawn_wrapper
557 sourcetext = self.env.Value(text)
560 textFile = self.confdir.File(f + extension)
561 textFileNode = self.env.SConfSourceBuilder(target=textFile,
563 nodesToBeBuilt.extend(textFileNode)
564 source = textFileNode
568 nodes = builder(target = target, source = source)
569 if not SCons.Util.is_List(nodes):
571 nodesToBeBuilt.extend(nodes)
572 result = self.BuildNodes(nodesToBeBuilt)
575 self.env['SPAWN'] = save_spawn
577 _ac_build_counter = _ac_build_counter + 1
579 self.lastTarget = nodes[0]
581 self.lastTarget = None
585 def TryAction(self, action, text = None, extension = ""):
586 """Tries to execute the given action with optional source file
587 contents <text> and optional source file extension <extension>,
588 Returns the status (0 : failed, 1 : ok) and the contents of the
591 builder = SCons.Builder.Builder(action=action)
592 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
593 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
594 del self.env['BUILDERS']['SConfActionBuilder']
596 outputStr = self.lastTarget.get_contents()
597 return (1, outputStr)
600 def TryCompile( self, text, extension):
601 """Compiles the program given in text to an env.Object, using extension
602 as file extension (e.g. '.c'). Returns 1, if compilation was
603 successful, 0 otherwise. The target is saved in self.lastTarget (for
606 return self.TryBuild(self.env.Object, text, extension)
608 def TryLink( self, text, extension ):
609 """Compiles the program given in text to an executable env.Program,
610 using extension as file extension (e.g. '.c'). Returns 1, if
611 compilation was successful, 0 otherwise. The target is saved in
612 self.lastTarget (for further processing).
614 return self.TryBuild(self.env.Program, text, extension )
616 def TryRun(self, text, extension ):
617 """Compiles and runs the program given in text, using extension
618 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
619 (0, '') otherwise. The target (a file containing the program's stdout)
620 is saved in self.lastTarget (for further processing).
622 ok = self.TryLink(text, extension)
624 prog = self.lastTarget
626 output = self.confdir.File(os.path.basename(pname)+'.out')
627 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
628 ok = self.BuildNodes(node)
630 outputStr = output.get_contents()
631 return( 1, outputStr)
635 """A wrapper around Tests (to ensure sanity)"""
636 def __init__(self, test, sconf):
639 def __call__(self, *args, **kw):
640 if not self.sconf.active:
641 raise SCons.Errors.UserError
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: