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__"
43 import SCons.Taskmaster
48 from SCons.Debug import Trace
50 # Turn off the Conftest error logging
51 SCons.Conftest.LogInputFiles = 0
52 SCons.Conftest.LogErrorMessages = 0
56 build_types = ['clean', 'help']
58 def SetBuildType(type):
62 # to be set, if we are in dry-run mode
65 AUTO=0 # use SCons dependency scanning for up-to-date checks
66 FORCE=1 # force all tests to be rebuilt
67 CACHE=2 # force all tests to be taken from cache (raise an error, if necessary)
70 def SetCacheMode(mode):
71 """Set the Configure cache mode. mode must be one of "auto", "force",
81 raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
83 progress_display = SCons.Util.display # will be overwritten by SCons.Script
84 def SetProgressDisplay(display):
85 """Set the progress display to use (called from SCons.Script)"""
86 global progress_display
87 progress_display = display
91 _ac_build_counter = 0 # incremented, whenever TryBuild is called
92 _ac_config_logs = {} # all config.log files created in this build
93 _ac_config_hs = {} # all config.h files created in this build
94 sconf_global = None # current sconf object
96 def _createConfigH(target, source, env):
97 t = open(str(target[0]), "w")
98 defname = re.sub('[^A-Za-z0-9_]', '_', str(target[0]).upper())
99 t.write("""#ifndef %(DEFNAME)s_SEEN
100 #define %(DEFNAME)s_SEEN
102 """ % {'DEFNAME' : defname})
103 t.write(source[0].get_contents())
105 #endif /* %(DEFNAME)s_SEEN */
106 """ % {'DEFNAME' : defname})
109 def _stringConfigH(target, source, env):
110 return "scons: Configure: creating " + str(target[0])
112 def CreateConfigHBuilder(env):
113 """Called just before the building targets phase begins."""
114 if len(_ac_config_hs) == 0:
116 action = SCons.Action.Action(_createConfigH,
118 sconfigHBld = SCons.Builder.Builder(action=action)
119 env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} )
120 for k in _ac_config_hs.keys():
121 env.SConfigHBuilder(k, env.Value(_ac_config_hs[k]))
123 class SConfWarning(SCons.Warnings.Warning):
125 SCons.Warnings.enableWarningClass(SConfWarning)
127 # some error definitions
128 class SConfError(SCons.Errors.UserError):
129 def __init__(self,msg):
130 SCons.Errors.UserError.__init__(self,msg)
132 class ConfigureDryRunError(SConfError):
133 """Raised when a file or directory needs to be updated during a Configure
134 process, but the user requested a dry-run"""
135 def __init__(self,target):
136 if not isinstance(target, SCons.Node.FS.File):
137 msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target)
139 msg = 'Cannot update configure test "%s" within a dry-run.' % str(target)
140 SConfError.__init__(self,msg)
142 class ConfigureCacheError(SConfError):
143 """Raised when a use explicitely requested the cache feature, but the test
144 is run the first time."""
145 def __init__(self,target):
146 SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target))
148 # define actions for building text files
149 def _createSource( target, source, env ):
150 fd = open(str(target[0]), "w")
151 fd.write(source[0].get_contents())
153 def _stringSource( target, source, env ):
154 return (str(target[0]) + ' <-\n |' +
155 source[0].get_contents().replace( '\n', "\n |" ) )
157 # python 2.2 introduces types.BooleanType
158 BooleanTypes = [types.IntType]
159 if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
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 = StringIO.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,
401 "Only one SConf object may be active at one time")
403 if log_file is not None:
404 log_file = SConfFS.File(env.subst(log_file))
405 self.logfile = log_file
406 self.logstream = None
407 self.lastTarget = None
409 self.cached = 0 # will be set, if all test results are cached
414 'CheckCXX' : CheckCXX,
415 'CheckSHCC' : CheckSHCC,
416 'CheckSHCXX' : CheckSHCXX,
417 'CheckFunc' : CheckFunc,
418 'CheckType' : CheckType,
419 'CheckTypeSize' : CheckTypeSize,
420 'CheckDeclaration' : CheckDeclaration,
421 'CheckHeader' : CheckHeader,
422 'CheckCHeader' : CheckCHeader,
423 'CheckCXXHeader' : CheckCXXHeader,
424 'CheckLib' : CheckLib,
425 'CheckLibWithHeader' : CheckLibWithHeader,
427 self.AddTests(default_tests)
428 self.AddTests(custom_tests)
429 self.confdir = SConfFS.Dir(env.subst(conf_dir))
430 if config_h is not None:
431 config_h = SConfFS.File(config_h)
432 self.config_h = config_h
436 """Call this method after finished with your tests:
442 def Define(self, name, value = None, comment = None):
444 Define a pre processor symbol name, with the optional given value in the
445 current config header.
447 If value is None (default), then #define name is written. If value is not
448 none, then #define name value is written.
450 comment is a string which will be put as a C comment in the
451 header, to explain the meaning of the value (appropriate C comments /* and
452 */ will be put automatically."""
455 comment_str = "/* %s */" % comment
456 lines.append(comment_str)
458 if value is not None:
459 define_str = "#define %s %s" % (name, value)
461 define_str = "#define %s" % name
462 lines.append(define_str)
465 self.config_h_text = self.config_h_text + '\n'.join(lines)
467 def BuildNodes(self, nodes):
469 Tries to build the given nodes immediately. Returns 1 on success,
472 if self.logstream is not None:
473 # override stdout / stderr to write in log file
474 oldStdout = sys.stdout
475 sys.stdout = self.logstream
476 oldStderr = sys.stderr
477 sys.stderr = self.logstream
479 # the engine assumes the current path is the SConstruct directory ...
480 old_fs_dir = SConfFS.getcwd()
481 old_os_dir = os.getcwd()
482 SConfFS.chdir(SConfFS.Top, change_os_dir=1)
484 # Because we take responsibility here for writing out our
485 # own .sconsign info (see SConfBuildTask.execute(), above),
486 # we override the store_info() method with a null place-holder
487 # so we really control how it gets written.
489 n.store_info = n.do_not_store_info
494 # ToDo: use user options for calc
495 save_max_drift = SConfFS.get_max_drift()
496 SConfFS.set_max_drift(0)
497 tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
498 # we don't want to build tests in parallel
499 jobs = SCons.Job.Jobs(1, tm )
502 state = n.get_state()
503 if (state != SCons.Node.executed and
504 state != SCons.Node.up_to_date):
505 # the node could not be built. we return 0 in this case
508 SConfFS.set_max_drift(save_max_drift)
510 SConfFS.chdir(old_fs_dir, change_os_dir=0)
511 if self.logstream is not None:
512 # restore stdout / stderr
513 sys.stdout = oldStdout
514 sys.stderr = oldStderr
517 def pspawn_wrapper(self, sh, escape, cmd, args, env):
518 """Wrapper function for handling piped spawns.
520 This looks to the calling interface (in Action.py) like a "normal"
521 spawn, but associates the call with the PSPAWN variable from
522 the construction environment and with the streams to which we
523 want the output logged. This gets slid into the construction
524 environment as the SPAWN variable so Action.py doesn't have to
525 know or care whether it's spawning a piped command or not.
527 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
530 def TryBuild(self, builder, text = None, extension = ""):
531 """Low level TryBuild implementation. Normally you don't need to
532 call that - you can use TryCompile / TryLink / TryRun instead
534 global _ac_build_counter
536 # Make sure we have a PSPAWN value, and save the current
539 self.pspawn = self.env['PSPAWN']
541 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
543 save_spawn = self.env['SPAWN']
545 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
549 f = "conftest_" + str(_ac_build_counter)
550 pref = self.env.subst( builder.builder.prefix )
551 suff = self.env.subst( builder.builder.suffix )
552 target = self.confdir.File(pref + f + suff)
555 # Slide our wrapper into the construction environment as
556 # the SPAWN function.
557 self.env['SPAWN'] = self.pspawn_wrapper
558 sourcetext = self.env.Value(text)
561 textFile = self.confdir.File(f + extension)
562 textFileNode = self.env.SConfSourceBuilder(target=textFile,
564 nodesToBeBuilt.extend(textFileNode)
565 source = textFileNode
569 nodes = builder(target = target, source = source)
570 if not SCons.Util.is_List(nodes):
572 nodesToBeBuilt.extend(nodes)
573 result = self.BuildNodes(nodesToBeBuilt)
576 self.env['SPAWN'] = save_spawn
578 _ac_build_counter = _ac_build_counter + 1
580 self.lastTarget = nodes[0]
582 self.lastTarget = None
586 def TryAction(self, action, text = None, extension = ""):
587 """Tries to execute the given action with optional source file
588 contents <text> and optional source file extension <extension>,
589 Returns the status (0 : failed, 1 : ok) and the contents of the
592 builder = SCons.Builder.Builder(action=action)
593 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
594 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
595 del self.env['BUILDERS']['SConfActionBuilder']
597 outputStr = self.lastTarget.get_contents()
598 return (1, outputStr)
601 def TryCompile( self, text, extension):
602 """Compiles the program given in text to an env.Object, using extension
603 as file extension (e.g. '.c'). Returns 1, if compilation was
604 successful, 0 otherwise. The target is saved in self.lastTarget (for
607 return self.TryBuild(self.env.Object, text, extension)
609 def TryLink( self, text, extension ):
610 """Compiles the program given in text to an executable env.Program,
611 using extension as file extension (e.g. '.c'). Returns 1, if
612 compilation was successful, 0 otherwise. The target is saved in
613 self.lastTarget (for further processing).
615 return self.TryBuild(self.env.Program, text, extension )
617 def TryRun(self, text, extension ):
618 """Compiles and runs the program given in text, using extension
619 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
620 (0, '') otherwise. The target (a file containing the program's stdout)
621 is saved in self.lastTarget (for further processing).
623 ok = self.TryLink(text, extension)
625 prog = self.lastTarget
627 output = self.confdir.File(os.path.basename(pname)+'.out')
628 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
629 ok = self.BuildNodes(node)
631 outputStr = output.get_contents()
632 return( 1, outputStr)
636 """A wrapper around Tests (to ensure sanity)"""
637 def __init__(self, test, sconf):
640 def __call__(self, *args, **kw):
641 if not self.sconf.active:
642 raise (SCons.Errors.UserError,
643 "Test called after sconf.Finish()")
644 context = CheckContext(self.sconf)
645 ret = self.test(context, *args, **kw)
646 if self.sconf.config_h is not None:
647 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
648 context.Result("error: no result")
651 def AddTest(self, test_name, test_instance):
652 """Adds test_class to this SConf instance. It can be called with
653 self.test_name(...)"""
654 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
656 def AddTests(self, tests):
657 """Adds all the tests given in the tests dictionary to this SConf
660 for name in tests.keys():
661 self.AddTest(name, tests[name])
663 def _createDir( self, node ):
666 if not os.path.isdir( dirName ):
667 raise ConfigureDryRunError(dirName)
669 if not os.path.isdir( dirName ):
670 os.makedirs( dirName )
674 """Private method. Set up logstream, and set the environment
675 variables necessary for a piped build
677 global _ac_config_logs
681 self.lastEnvFs = self.env.fs
682 self.env.fs = SConfFS
683 self._createDir(self.confdir)
684 self.confdir.up().add_ignore( [self.confdir] )
686 if self.logfile is not None and not dryrun:
687 # truncate logfile, if SConf.Configure is called for the first time
689 if self.logfile in _ac_config_logs:
692 _ac_config_logs[self.logfile] = None
694 fp = open(str(self.logfile), log_mode)
695 self.logstream = SCons.Util.Unbuffered(fp)
696 # logfile may stay in a build directory, so we tell
697 # the build system not to override it with a eventually
698 # existing file with the same name in the source directory
699 self.logfile.dir.add_ignore( [self.logfile] )
701 tb = traceback.extract_stack()[-3-self.depth]
702 old_fs_dir = SConfFS.getcwd()
703 SConfFS.chdir(SConfFS.Top, change_os_dir=0)
704 self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' %
705 (tb[0], tb[1], str(self.confdir)) )
706 SConfFS.chdir(old_fs_dir)
708 self.logstream = None
709 # we use a special builder to create source files from TEXT
710 action = SCons.Action.Action(_createSource,
712 sconfSrcBld = SCons.Builder.Builder(action=action)
713 self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} )
714 self.config_h_text = _ac_config_hs.get(self.config_h, "")
716 # only one SConf instance should be active at a time ...
720 """Private method. Reset to non-piped spawn"""
721 global sconf_global, _ac_config_hs
724 raise SCons.Errors.UserError, "Finish may be called only once!"
725 if self.logstream is not None and not dryrun:
726 self.logstream.write("\n")
727 self.logstream.close()
728 self.logstream = None
729 # remove the SConfSourceBuilder from the environment
730 blds = self.env['BUILDERS']
731 del blds['SConfSourceBuilder']
732 self.env.Replace( BUILDERS=blds )
735 if not self.config_h is None:
736 _ac_config_hs[self.config_h] = self.config_h_text
737 self.env.fs = self.lastEnvFs
740 """Provides a context for configure tests. Defines how a test writes to the
743 A typical test is just a callable with an instance of CheckContext as
746 def CheckCustom(context, ...)
747 context.Message('Checking my weird test ... ')
748 ret = myWeirdTestFunction(...)
751 Often, myWeirdTestFunction will be one of
752 context.TryCompile/context.TryLink/context.TryRun. The results of
753 those are cached, for they are only rebuild, if the dependencies have
757 def __init__(self, sconf):
758 """Constructor. Pass the corresponding SConf instance."""
760 self.did_show_result = 0
765 self.headerfilename = None
766 self.config_h = "" # config_h text will be stored here
767 # we don't regenerate the config.h file after each test. That means,
768 # that tests won't be able to include the config.h file, and so
769 # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major
770 # issue, though. If it turns out, that we need to include config.h
771 # in tests, we must ensure, that the dependencies are worked out
772 # correctly. Note that we can't use Conftest.py's support for config.h,
773 # cause we will need to specify a builder for the config.h file ...
775 def Message(self, text):
776 """Inform about what we are doing right now, e.g.
777 'Checking for SOMETHING ... '
780 self.sconf.cached = 1
781 self.did_show_result = 0
783 def Result(self, res):
784 """Inform about the result of the test. res may be an integer or a
785 string. In case of an integer, the written text will be 'yes' or 'no'.
786 The result is only displayed when self.did_show_result is not set.
788 if type(res) in BooleanTypes:
793 elif type(res) == types.StringType:
796 raise TypeError, "Expected string, int or bool, got " + str(type(res))
798 if self.did_show_result == 0:
799 # Didn't show result yet, do it now.
800 self.Display(text + "\n")
801 self.did_show_result = 1
803 def TryBuild(self, *args, **kw):
804 return self.sconf.TryBuild(*args, **kw)
806 def TryAction(self, *args, **kw):
807 return self.sconf.TryAction(*args, **kw)
809 def TryCompile(self, *args, **kw):
810 return self.sconf.TryCompile(*args, **kw)
812 def TryLink(self, *args, **kw):
813 return self.sconf.TryLink(*args, **kw)
815 def TryRun(self, *args, **kw):
816 return self.sconf.TryRun(*args, **kw)
818 def __getattr__( self, attr ):
820 return self.sconf.env
821 elif( attr == 'lastTarget' ):
822 return self.sconf.lastTarget
824 raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
826 #### Stuff used by Conftest.py (look there for explanations).
828 def BuildProg(self, text, ext):
829 self.sconf.cached = 1
830 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
831 return not self.TryBuild(self.env.Program, text, ext)
833 def CompileProg(self, text, ext):
834 self.sconf.cached = 1
835 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
836 return not self.TryBuild(self.env.Object, text, ext)
838 def CompileSharedObject(self, text, ext):
839 self.sconf.cached = 1
840 # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc.
841 return not self.TryBuild(self.env.SharedObject, text, ext)
843 def RunProg(self, text, ext):
844 self.sconf.cached = 1
845 # TODO: should use self.vardict for $CC, $CPPFLAGS, etc.
846 st, out = self.TryRun(text, ext)
849 def AppendLIBS(self, lib_name_list):
850 oldLIBS = self.env.get( 'LIBS', [] )
851 self.env.Append(LIBS = lib_name_list)
854 def PrependLIBS(self, lib_name_list):
855 oldLIBS = self.env.get( 'LIBS', [] )
856 self.env.Prepend(LIBS = lib_name_list)
859 def SetLIBS(self, val):
860 oldLIBS = self.env.get( 'LIBS', [] )
861 self.env.Replace(LIBS = val)
864 def Display(self, msg):
865 if self.sconf.cached:
866 # We assume that Display is called twice for each test here
867 # once for the Checking for ... message and once for the result.
868 # The self.sconf.cached flag can only be set between those calls
869 msg = "(cached) " + msg
870 self.sconf.cached = 0
871 progress_display(msg, append_newline=0)
872 self.Log("scons: Configure: " + msg + "\n")
875 if self.sconf.logstream is not None:
876 self.sconf.logstream.write(msg)
878 #### End of stuff used by Conftest.py.
881 def SConf(*args, **kw):
882 if kw.get(build_type, True):
883 kw['_depth'] = kw.get('_depth', 0) + 1
884 for bt in build_types:
889 return SConfBase(*args, **kw)
891 return SCons.Util.Null()
894 def CheckFunc(context, function_name, header = None, language = None):
895 res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language)
896 context.did_show_result = 1
899 def CheckType(context, type_name, includes = "", language = None):
900 res = SCons.Conftest.CheckType(context, type_name,
901 header = includes, language = language)
902 context.did_show_result = 1
905 def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
906 res = SCons.Conftest.CheckTypeSize(context, type_name,
907 header = includes, language = language,
909 context.did_show_result = 1
912 def CheckDeclaration(context, declaration, includes = "", language = None):
913 res = SCons.Conftest.CheckDeclaration(context, declaration,
916 context.did_show_result = 1
919 def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'):
920 # used by CheckHeader and CheckLibWithHeader to produce C - #include
921 # statements from the specified header (list)
922 if not SCons.Util.is_List(headers):
926 lastHeader = headers[-1]
927 headers = headers[:-1]
931 l.append("#include %s%s%s\n"
932 % (include_quotes[0], s, include_quotes[1]))
933 return ''.join(l), lastHeader
935 def CheckHeader(context, header, include_quotes = '<>', language = None):
937 A test for a C or C++ header file.
939 prog_prefix, hdr_to_check = \
940 createIncludesFromHeaders(header, 1, include_quotes)
941 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
943 include_quotes = include_quotes)
944 context.did_show_result = 1
947 def CheckCC(context):
948 res = SCons.Conftest.CheckCC(context)
949 context.did_show_result = 1
952 def CheckCXX(context):
953 res = SCons.Conftest.CheckCXX(context)
954 context.did_show_result = 1
957 def CheckSHCC(context):
958 res = SCons.Conftest.CheckSHCC(context)
959 context.did_show_result = 1
962 def CheckSHCXX(context):
963 res = SCons.Conftest.CheckSHCXX(context)
964 context.did_show_result = 1
967 # Bram: Make this function obsolete? CheckHeader() is more generic.
969 def CheckCHeader(context, header, include_quotes = '""'):
971 A test for a C header file.
973 return CheckHeader(context, header, include_quotes, language = "C")
976 # Bram: Make this function obsolete? CheckHeader() is more generic.
978 def CheckCXXHeader(context, header, include_quotes = '""'):
980 A test for a C++ header file.
982 return CheckHeader(context, header, include_quotes, language = "C++")
985 def CheckLib(context, library = None, symbol = "main",
986 header = None, language = None, autoadd = 1):
988 A test for a library. See also CheckLibWithHeader.
989 Note that library may also be None to test whether the given symbol
990 compiles without flags.
996 if not SCons.Util.is_List(library):
999 # ToDo: accept path for the library
1000 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
1001 language = language, autoadd = autoadd)
1002 context.did_show_result = 1
1006 # Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H.
1008 def CheckLibWithHeader(context, libs, header, language,
1009 call = None, autoadd = 1):
1010 # ToDo: accept path for library. Support system header files.
1012 Another (more sophisticated) test for a library.
1013 Checks, if library and header is available for language (may be 'C'
1014 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
1015 As in CheckLib, we support library=None, to test if the call compiles
1016 without extra link flags.
1018 prog_prefix, dummy = \
1019 createIncludesFromHeaders(header, 0)
1023 if not SCons.Util.is_List(libs):
1026 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1027 call = call, language = language, autoadd = autoadd)
1028 context.did_show_result = 1
1033 # indent-tabs-mode:nil
1035 # vim: set expandtab tabstop=4 shiftwidth=4: