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 3842 2008/12/20 22:59:52 scons"
30
31 import os
32 import re
33 import string
34 import StringIO
35 import sys
36 import traceback
37 import types
38
39 import SCons.Action
40 import SCons.Builder
41 import SCons.Errors
42 import SCons.Job
43 import SCons.Node.FS
44 import SCons.Taskmaster
45 import SCons.Util
46 import SCons.Warnings
47 import SCons.Conftest
48
49 from SCons.Debug import Trace
50
51
52 SCons.Conftest.LogInputFiles = 0
53 SCons.Conftest.LogErrorMessages = 0
54
55
56 build_type = None
57 build_types = ['clean', 'help']
58
62
63
64 dryrun = 0
65
66 AUTO=0
67 FORCE=1
68 CACHE=2
69 cache_mode = AUTO
70
72 """Set the Configure cache mode. mode must be one of "auto", "force",
73 or "cache"."""
74 global cache_mode
75 if mode == "auto":
76 cache_mode = AUTO
77 elif mode == "force":
78 cache_mode = FORCE
79 elif mode == "cache":
80 cache_mode = CACHE
81 else:
82 raise ValueError, "SCons.SConf.SetCacheMode: Unknown mode " + mode
83
84 progress_display = SCons.Util.display
89
90 SConfFS = None
91
92 _ac_build_counter = 0
93 _ac_config_logs = {}
94 _ac_config_hs = {}
95 sconf_global = None
96
98 t = open(str(target[0]), "w")
99 defname = re.sub('[^A-Za-z0-9_]', '_', string.upper(str(target[0])))
100 t.write("""#ifndef %(DEFNAME)s_SEEN
101 #define %(DEFNAME)s_SEEN
102
103 """ % {'DEFNAME' : defname})
104 t.write(source[0].get_contents())
105 t.write("""
106 #endif /* %(DEFNAME)s_SEEN */
107 """ % {'DEFNAME' : defname})
108 t.close()
109
111 return "scons: Configure: creating " + str(target[0])
112
123
126 SCons.Warnings.enableWarningClass(SConfWarning)
127
128
132
142
148
149
155 return (str(target[0]) + ' <-\n |' +
156 string.replace( source[0].get_contents(),
157 '\n', "\n |" ) )
158
159
160 BooleanTypes = [types.IntType]
161 if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType)
162
164 """
165 Special build info for targets of configure tests. Additional members
166 are result (did the builder succeed last time?) and string, which
167 contains messages of the original build phase.
168 """
169 result = None
170 string = None
171
175
176
178 """
179 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
180 """
182 self.orig = orig
183 self.s = StringIO.StringIO()
184
186 if self.orig:
187 self.orig.write(str)
188 self.s.write(str)
189
191 for l in lines:
192 self.write(l + '\n')
193
195 """
196 Return everything written to orig since the Streamer was created.
197 """
198 return self.s.getvalue()
199
201 if self.orig:
202 self.orig.flush()
203 self.s.flush()
204
205
207 """
208 This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
209 correctly and knows about the current cache_mode.
210 """
214
216 """
217 Logs the original builder messages, given the SConfBuildInfo instance
218 bi.
219 """
220 if not isinstance(bi, SConfBuildInfo):
221 SCons.Warnings.warn(SConfWarning,
222 "The stored build information has an unexpected class: %s" % bi.__class__)
223 else:
224 self.display("The original builder output was:\n" +
225 string.replace(" |" + str(bi.string),
226 "\n", "\n |"))
227
229
230
231 exc_type = self.exc_info()[0]
232 if issubclass(exc_type, SConfError):
233 raise
234 elif issubclass(exc_type, SCons.Errors.BuildError):
235
236
237
238 self.exc_clear()
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 'CheckCC' : CheckCC,
405 'CheckCXX' : CheckCXX,
406 'CheckSHCC' : CheckSHCC,
407 'CheckSHCXX' : CheckSHCXX,
408 'CheckFunc' : CheckFunc,
409 'CheckType' : CheckType,
410 'CheckTypeSize' : CheckTypeSize,
411 'CheckDeclaration' : CheckDeclaration,
412 'CheckHeader' : CheckHeader,
413 'CheckCHeader' : CheckCHeader,
414 'CheckCXXHeader' : CheckCXXHeader,
415 'CheckLib' : CheckLib,
416 'CheckLibWithHeader' : CheckLibWithHeader,
417 }
418 self.AddTests(default_tests)
419 self.AddTests(custom_tests)
420 self.confdir = SConfFS.Dir(env.subst(conf_dir))
421 if not config_h is None:
422 config_h = SConfFS.File(config_h)
423 self.config_h = config_h
424 self._startup()
425
427 """Call this method after finished with your tests:
428 env = sconf.Finish()
429 """
430 self._shutdown()
431 return self.env
432
433 - def Define(self, name, value = None, comment = None):
434 """
435 Define a pre processor symbol name, with the optional given value in the
436 current config header.
437
438 If value is None (default), then #define name is written. If value is not
439 none, then #define name value is written.
440
441 comment is a string which will be put as a C comment in the
442 header, to explain the meaning of the value (appropriate C comments /* and
443 */ will be put automatically."""
444 lines = []
445 if comment:
446 comment_str = "/* %s */" % comment
447 lines.append(comment_str)
448
449 if value is not None:
450 define_str = "#define %s %s" % (name, value)
451 else:
452 define_str = "#define %s" % name
453 lines.append(define_str)
454 lines.append('')
455
456 self.config_h_text = self.config_h_text + string.join(lines, '\n')
457
507
509 """Wrapper function for handling piped spawns.
510
511 This looks to the calling interface (in Action.py) like a "normal"
512 spawn, but associates the call with the PSPAWN variable from
513 the construction environment and with the streams to which we
514 want the output logged. This gets slid into the construction
515 environment as the SPAWN variable so Action.py doesn't have to
516 know or care whether it's spawning a piped command or not.
517 """
518 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
519
520
521 - def TryBuild(self, builder, text = None, extension = ""):
522 """Low level TryBuild implementation. Normally you don't need to
523 call that - you can use TryCompile / TryLink / TryRun instead
524 """
525 global _ac_build_counter
526
527
528
529 try:
530 self.pspawn = self.env['PSPAWN']
531 except KeyError:
532 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
533 try:
534 save_spawn = self.env['SPAWN']
535 except KeyError:
536 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
537
538 nodesToBeBuilt = []
539
540 f = "conftest_" + str(_ac_build_counter)
541 pref = self.env.subst( builder.builder.prefix )
542 suff = self.env.subst( builder.builder.suffix )
543 target = self.confdir.File(pref + f + suff)
544
545 try:
546
547
548 self.env['SPAWN'] = self.pspawn_wrapper
549 sourcetext = self.env.Value(text)
550
551 if text != None:
552 textFile = self.confdir.File(f + extension)
553 textFileNode = self.env.SConfSourceBuilder(target=textFile,
554 source=sourcetext)
555 nodesToBeBuilt.extend(textFileNode)
556 source = textFileNode
557 else:
558 source = None
559
560 nodes = builder(target = target, source = source)
561 if not SCons.Util.is_List(nodes):
562 nodes = [nodes]
563 nodesToBeBuilt.extend(nodes)
564 result = self.BuildNodes(nodesToBeBuilt)
565
566 finally:
567 self.env['SPAWN'] = save_spawn
568
569 _ac_build_counter = _ac_build_counter + 1
570 if result:
571 self.lastTarget = nodes[0]
572 else:
573 self.lastTarget = None
574
575 return result
576
577 - def TryAction(self, action, text = None, extension = ""):
578 """Tries to execute the given action with optional source file
579 contents <text> and optional source file extension <extension>,
580 Returns the status (0 : failed, 1 : ok) and the contents of the
581 output file.
582 """
583 builder = SCons.Builder.Builder(action=action)
584 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
585 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
586 del self.env['BUILDERS']['SConfActionBuilder']
587 if ok:
588 outputStr = self.lastTarget.get_contents()
589 return (1, outputStr)
590 return (0, "")
591
593 """Compiles the program given in text to an env.Object, using extension
594 as file extension (e.g. '.c'). Returns 1, if compilation was
595 successful, 0 otherwise. The target is saved in self.lastTarget (for
596 further processing).
597 """
598 return self.TryBuild(self.env.Object, text, extension)
599
600 - def TryLink( self, text, extension ):
601 """Compiles the program given in text to an executable env.Program,
602 using extension as file extension (e.g. '.c'). Returns 1, if
603 compilation was successful, 0 otherwise. The target is saved in
604 self.lastTarget (for further processing).
605 """
606 return self.TryBuild(self.env.Program, text, extension )
607
608 - def TryRun(self, text, extension ):
609 """Compiles and runs the program given in text, using extension
610 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
611 (0, '') otherwise. The target (a file containing the program's stdout)
612 is saved in self.lastTarget (for further processing).
613 """
614 ok = self.TryLink(text, extension)
615 if( ok ):
616 prog = self.lastTarget
617 pname = str(prog)
618 output = SConfFS.File(pname+'.out')
619 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
620 ok = self.BuildNodes(node)
621 if ok:
622 outputStr = output.get_contents()
623 return( 1, outputStr)
624 return (0, "")
625
627 """A wrapper around Tests (to ensure sanity)"""
629 self.test = test
630 self.sconf = sconf
632 if not self.sconf.active:
633 raise (SCons.Errors.UserError,
634 "Test called after sconf.Finish()")
635 context = CheckContext(self.sconf)
636 ret = apply(self.test, (context,) + args, kw)
637 if not self.sconf.config_h is None:
638 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
639 context.Result("error: no result")
640 return ret
641
642 - def AddTest(self, test_name, test_instance):
643 """Adds test_class to this SConf instance. It can be called with
644 self.test_name(...)"""
645 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
646
648 """Adds all the tests given in the tests dictionary to this SConf
649 instance
650 """
651 for name in tests.keys():
652 self.AddTest(name, tests[name])
653
663
709
711 """Private method. Reset to non-piped spawn"""
712 global sconf_global, _ac_config_hs
713
714 if not self.active:
715 raise SCons.Errors.UserError, "Finish may be called only once!"
716 if self.logstream != None and not dryrun:
717 self.logstream.write("\n")
718 self.logstream.close()
719 self.logstream = None
720
721 blds = self.env['BUILDERS']
722 del blds['SConfSourceBuilder']
723 self.env.Replace( BUILDERS=blds )
724 self.active = 0
725 sconf_global = None
726 if not self.config_h is None:
727 _ac_config_hs[self.config_h] = self.config_h_text
728 self.env.fs = self.lastEnvFs
729
731 """Provides a context for configure tests. Defines how a test writes to the
732 screen and log file.
733
734 A typical test is just a callable with an instance of CheckContext as
735 first argument:
736
737 def CheckCustom(context, ...)
738 context.Message('Checking my weird test ... ')
739 ret = myWeirdTestFunction(...)
740 context.Result(ret)
741
742 Often, myWeirdTestFunction will be one of
743 context.TryCompile/context.TryLink/context.TryRun. The results of
744 those are cached, for they are only rebuild, if the dependencies have
745 changed.
746 """
747
748 - def __init__(self, sconf):
749 """Constructor. Pass the corresponding SConf instance."""
750 self.sconf = sconf
751 self.did_show_result = 0
752
753
754 self.vardict = {}
755 self.havedict = {}
756 self.headerfilename = None
757 self.config_h = ""
758
759
760
761
762
763
764
765
766 - def Message(self, text):
767 """Inform about what we are doing right now, e.g.
768 'Checking for SOMETHING ... '
769 """
770 self.Display(text)
771 self.sconf.cached = 1
772 self.did_show_result = 0
773
774 - def Result(self, res):
775 """Inform about the result of the test. res may be an integer or a
776 string. In case of an integer, the written text will be 'ok' or
777 'failed'.
778 The result is only displayed when self.did_show_result is not set.
779 """
780 if type(res) in BooleanTypes:
781 if res:
782 text = "yes"
783 else:
784 text = "no"
785 elif type(res) == types.StringType:
786 text = res
787 else:
788 raise TypeError, "Expected string, int or bool, got " + str(type(res))
789
790 if self.did_show_result == 0:
791
792 self.Display(text + "\n")
793 self.did_show_result = 1
794
795 - def TryBuild(self, *args, **kw):
796 return apply(self.sconf.TryBuild, args, kw)
797
798 - def TryAction(self, *args, **kw):
799 return apply(self.sconf.TryAction, args, kw)
800
801 - def TryCompile(self, *args, **kw):
802 return apply(self.sconf.TryCompile, args, kw)
803
804 - def TryLink(self, *args, **kw):
805 return apply(self.sconf.TryLink, args, kw)
806
807 - def TryRun(self, *args, **kw):
808 return apply(self.sconf.TryRun, args, kw)
809
810 - def __getattr__( self, attr ):
811 if( attr == 'env' ):
812 return self.sconf.env
813 elif( attr == 'lastTarget' ):
814 return self.sconf.lastTarget
815 else:
816 raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
817
818
819
820 - def BuildProg(self, text, ext):
821 self.sconf.cached = 1
822
823 return not self.TryBuild(self.env.Program, text, ext)
824
825 - def CompileProg(self, text, ext):
826 self.sconf.cached = 1
827
828 return not self.TryBuild(self.env.Object, text, ext)
829
830 - def CompileSharedObject(self, text, ext):
831 self.sconf.cached = 1
832
833 return not self.TryBuild(self.env.SharedObject, text, ext)
834
835 - def RunProg(self, text, ext):
836 self.sconf.cached = 1
837
838 st, out = self.TryRun(text, ext)
839 return not st, out
840
841 - def AppendLIBS(self, lib_name_list):
842 oldLIBS = self.env.get( 'LIBS', [] )
843 self.env.Append(LIBS = lib_name_list)
844 return oldLIBS
845
846 - def SetLIBS(self, val):
847 oldLIBS = self.env.get( 'LIBS', [] )
848 self.env.Replace(LIBS = val)
849 return oldLIBS
850
851 - def Display(self, msg):
852 if self.sconf.cached:
853
854
855
856 msg = "(cached) " + msg
857 self.sconf.cached = 0
858 progress_display(msg, append_newline=0)
859 self.Log("scons: Configure: " + msg + "\n")
860
861 - def Log(self, msg):
862 if self.sconf.logstream != None:
863 self.sconf.logstream.write(msg)
864
865
866
867
879
880
881 -def CheckFunc(context, function_name, header = None, language = None):
885
886 -def CheckType(context, type_name, includes = "", language = None):
887 res = SCons.Conftest.CheckType(context, type_name,
888 header = includes, language = language)
889 context.did_show_result = 1
890 return not res
891
892 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
893 res = SCons.Conftest.CheckTypeSize(context, type_name,
894 header = includes, language = language,
895 expect = expect)
896 context.did_show_result = 1
897 return res
898
900 res = SCons.Conftest.CheckDeclaration(context, declaration,
901 includes = includes,
902 language = language)
903 context.did_show_result = 1
904 return not res
905
907
908
909 if not SCons.Util.is_List(headers):
910 headers = [headers]
911 l = []
912 if leaveLast:
913 lastHeader = headers[-1]
914 headers = headers[:-1]
915 else:
916 lastHeader = None
917 for s in headers:
918 l.append("#include %s%s%s\n"
919 % (include_quotes[0], s, include_quotes[1]))
920 return string.join(l, ''), lastHeader
921
923 """
924 A test for a C or C++ header file.
925 """
926 prog_prefix, hdr_to_check = \
927 createIncludesFromHeaders(header, 1, include_quotes)
928 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
929 language = language,
930 include_quotes = include_quotes)
931 context.did_show_result = 1
932 return not res
933
937
941
945
949
950
951
953 """
954 A test for a C header file.
955 """
956 return CheckHeader(context, header, include_quotes, language = "C")
957
958
959
960
962 """
963 A test for a C++ header file.
964 """
965 return CheckHeader(context, header, include_quotes, language = "C++")
966
967
968 -def CheckLib(context, library = None, symbol = "main",
969 header = None, language = None, autoadd = 1):
970 """
971 A test for a library. See also CheckLibWithHeader.
972 Note that library may also be None to test whether the given symbol
973 compiles without flags.
974 """
975
976 if library == []:
977 library = [None]
978
979 if not SCons.Util.is_List(library):
980 library = [library]
981
982
983 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
984 language = language, autoadd = autoadd)
985 context.did_show_result = 1
986 return not res
987
988
989
990
993
994 """
995 Another (more sophisticated) test for a library.
996 Checks, if library and header is available for language (may be 'C'
997 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
998 As in CheckLib, we support library=None, to test if the call compiles
999 without extra link flags.
1000 """
1001 prog_prefix, dummy = \
1002 createIncludesFromHeaders(header, 0)
1003 if libs == []:
1004 libs = [None]
1005
1006 if not SCons.Util.is_List(libs):
1007 libs = [libs]
1008
1009 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1010 call = call, language = language, autoadd = autoadd)
1011 context.did_show_result = 1
1012 return not res
1013