1 """SCons.SConf
2
3 Autoconf-like configuration support.
4 """
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 __revision__ = "src/engine/SCons/SConf.py 3266 2008/08/12 07:31:01 knight"
30
31 import SCons.compat
32
33 import os
34 import re
35 import string
36 import StringIO
37 import sys
38 import traceback
39 import types
40
41 import SCons.Action
42 import SCons.Builder
43 import SCons.Errors
44 import SCons.Job
45 import SCons.Node.FS
46 import SCons.Taskmaster
47 import SCons.Util
48 import SCons.Warnings
49 import SCons.Conftest
50
51 from SCons.Debug import Trace
52
53
54 SCons.Conftest.LogInputFiles = 0
55 SCons.Conftest.LogErrorMessages = 0
56
57
58 build_type = None
59 build_types = ['clean', 'help']
60
64
65
66 dryrun = 0
67
68 AUTO=0
69 FORCE=1
70 CACHE=2
71 cache_mode = AUTO
72
74 """Set the Configure cache mode. mode must be one of "auto", "force",
75 or "cache"."""
76 global cache_mode
77 if mode == "auto":
78 cache_mode = AUTO
79 elif mode == "force":
80 cache_mode = FORCE
81 elif mode == "cache":
82 cache_mode = CACHE
83 else:
84 raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
85
86 progress_display = SCons.Util.display
91
92 SConfFS = None
93
94 _ac_build_counter = 0
95 _ac_config_logs = {}
96 _ac_config_hs = {}
97 sconf_global = None
98
100 t = open(str(target[0]), "w")
101 defname = re.sub('[^A-Za-z0-9_]', '_', string.upper(str(target[0])))
102 t.write("""#ifndef %(DEFNAME)s_SEEN
103 #define %(DEFNAME)s_SEEN
104
105 """ % {'DEFNAME' : defname})
106 t.write(source[0].get_contents())
107 t.write("""
108 #endif /* %(DEFNAME)s_SEEN */
109 """ % {'DEFNAME' : defname})
110 t.close()
111
113 return "scons: Configure: creating " + str(target[0])
114
125
128 SCons.Warnings.enableWarningClass(SConfWarning)
129
130
134
144
150
151
157 return (str(target[0]) + ' <-\n |' +
158 string.replace( source[0].get_contents(),
159 '\n', "\n |" ) )
160
161
162 BooleanTypes = [types.IntType]
163 if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
164
166 """
167 Special build info for targets of configure tests. Additional members
168 are result (did the builder succeed last time?) and string, which
169 contains messages of the original build phase.
170 """
171 result = None
172 string = None
173
177
178
180 """
181 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
182 """
184 self.orig = orig
185 self.s = StringIO.StringIO()
186
188 if self.orig:
189 self.orig.write(str)
190 self.s.write(str)
191
193 for l in lines:
194 self.write(l + '\n')
195
197 """
198 Return everything written to orig since the Streamer was created.
199 """
200 return self.s.getvalue()
201
203 if self.orig:
204 self.orig.flush()
205 self.s.flush()
206
207
209 """
210 This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
211 correctly and knows about the current cache_mode.
212 """
216
218 """
219 Logs the original builder messages, given the SConfBuildInfo instance
220 bi.
221 """
222 if not isinstance(bi, SConfBuildInfo):
223 SCons.Warnings.warn(SConfWarning,
224 "The stored build information has an unexpected class: %s" % bi.__class__)
225 else:
226 self.display("The original builder output was:\n" +
227 string.replace(" |" + str(bi.string),
228 "\n", "\n |"))
229
231
232
233 exc_type = self.exc_info()[0]
234 if issubclass(exc_type, SConfError):
235 raise
236 elif issubclass(exc_type, SCons.Errors.BuildError):
237
238 pass
239 else:
240 self.display('Caught exception while building "%s":\n' %
241 self.targets[0])
242 try:
243 excepthook = sys.excepthook
244 except AttributeError:
245
246 def excepthook(type, value, tb):
247 traceback.print_tb(tb)
248 print type, value
249 apply(excepthook, self.exc_info())
250 return SCons.Taskmaster.Task.failed(self)
251
286
364
366 """This is simply a class to represent a configure context. After
367 creating a SConf object, you can call any tests. After finished with your
368 tests, be sure to call the Finish() method, which returns the modified
369 environment.
370 Some words about caching: In most cases, it is not necessary to cache
371 Test results explicitely. Instead, we use the scons dependency checking
372 mechanism. For example, if one wants to compile a test program
373 (SConf.TryLink), the compiler is only called, if the program dependencies
374 have changed. However, if the program could not be compiled in a former
375 SConf run, we need to explicitely cache this error.
376 """
377
378 - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
379 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
380 """Constructor. Pass additional tests in the custom_tests-dictinary,
381 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
382 defines a custom test.
383 Note also the conf_dir and log_file arguments (you may want to
384 build tests in the VariantDir, not in the SourceDir)
385 """
386 global SConfFS
387 if not SConfFS:
388 SConfFS = SCons.Node.FS.default_fs or \
389 SCons.Node.FS.FS(env.fs.pathTop)
390 if not sconf_global is None:
391 raise (SCons.Errors.UserError,
392 "Only one SConf object may be active at one time")
393 self.env = env
394 if log_file != None:
395 log_file = SConfFS.File(env.subst(log_file))
396 self.logfile = log_file
397 self.logstream = None
398 self.lastTarget = None
399 self.depth = _depth
400 self.cached = 0
401
402
403 default_tests = {
404 'CheckFunc' : CheckFunc,
405 'CheckType' : CheckType,
406 'CheckTypeSize' : CheckTypeSize,
407 'CheckDeclaration' : CheckDeclaration,
408 'CheckHeader' : CheckHeader,
409 'CheckCHeader' : CheckCHeader,
410 'CheckCXXHeader' : CheckCXXHeader,
411 'CheckLib' : CheckLib,
412 'CheckLibWithHeader' : CheckLibWithHeader,
413 }
414 self.AddTests(default_tests)
415 self.AddTests(custom_tests)
416 self.confdir = SConfFS.Dir(env.subst(conf_dir))
417 if not config_h is None:
418 config_h = SConfFS.File(config_h)
419 self.config_h = config_h
420 self._startup()
421
423 """Call this method after finished with your tests:
424 env = sconf.Finish()
425 """
426 self._shutdown()
427 return self.env
428
429 - def Define(self, name, value = None, comment = None):
430 """
431 Define a pre processor symbol name, with the optional given value in the
432 current config header.
433
434 If value is None (default), then #define name is written. If value is not
435 none, then #define name value is written.
436
437 comment is a string which will be put as a C comment in the
438 header, to explain the meaning of the value (appropriate C comments /* and
439 */ will be put automatically."""
440 lines = []
441 if comment:
442 comment_str = "/* %s */" % comment
443 lines.append(comment_str)
444
445 if value is not None:
446 define_str = "#define %s %s" % (name, value)
447 else:
448 define_str = "#define %s" % name
449 lines.append(define_str)
450 lines.append('')
451
452 self.config_h_text = self.config_h_text + string.join(lines, '\n')
453
503
505 """Wrapper function for handling piped spawns.
506
507 This looks to the calling interface (in Action.py) like a "normal"
508 spawn, but associates the call with the PSPAWN variable from
509 the construction environment and with the streams to which we
510 want the output logged. This gets slid into the construction
511 environment as the SPAWN variable so Action.py doesn't have to
512 know or care whether it's spawning a piped command or not.
513 """
514 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
515
516
517 - def TryBuild(self, builder, text = None, extension = ""):
518 """Low level TryBuild implementation. Normally you don't need to
519 call that - you can use TryCompile / TryLink / TryRun instead
520 """
521 global _ac_build_counter
522
523
524
525 try:
526 self.pspawn = self.env['PSPAWN']
527 except KeyError:
528 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
529 try:
530 save_spawn = self.env['SPAWN']
531 except KeyError:
532 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
533
534 nodesToBeBuilt = []
535
536 f = "conftest_" + str(_ac_build_counter)
537 pref = self.env.subst( builder.builder.prefix )
538 suff = self.env.subst( builder.builder.suffix )
539 target = self.confdir.File(pref + f + suff)
540
541 try:
542
543
544 self.env['SPAWN'] = self.pspawn_wrapper
545 sourcetext = self.env.Value(text)
546
547 if text != None:
548 textFile = self.confdir.File(f + extension)
549 textFileNode = self.env.SConfSourceBuilder(target=textFile,
550 source=sourcetext)
551 nodesToBeBuilt.extend(textFileNode)
552 source = textFileNode
553 else:
554 source = None
555
556 nodes = builder(target = target, source = source)
557 if not SCons.Util.is_List(nodes):
558 nodes = [nodes]
559 nodesToBeBuilt.extend(nodes)
560 result = self.BuildNodes(nodesToBeBuilt)
561
562 finally:
563 self.env['SPAWN'] = save_spawn
564
565 _ac_build_counter = _ac_build_counter + 1
566 if result:
567 self.lastTarget = nodes[0]
568 else:
569 self.lastTarget = None
570
571 return result
572
573 - def TryAction(self, action, text = None, extension = ""):
574 """Tries to execute the given action with optional source file
575 contents <text> and optional source file extension <extension>,
576 Returns the status (0 : failed, 1 : ok) and the contents of the
577 output file.
578 """
579 builder = SCons.Builder.Builder(action=action)
580 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
581 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
582 del self.env['BUILDERS']['SConfActionBuilder']
583 if ok:
584 outputStr = self.lastTarget.get_contents()
585 return (1, outputStr)
586 return (0, "")
587
589 """Compiles the program given in text to an env.Object, using extension
590 as file extension (e.g. '.c'). Returns 1, if compilation was
591 successful, 0 otherwise. The target is saved in self.lastTarget (for
592 further processing).
593 """
594 return self.TryBuild(self.env.Object, text, extension)
595
596 - def TryLink( self, text, extension ):
597 """Compiles the program given in text to an executable env.Program,
598 using extension as file extension (e.g. '.c'). Returns 1, if
599 compilation was successful, 0 otherwise. The target is saved in
600 self.lastTarget (for further processing).
601 """
602 return self.TryBuild(self.env.Program, text, extension )
603
604 - def TryRun(self, text, extension ):
605 """Compiles and runs the program given in text, using extension
606 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
607 (0, '') otherwise. The target (a file containing the program's stdout)
608 is saved in self.lastTarget (for further processing).
609 """
610 ok = self.TryLink(text, extension)
611 if( ok ):
612 prog = self.lastTarget
613 pname = str(prog)
614 output = SConfFS.File(pname+'.out')
615 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
616 ok = self.BuildNodes(node)
617 if ok:
618 outputStr = output.get_contents()
619 return( 1, outputStr)
620 return (0, "")
621
623 """A wrapper around Tests (to ensure sanity)"""
625 self.test = test
626 self.sconf = sconf
628 if not self.sconf.active:
629 raise (SCons.Errors.UserError,
630 "Test called after sconf.Finish()")
631 context = CheckContext(self.sconf)
632 ret = apply(self.test, (context,) + args, kw)
633 if not self.sconf.config_h is None:
634 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
635 context.Result("error: no result")
636 return ret
637
638 - def AddTest(self, test_name, test_instance):
639 """Adds test_class to this SConf instance. It can be called with
640 self.test_name(...)"""
641 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
642
644 """Adds all the tests given in the tests dictionary to this SConf
645 instance
646 """
647 for name in tests.keys():
648 self.AddTest(name, tests[name])
649
659
705
707 """Private method. Reset to non-piped spawn"""
708 global sconf_global, _ac_config_hs
709
710 if not self.active:
711 raise SCons.Errors.UserError, "Finish may be called only once!"
712 if self.logstream != None and not dryrun:
713 self.logstream.write("\n")
714 self.logstream.close()
715 self.logstream = None
716
717 blds = self.env['BUILDERS']
718 del blds['SConfSourceBuilder']
719 self.env.Replace( BUILDERS=blds )
720 self.active = 0
721 sconf_global = None
722 if not self.config_h is None:
723 _ac_config_hs[self.config_h] = self.config_h_text
724 self.env.fs = self.lastEnvFs
725
727 """Provides a context for configure tests. Defines how a test writes to the
728 screen and log file.
729
730 A typical test is just a callable with an instance of CheckContext as
731 first argument:
732
733 def CheckCustom(context, ...)
734 context.Message('Checking my weird test ... ')
735 ret = myWeirdTestFunction(...)
736 context.Result(ret)
737
738 Often, myWeirdTestFunction will be one of
739 context.TryCompile/context.TryLink/context.TryRun. The results of
740 those are cached, for they are only rebuild, if the dependencies have
741 changed.
742 """
743
744 - def __init__(self, sconf):
745 """Constructor. Pass the corresponding SConf instance."""
746 self.sconf = sconf
747 self.did_show_result = 0
748
749
750 self.vardict = {}
751 self.havedict = {}
752 self.headerfilename = None
753 self.config_h = ""
754
755
756
757
758
759
760
761
762 - def Message(self, text):
763 """Inform about what we are doing right now, e.g.
764 'Checking for SOMETHING ... '
765 """
766 self.Display(text)
767 self.sconf.cached = 1
768 self.did_show_result = 0
769
770 - def Result(self, res):
771 """Inform about the result of the test. res may be an integer or a
772 string. In case of an integer, the written text will be 'ok' or
773 'failed'.
774 The result is only displayed when self.did_show_result is not set.
775 """
776 if type(res) in BooleanTypes:
777 if res:
778 text = "yes"
779 else:
780 text = "no"
781 elif type(res) == types.StringType:
782 text = res
783 else:
784 raise TypeError, "Expected string, int or bool, got " + str(type(res))
785
786 if self.did_show_result == 0:
787
788 self.Display(text + "\n")
789 self.did_show_result = 1
790
791 - def TryBuild(self, *args, **kw):
792 return apply(self.sconf.TryBuild, args, kw)
793
794 - def TryAction(self, *args, **kw):
795 return apply(self.sconf.TryAction, args, kw)
796
797 - def TryCompile(self, *args, **kw):
798 return apply(self.sconf.TryCompile, args, kw)
799
800 - def TryLink(self, *args, **kw):
801 return apply(self.sconf.TryLink, args, kw)
802
803 - def TryRun(self, *args, **kw):
804 return apply(self.sconf.TryRun, args, kw)
805
806 - def __getattr__( self, attr ):
807 if( attr == 'env' ):
808 return self.sconf.env
809 elif( attr == 'lastTarget' ):
810 return self.sconf.lastTarget
811 else:
812 raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
813
814
815
816 - def BuildProg(self, text, ext):
817 self.sconf.cached = 1
818
819 return not self.TryBuild(self.env.Program, text, ext)
820
821 - def CompileProg(self, text, ext):
822 self.sconf.cached = 1
823
824 return not self.TryBuild(self.env.Object, text, ext)
825
826 - def RunProg(self, text, ext):
827 self.sconf.cached = 1
828
829 st, out = self.TryRun(text, ext)
830 return not st, out
831
832 - def AppendLIBS(self, lib_name_list):
833 oldLIBS = self.env.get( 'LIBS', [] )
834 self.env.Append(LIBS = lib_name_list)
835 return oldLIBS
836
837 - def SetLIBS(self, val):
838 oldLIBS = self.env.get( 'LIBS', [] )
839 self.env.Replace(LIBS = val)
840 return oldLIBS
841
842 - def Display(self, msg):
843 if self.sconf.cached:
844
845
846
847 msg = "(cached) " + msg
848 self.sconf.cached = 0
849 progress_display(msg, append_newline=0)
850 self.Log("scons: Configure: " + msg + "\n")
851
852 - def Log(self, msg):
853 if self.sconf.logstream != None:
854 self.sconf.logstream.write(msg)
855
856
857
858
870
871
872 -def CheckFunc(context, function_name, header = None, language = None):
876
877 -def CheckType(context, type_name, includes = "", language = None):
878 res = SCons.Conftest.CheckType(context, type_name,
879 header = includes, language = language)
880 context.did_show_result = 1
881 return not res
882
883 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
884 res = SCons.Conftest.CheckTypeSize(context, type_name,
885 header = includes, language = language,
886 expect = expect)
887 context.did_show_result = 1
888 return res
889
891 res = SCons.Conftest.CheckDeclaration(context, declaration,
892 includes = includes,
893 language = language)
894 context.did_show_result = 1
895 return not res
896
898
899
900 if not SCons.Util.is_List(headers):
901 headers = [headers]
902 l = []
903 if leaveLast:
904 lastHeader = headers[-1]
905 headers = headers[:-1]
906 else:
907 lastHeader = None
908 for s in headers:
909 l.append("#include %s%s%s\n"
910 % (include_quotes[0], s, include_quotes[1]))
911 return string.join(l, ''), lastHeader
912
914 """
915 A test for a C or C++ header file.
916 """
917 prog_prefix, hdr_to_check = \
918 createIncludesFromHeaders(header, 1, include_quotes)
919 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
920 language = language,
921 include_quotes = include_quotes)
922 context.did_show_result = 1
923 return not res
924
925
926
928 """
929 A test for a C header file.
930 """
931 return CheckHeader(context, header, include_quotes, language = "C")
932
933
934
935
937 """
938 A test for a C++ header file.
939 """
940 return CheckHeader(context, header, include_quotes, language = "C++")
941
942
943 -def CheckLib(context, library = None, symbol = "main",
944 header = None, language = None, autoadd = 1):
945 """
946 A test for a library. See also CheckLibWithHeader.
947 Note that library may also be None to test whether the given symbol
948 compiles without flags.
949 """
950
951 if library == []:
952 library = [None]
953
954 if not SCons.Util.is_List(library):
955 library = [library]
956
957
958 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
959 language = language, autoadd = autoadd)
960 context.did_show_result = 1
961 return not res
962
963
964
965
968
969 """
970 Another (more sophisticated) test for a library.
971 Checks, if library and header is available for language (may be 'C'
972 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
973 As in CheckLib, we support library=None, to test if the call compiles
974 without extra link flags.
975 """
976 prog_prefix, dummy = \
977 createIncludesFromHeaders(header, 0)
978 if libs == []:
979 libs = [None]
980
981 if not SCons.Util.is_List(libs):
982 libs = [libs]
983
984 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
985 call = call, language = language, autoadd = autoadd)
986 context.did_show_result = 1
987 return not res
988