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 5110 2010/07/25 16:14:38 bdeegan"
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
327 if env.decide_source.func_code is not force_build.func_code:
328 env.Decider(force_build)
329 env['PSTDOUT'] = env['PSTDERR'] = s
330 try:
331 sconf.cached = 0
332 self.targets[0].build()
333 finally:
334 sys.stdout = sys.stderr = env['PSTDOUT'] = \
335 env['PSTDERR'] = sconf.logstream
336 except KeyboardInterrupt:
337 raise
338 except SystemExit:
339 exc_value = sys.exc_info()[1]
340 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
341 except Exception, e:
342 for t in self.targets:
343 binfo = t.get_binfo()
344 binfo.__class__ = SConfBuildInfo
345 binfo.set_build_result(1, s.getvalue())
346 sconsign_entry = SCons.SConsign.SConsignEntry()
347 sconsign_entry.binfo = binfo
348
349
350
351
352
353
354
355 sconsign = t.dir.sconsign()
356 sconsign.set_entry(t.name, sconsign_entry)
357 sconsign.merge()
358 raise e
359 else:
360 for t in self.targets:
361 binfo = t.get_binfo()
362 binfo.__class__ = SConfBuildInfo
363 binfo.set_build_result(0, s.getvalue())
364 sconsign_entry = SCons.SConsign.SConsignEntry()
365 sconsign_entry.binfo = binfo
366
367
368
369
370
371
372
373 sconsign = t.dir.sconsign()
374 sconsign.set_entry(t.name, sconsign_entry)
375 sconsign.merge()
376
378 """This is simply a class to represent a configure context. After
379 creating a SConf object, you can call any tests. After finished with your
380 tests, be sure to call the Finish() method, which returns the modified
381 environment.
382 Some words about caching: In most cases, it is not necessary to cache
383 Test results explicitely. Instead, we use the scons dependency checking
384 mechanism. For example, if one wants to compile a test program
385 (SConf.TryLink), the compiler is only called, if the program dependencies
386 have changed. However, if the program could not be compiled in a former
387 SConf run, we need to explicitely cache this error.
388 """
389
390 - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
391 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
392 """Constructor. Pass additional tests in the custom_tests-dictinary,
393 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
394 defines a custom test.
395 Note also the conf_dir and log_file arguments (you may want to
396 build tests in the VariantDir, not in the SourceDir)
397 """
398 global SConfFS
399 if not SConfFS:
400 SConfFS = SCons.Node.FS.default_fs or \
401 SCons.Node.FS.FS(env.fs.pathTop)
402 if sconf_global is not None:
403 raise (SCons.Errors.UserError,
404 "Only one SConf object may be active at one time")
405 self.env = env
406 if log_file is not None:
407 log_file = SConfFS.File(env.subst(log_file))
408 self.logfile = log_file
409 self.logstream = None
410 self.lastTarget = None
411 self.depth = _depth
412 self.cached = 0
413
414
415 default_tests = {
416 'CheckCC' : CheckCC,
417 'CheckCXX' : CheckCXX,
418 'CheckSHCC' : CheckSHCC,
419 'CheckSHCXX' : CheckSHCXX,
420 'CheckFunc' : CheckFunc,
421 'CheckType' : CheckType,
422 'CheckTypeSize' : CheckTypeSize,
423 'CheckDeclaration' : CheckDeclaration,
424 'CheckHeader' : CheckHeader,
425 'CheckCHeader' : CheckCHeader,
426 'CheckCXXHeader' : CheckCXXHeader,
427 'CheckLib' : CheckLib,
428 'CheckLibWithHeader' : CheckLibWithHeader,
429 }
430 self.AddTests(default_tests)
431 self.AddTests(custom_tests)
432 self.confdir = SConfFS.Dir(env.subst(conf_dir))
433 if config_h is not None:
434 config_h = SConfFS.File(config_h)
435 self.config_h = config_h
436 self._startup()
437
439 """Call this method after finished with your tests:
440 env = sconf.Finish()
441 """
442 self._shutdown()
443 return self.env
444
445 - def Define(self, name, value = None, comment = None):
446 """
447 Define a pre processor symbol name, with the optional given value in the
448 current config header.
449
450 If value is None (default), then #define name is written. If value is not
451 none, then #define name value is written.
452
453 comment is a string which will be put as a C comment in the
454 header, to explain the meaning of the value (appropriate C comments /* and
455 */ will be put automatically."""
456 lines = []
457 if comment:
458 comment_str = "/* %s */" % comment
459 lines.append(comment_str)
460
461 if value is not None:
462 define_str = "#define %s %s" % (name, value)
463 else:
464 define_str = "#define %s" % name
465 lines.append(define_str)
466 lines.append('')
467
468 self.config_h_text = self.config_h_text + string.join(lines, '\n')
469
519
521 """Wrapper function for handling piped spawns.
522
523 This looks to the calling interface (in Action.py) like a "normal"
524 spawn, but associates the call with the PSPAWN variable from
525 the construction environment and with the streams to which we
526 want the output logged. This gets slid into the construction
527 environment as the SPAWN variable so Action.py doesn't have to
528 know or care whether it's spawning a piped command or not.
529 """
530 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
531
532
533 - def TryBuild(self, builder, text = None, extension = ""):
534 """Low level TryBuild implementation. Normally you don't need to
535 call that - you can use TryCompile / TryLink / TryRun instead
536 """
537 global _ac_build_counter
538
539
540
541 try:
542 self.pspawn = self.env['PSPAWN']
543 except KeyError:
544 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
545 try:
546 save_spawn = self.env['SPAWN']
547 except KeyError:
548 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
549
550 nodesToBeBuilt = []
551
552 f = "conftest_" + str(_ac_build_counter)
553 pref = self.env.subst( builder.builder.prefix )
554 suff = self.env.subst( builder.builder.suffix )
555 target = self.confdir.File(pref + f + suff)
556
557 try:
558
559
560 self.env['SPAWN'] = self.pspawn_wrapper
561 sourcetext = self.env.Value(text)
562
563 if text is not None:
564 textFile = self.confdir.File(f + extension)
565 textFileNode = self.env.SConfSourceBuilder(target=textFile,
566 source=sourcetext)
567 nodesToBeBuilt.extend(textFileNode)
568 source = textFileNode
569 else:
570 source = None
571
572 nodes = builder(target = target, source = source)
573 if not SCons.Util.is_List(nodes):
574 nodes = [nodes]
575 nodesToBeBuilt.extend(nodes)
576 result = self.BuildNodes(nodesToBeBuilt)
577
578 finally:
579 self.env['SPAWN'] = save_spawn
580
581 _ac_build_counter = _ac_build_counter + 1
582 if result:
583 self.lastTarget = nodes[0]
584 else:
585 self.lastTarget = None
586
587 return result
588
589 - def TryAction(self, action, text = None, extension = ""):
590 """Tries to execute the given action with optional source file
591 contents <text> and optional source file extension <extension>,
592 Returns the status (0 : failed, 1 : ok) and the contents of the
593 output file.
594 """
595 builder = SCons.Builder.Builder(action=action)
596 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
597 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
598 del self.env['BUILDERS']['SConfActionBuilder']
599 if ok:
600 outputStr = self.lastTarget.get_contents()
601 return (1, outputStr)
602 return (0, "")
603
605 """Compiles the program given in text to an env.Object, using extension
606 as file extension (e.g. '.c'). Returns 1, if compilation was
607 successful, 0 otherwise. The target is saved in self.lastTarget (for
608 further processing).
609 """
610 return self.TryBuild(self.env.Object, text, extension)
611
612 - def TryLink( self, text, extension ):
613 """Compiles the program given in text to an executable env.Program,
614 using extension as file extension (e.g. '.c'). Returns 1, if
615 compilation was successful, 0 otherwise. The target is saved in
616 self.lastTarget (for further processing).
617 """
618 return self.TryBuild(self.env.Program, text, extension )
619
620 - def TryRun(self, text, extension ):
621 """Compiles and runs the program given in text, using extension
622 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
623 (0, '') otherwise. The target (a file containing the program's stdout)
624 is saved in self.lastTarget (for further processing).
625 """
626 ok = self.TryLink(text, extension)
627 if( ok ):
628 prog = self.lastTarget
629 pname = prog.path
630 output = self.confdir.File(os.path.basename(pname)+'.out')
631 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
632 ok = self.BuildNodes(node)
633 if ok:
634 outputStr = output.get_contents()
635 return( 1, outputStr)
636 return (0, "")
637
639 """A wrapper around Tests (to ensure sanity)"""
641 self.test = test
642 self.sconf = sconf
644 if not self.sconf.active:
645 raise (SCons.Errors.UserError,
646 "Test called after sconf.Finish()")
647 context = CheckContext(self.sconf)
648 ret = apply(self.test, (context,) + args, kw)
649 if self.sconf.config_h is not None:
650 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
651 context.Result("error: no result")
652 return ret
653
654 - def AddTest(self, test_name, test_instance):
655 """Adds test_class to this SConf instance. It can be called with
656 self.test_name(...)"""
657 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
658
660 """Adds all the tests given in the tests dictionary to this SConf
661 instance
662 """
663 for name in tests.keys():
664 self.AddTest(name, tests[name])
665
675
721
723 """Private method. Reset to non-piped spawn"""
724 global sconf_global, _ac_config_hs
725
726 if not self.active:
727 raise SCons.Errors.UserError, "Finish may be called only once!"
728 if self.logstream is not None and not dryrun:
729 self.logstream.write("\n")
730 self.logstream.close()
731 self.logstream = None
732
733 blds = self.env['BUILDERS']
734 del blds['SConfSourceBuilder']
735 self.env.Replace( BUILDERS=blds )
736 self.active = 0
737 sconf_global = None
738 if not self.config_h is None:
739 _ac_config_hs[self.config_h] = self.config_h_text
740 self.env.fs = self.lastEnvFs
741
743 """Provides a context for configure tests. Defines how a test writes to the
744 screen and log file.
745
746 A typical test is just a callable with an instance of CheckContext as
747 first argument:
748
749 def CheckCustom(context, ...)
750 context.Message('Checking my weird test ... ')
751 ret = myWeirdTestFunction(...)
752 context.Result(ret)
753
754 Often, myWeirdTestFunction will be one of
755 context.TryCompile/context.TryLink/context.TryRun. The results of
756 those are cached, for they are only rebuild, if the dependencies have
757 changed.
758 """
759
760 - def __init__(self, sconf):
761 """Constructor. Pass the corresponding SConf instance."""
762 self.sconf = sconf
763 self.did_show_result = 0
764
765
766 self.vardict = {}
767 self.havedict = {}
768 self.headerfilename = None
769 self.config_h = ""
770
771
772
773
774
775
776
777
778 - def Message(self, text):
779 """Inform about what we are doing right now, e.g.
780 'Checking for SOMETHING ... '
781 """
782 self.Display(text)
783 self.sconf.cached = 1
784 self.did_show_result = 0
785
786 - def Result(self, res):
787 """Inform about the result of the test. res may be an integer or a
788 string. In case of an integer, the written text will be 'yes' or 'no'.
789 The result is only displayed when self.did_show_result is not set.
790 """
791 if type(res) in BooleanTypes:
792 if res:
793 text = "yes"
794 else:
795 text = "no"
796 elif type(res) == types.StringType:
797 text = res
798 else:
799 raise TypeError, "Expected string, int or bool, got " + str(type(res))
800
801 if self.did_show_result == 0:
802
803 self.Display(text + "\n")
804 self.did_show_result = 1
805
806 - def TryBuild(self, *args, **kw):
807 return apply(self.sconf.TryBuild, args, kw)
808
809 - def TryAction(self, *args, **kw):
810 return apply(self.sconf.TryAction, args, kw)
811
812 - def TryCompile(self, *args, **kw):
813 return apply(self.sconf.TryCompile, args, kw)
814
815 - def TryLink(self, *args, **kw):
816 return apply(self.sconf.TryLink, args, kw)
817
818 - def TryRun(self, *args, **kw):
819 return apply(self.sconf.TryRun, args, kw)
820
821 - def __getattr__( self, attr ):
822 if( attr == 'env' ):
823 return self.sconf.env
824 elif( attr == 'lastTarget' ):
825 return self.sconf.lastTarget
826 else:
827 raise AttributeError, "CheckContext instance has no attribute '%s'" % attr
828
829
830
831 - def BuildProg(self, text, ext):
832 self.sconf.cached = 1
833
834 return not self.TryBuild(self.env.Program, text, ext)
835
836 - def CompileProg(self, text, ext):
837 self.sconf.cached = 1
838
839 return not self.TryBuild(self.env.Object, text, ext)
840
841 - def CompileSharedObject(self, text, ext):
842 self.sconf.cached = 1
843
844 return not self.TryBuild(self.env.SharedObject, text, ext)
845
846 - def RunProg(self, text, ext):
847 self.sconf.cached = 1
848
849 st, out = self.TryRun(text, ext)
850 return not st, out
851
852 - def AppendLIBS(self, lib_name_list):
853 oldLIBS = self.env.get( 'LIBS', [] )
854 self.env.Append(LIBS = lib_name_list)
855 return oldLIBS
856
857 - def PrependLIBS(self, lib_name_list):
858 oldLIBS = self.env.get( 'LIBS', [] )
859 self.env.Prepend(LIBS = lib_name_list)
860 return oldLIBS
861
862 - def SetLIBS(self, val):
863 oldLIBS = self.env.get( 'LIBS', [] )
864 self.env.Replace(LIBS = val)
865 return oldLIBS
866
867 - def Display(self, msg):
868 if self.sconf.cached:
869
870
871
872 msg = "(cached) " + msg
873 self.sconf.cached = 0
874 progress_display(msg, append_newline=0)
875 self.Log("scons: Configure: " + msg + "\n")
876
877 - def Log(self, msg):
878 if self.sconf.logstream is not None:
879 self.sconf.logstream.write(msg)
880
881
882
883
895
896
897 -def CheckFunc(context, function_name, header = None, language = None):
901
902 -def CheckType(context, type_name, includes = "", language = None):
903 res = SCons.Conftest.CheckType(context, type_name,
904 header = includes, language = language)
905 context.did_show_result = 1
906 return not res
907
908 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
909 res = SCons.Conftest.CheckTypeSize(context, type_name,
910 header = includes, language = language,
911 expect = expect)
912 context.did_show_result = 1
913 return res
914
916 res = SCons.Conftest.CheckDeclaration(context, declaration,
917 includes = includes,
918 language = language)
919 context.did_show_result = 1
920 return not res
921
923
924
925 if not SCons.Util.is_List(headers):
926 headers = [headers]
927 l = []
928 if leaveLast:
929 lastHeader = headers[-1]
930 headers = headers[:-1]
931 else:
932 lastHeader = None
933 for s in headers:
934 l.append("#include %s%s%s\n"
935 % (include_quotes[0], s, include_quotes[1]))
936 return string.join(l, ''), lastHeader
937
939 """
940 A test for a C or C++ header file.
941 """
942 prog_prefix, hdr_to_check = \
943 createIncludesFromHeaders(header, 1, include_quotes)
944 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
945 language = language,
946 include_quotes = include_quotes)
947 context.did_show_result = 1
948 return not res
949
954
959
964
969
970
971
973 """
974 A test for a C header file.
975 """
976 return CheckHeader(context, header, include_quotes, language = "C")
977
978
979
980
982 """
983 A test for a C++ header file.
984 """
985 return CheckHeader(context, header, include_quotes, language = "C++")
986
987
988 -def CheckLib(context, library = None, symbol = "main",
989 header = None, language = None, autoadd = 1):
990 """
991 A test for a library. See also CheckLibWithHeader.
992 Note that library may also be None to test whether the given symbol
993 compiles without flags.
994 """
995
996 if library == []:
997 library = [None]
998
999 if not SCons.Util.is_List(library):
1000 library = [library]
1001
1002
1003 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
1004 language = language, autoadd = autoadd)
1005 context.did_show_result = 1
1006 return not res
1007
1008
1009
1010
1013
1014 """
1015 Another (more sophisticated) test for a library.
1016 Checks, if library and header is available for language (may be 'C'
1017 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
1018 As in CheckLib, we support library=None, to test if the call compiles
1019 without extra link flags.
1020 """
1021 prog_prefix, dummy = \
1022 createIncludesFromHeaders(header, 0)
1023 if libs == []:
1024 libs = [None]
1025
1026 if not SCons.Util.is_List(libs):
1027 libs = [libs]
1028
1029 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1030 call = call, language = language, autoadd = autoadd)
1031 context.did_show_result = 1
1032 return not res
1033
1034
1035
1036
1037
1038
1039