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 5023 2010/06/14 22:05:46 scons"
30
31 import SCons.compat
32
33 import io
34 import os
35 import re
36 import sys
37 import traceback
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_]', '_', str(target[0]).upper())
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 source[0].get_contents().replace( '\n', "\n |" ) )
157
159 """
160 Special build info for targets of configure tests. Additional members
161 are result (did the builder succeed last time?) and string, which
162 contains messages of the original build phase.
163 """
164 result = None
165 string = None
166
170
171
173 """
174 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
175 """
177 self.orig = orig
178 self.s = io.StringIO()
179
181 if self.orig:
182 self.orig.write(str)
183 self.s.write(str)
184
186 for l in lines:
187 self.write(l + '\n')
188
190 """
191 Return everything written to orig since the Streamer was created.
192 """
193 return self.s.getvalue()
194
196 if self.orig:
197 self.orig.flush()
198 self.s.flush()
199
200
202 """
203 This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
204 correctly and knows about the current cache_mode.
205 """
209
211 """
212 Logs the original builder messages, given the SConfBuildInfo instance
213 bi.
214 """
215 if not isinstance(bi, SConfBuildInfo):
216 SCons.Warnings.warn(SConfWarning,
217 "The stored build information has an unexpected class: %s" % bi.__class__)
218 else:
219 self.display("The original builder output was:\n" +
220 (" |" + str(bi.string)).replace("\n", "\n |"))
221
223
224
225 exc_type = self.exc_info()[0]
226 if issubclass(exc_type, SConfError):
227 raise
228 elif issubclass(exc_type, SCons.Errors.BuildError):
229
230
231
232 self.exc_clear()
233 else:
234 self.display('Caught exception while building "%s":\n' %
235 self.targets[0])
236 try:
237 excepthook = sys.excepthook
238 except AttributeError:
239
240 def excepthook(type, value, tb):
241 traceback.print_tb(tb)
242 print type, value
243 excepthook(*self.exc_info())
244 return SCons.Taskmaster.Task.failed(self)
245
280
321 if env.decide_source.func_code is not force_build.func_code:
322 env.Decider(force_build)
323 env['PSTDOUT'] = env['PSTDERR'] = s
324 try:
325 sconf.cached = 0
326 self.targets[0].build()
327 finally:
328 sys.stdout = sys.stderr = env['PSTDOUT'] = \
329 env['PSTDERR'] = sconf.logstream
330 except KeyboardInterrupt:
331 raise
332 except SystemExit:
333 exc_value = sys.exc_info()[1]
334 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
335 except Exception, e:
336 for t in self.targets:
337 binfo = t.get_binfo()
338 binfo.__class__ = SConfBuildInfo
339 binfo.set_build_result(1, s.getvalue())
340 sconsign_entry = SCons.SConsign.SConsignEntry()
341 sconsign_entry.binfo = binfo
342
343
344
345
346
347
348
349 sconsign = t.dir.sconsign()
350 sconsign.set_entry(t.name, sconsign_entry)
351 sconsign.merge()
352 raise e
353 else:
354 for t in self.targets:
355 binfo = t.get_binfo()
356 binfo.__class__ = SConfBuildInfo
357 binfo.set_build_result(0, s.getvalue())
358 sconsign_entry = SCons.SConsign.SConsignEntry()
359 sconsign_entry.binfo = binfo
360
361
362
363
364
365
366
367 sconsign = t.dir.sconsign()
368 sconsign.set_entry(t.name, sconsign_entry)
369 sconsign.merge()
370
372 """This is simply a class to represent a configure context. After
373 creating a SConf object, you can call any tests. After finished with your
374 tests, be sure to call the Finish() method, which returns the modified
375 environment.
376 Some words about caching: In most cases, it is not necessary to cache
377 Test results explicitely. Instead, we use the scons dependency checking
378 mechanism. For example, if one wants to compile a test program
379 (SConf.TryLink), the compiler is only called, if the program dependencies
380 have changed. However, if the program could not be compiled in a former
381 SConf run, we need to explicitely cache this error.
382 """
383
384 - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
385 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
386 """Constructor. Pass additional tests in the custom_tests-dictinary,
387 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
388 defines a custom test.
389 Note also the conf_dir and log_file arguments (you may want to
390 build tests in the VariantDir, not in the SourceDir)
391 """
392 global SConfFS
393 if not SConfFS:
394 SConfFS = SCons.Node.FS.default_fs or \
395 SCons.Node.FS.FS(env.fs.pathTop)
396 if sconf_global is not None:
397 raise SCons.Errors.UserError
398 self.env = env
399 if log_file is not None:
400 log_file = SConfFS.File(env.subst(log_file))
401 self.logfile = log_file
402 self.logstream = None
403 self.lastTarget = None
404 self.depth = _depth
405 self.cached = 0
406
407
408 default_tests = {
409 'CheckCC' : CheckCC,
410 'CheckCXX' : CheckCXX,
411 'CheckSHCC' : CheckSHCC,
412 'CheckSHCXX' : CheckSHCXX,
413 'CheckFunc' : CheckFunc,
414 'CheckType' : CheckType,
415 'CheckTypeSize' : CheckTypeSize,
416 'CheckDeclaration' : CheckDeclaration,
417 'CheckHeader' : CheckHeader,
418 'CheckCHeader' : CheckCHeader,
419 'CheckCXXHeader' : CheckCXXHeader,
420 'CheckLib' : CheckLib,
421 'CheckLibWithHeader' : CheckLibWithHeader,
422 }
423 self.AddTests(default_tests)
424 self.AddTests(custom_tests)
425 self.confdir = SConfFS.Dir(env.subst(conf_dir))
426 if config_h is not None:
427 config_h = SConfFS.File(config_h)
428 self.config_h = config_h
429 self._startup()
430
432 """Call this method after finished with your tests:
433 env = sconf.Finish()
434 """
435 self._shutdown()
436 return self.env
437
438 - def Define(self, name, value = None, comment = None):
439 """
440 Define a pre processor symbol name, with the optional given value in the
441 current config header.
442
443 If value is None (default), then #define name is written. If value is not
444 none, then #define name value is written.
445
446 comment is a string which will be put as a C comment in the
447 header, to explain the meaning of the value (appropriate C comments /* and
448 */ will be put automatically."""
449 lines = []
450 if comment:
451 comment_str = "/* %s */" % comment
452 lines.append(comment_str)
453
454 if value is not None:
455 define_str = "#define %s %s" % (name, value)
456 else:
457 define_str = "#define %s" % name
458 lines.append(define_str)
459 lines.append('')
460
461 self.config_h_text = self.config_h_text + '\n'.join(lines)
462
512
514 """Wrapper function for handling piped spawns.
515
516 This looks to the calling interface (in Action.py) like a "normal"
517 spawn, but associates the call with the PSPAWN variable from
518 the construction environment and with the streams to which we
519 want the output logged. This gets slid into the construction
520 environment as the SPAWN variable so Action.py doesn't have to
521 know or care whether it's spawning a piped command or not.
522 """
523 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
524
525
526 - def TryBuild(self, builder, text = None, extension = ""):
527 """Low level TryBuild implementation. Normally you don't need to
528 call that - you can use TryCompile / TryLink / TryRun instead
529 """
530 global _ac_build_counter
531
532
533
534 try:
535 self.pspawn = self.env['PSPAWN']
536 except KeyError:
537 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
538 try:
539 save_spawn = self.env['SPAWN']
540 except KeyError:
541 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
542
543 nodesToBeBuilt = []
544
545 f = "conftest_" + str(_ac_build_counter)
546 pref = self.env.subst( builder.builder.prefix )
547 suff = self.env.subst( builder.builder.suffix )
548 target = self.confdir.File(pref + f + suff)
549
550 try:
551
552
553 self.env['SPAWN'] = self.pspawn_wrapper
554 sourcetext = self.env.Value(text)
555
556 if text is not None:
557 textFile = self.confdir.File(f + extension)
558 textFileNode = self.env.SConfSourceBuilder(target=textFile,
559 source=sourcetext)
560 nodesToBeBuilt.extend(textFileNode)
561 source = textFileNode
562 else:
563 source = None
564
565 nodes = builder(target = target, source = source)
566 if not SCons.Util.is_List(nodes):
567 nodes = [nodes]
568 nodesToBeBuilt.extend(nodes)
569 result = self.BuildNodes(nodesToBeBuilt)
570
571 finally:
572 self.env['SPAWN'] = save_spawn
573
574 _ac_build_counter = _ac_build_counter + 1
575 if result:
576 self.lastTarget = nodes[0]
577 else:
578 self.lastTarget = None
579
580 return result
581
582 - def TryAction(self, action, text = None, extension = ""):
583 """Tries to execute the given action with optional source file
584 contents <text> and optional source file extension <extension>,
585 Returns the status (0 : failed, 1 : ok) and the contents of the
586 output file.
587 """
588 builder = SCons.Builder.Builder(action=action)
589 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
590 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
591 del self.env['BUILDERS']['SConfActionBuilder']
592 if ok:
593 outputStr = self.lastTarget.get_contents()
594 return (1, outputStr)
595 return (0, "")
596
598 """Compiles the program given in text to an env.Object, using extension
599 as file extension (e.g. '.c'). Returns 1, if compilation was
600 successful, 0 otherwise. The target is saved in self.lastTarget (for
601 further processing).
602 """
603 return self.TryBuild(self.env.Object, text, extension)
604
605 - def TryLink( self, text, extension ):
606 """Compiles the program given in text to an executable env.Program,
607 using extension as file extension (e.g. '.c'). Returns 1, if
608 compilation was successful, 0 otherwise. The target is saved in
609 self.lastTarget (for further processing).
610 """
611 return self.TryBuild(self.env.Program, text, extension )
612
613 - def TryRun(self, text, extension ):
614 """Compiles and runs the program given in text, using extension
615 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
616 (0, '') otherwise. The target (a file containing the program's stdout)
617 is saved in self.lastTarget (for further processing).
618 """
619 ok = self.TryLink(text, extension)
620 if( ok ):
621 prog = self.lastTarget
622 pname = prog.path
623 output = self.confdir.File(os.path.basename(pname)+'.out')
624 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
625 ok = self.BuildNodes(node)
626 if ok:
627 outputStr = output.get_contents()
628 return( 1, outputStr)
629 return (0, "")
630
632 """A wrapper around Tests (to ensure sanity)"""
634 self.test = test
635 self.sconf = sconf
637 if not self.sconf.active:
638 raise SCons.Errors.UserError
639 context = CheckContext(self.sconf)
640 ret = self.test(context, *args, **kw)
641 if self.sconf.config_h is not None:
642 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
643 context.Result("error: no result")
644 return ret
645
646 - def AddTest(self, test_name, test_instance):
647 """Adds test_class to this SConf instance. It can be called with
648 self.test_name(...)"""
649 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
650
652 """Adds all the tests given in the tests dictionary to this SConf
653 instance
654 """
655 for name in tests.keys():
656 self.AddTest(name, tests[name])
657
667
713
715 """Private method. Reset to non-piped spawn"""
716 global sconf_global, _ac_config_hs
717
718 if not self.active:
719 raise SCons.Errors.UserError("Finish may be called only once!")
720 if self.logstream is not None and not dryrun:
721 self.logstream.write("\n")
722 self.logstream.close()
723 self.logstream = None
724
725 blds = self.env['BUILDERS']
726 del blds['SConfSourceBuilder']
727 self.env.Replace( BUILDERS=blds )
728 self.active = 0
729 sconf_global = None
730 if not self.config_h is None:
731 _ac_config_hs[self.config_h] = self.config_h_text
732 self.env.fs = self.lastEnvFs
733
734 -class CheckContext(object):
735 """Provides a context for configure tests. Defines how a test writes to the
736 screen and log file.
737
738 A typical test is just a callable with an instance of CheckContext as
739 first argument:
740
741 def CheckCustom(context, ...)
742 context.Message('Checking my weird test ... ')
743 ret = myWeirdTestFunction(...)
744 context.Result(ret)
745
746 Often, myWeirdTestFunction will be one of
747 context.TryCompile/context.TryLink/context.TryRun. The results of
748 those are cached, for they are only rebuild, if the dependencies have
749 changed.
750 """
751
752 - def __init__(self, sconf):
753 """Constructor. Pass the corresponding SConf instance."""
754 self.sconf = sconf
755 self.did_show_result = 0
756
757
758 self.vardict = {}
759 self.havedict = {}
760 self.headerfilename = None
761 self.config_h = ""
762
763
764
765
766
767
768
769
770 - def Message(self, text):
771 """Inform about what we are doing right now, e.g.
772 'Checking for SOMETHING ... '
773 """
774 self.Display(text)
775 self.sconf.cached = 1
776 self.did_show_result = 0
777
778 - def Result(self, res):
779 """Inform about the result of the test. res may be an integer or a
780 string. In case of an integer, the written text will be 'yes' or 'no'.
781 The result is only displayed when self.did_show_result is not set.
782 """
783 if isinstance(res, (int, bool)):
784 if res:
785 text = "yes"
786 else:
787 text = "no"
788 elif isinstance(res, str):
789 text = res
790 else:
791 raise TypeError("Expected string, int or bool, got " + str(type(res)))
792
793 if self.did_show_result == 0:
794
795 self.Display(text + "\n")
796 self.did_show_result = 1
797
798 - def TryBuild(self, *args, **kw):
799 return self.sconf.TryBuild(*args, **kw)
800
801 - def TryAction(self, *args, **kw):
802 return self.sconf.TryAction(*args, **kw)
803
804 - def TryCompile(self, *args, **kw):
805 return self.sconf.TryCompile(*args, **kw)
806
807 - def TryLink(self, *args, **kw):
808 return self.sconf.TryLink(*args, **kw)
809
810 - def TryRun(self, *args, **kw):
811 return self.sconf.TryRun(*args, **kw)
812
813 - def __getattr__( self, attr ):
814 if( attr == 'env' ):
815 return self.sconf.env
816 elif( attr == 'lastTarget' ):
817 return self.sconf.lastTarget
818 else:
819 raise AttributeError("CheckContext instance has no attribute '%s'" % attr)
820
821
822
823 - def BuildProg(self, text, ext):
824 self.sconf.cached = 1
825
826 return not self.TryBuild(self.env.Program, text, ext)
827
828 - def CompileProg(self, text, ext):
829 self.sconf.cached = 1
830
831 return not self.TryBuild(self.env.Object, text, ext)
832
833 - def CompileSharedObject(self, text, ext):
834 self.sconf.cached = 1
835
836 return not self.TryBuild(self.env.SharedObject, text, ext)
837
838 - def RunProg(self, text, ext):
839 self.sconf.cached = 1
840
841 st, out = self.TryRun(text, ext)
842 return not st, out
843
844 - def AppendLIBS(self, lib_name_list):
845 oldLIBS = self.env.get( 'LIBS', [] )
846 self.env.Append(LIBS = lib_name_list)
847 return oldLIBS
848
849 - def PrependLIBS(self, lib_name_list):
850 oldLIBS = self.env.get( 'LIBS', [] )
851 self.env.Prepend(LIBS = lib_name_list)
852 return oldLIBS
853
854 - def SetLIBS(self, val):
855 oldLIBS = self.env.get( 'LIBS', [] )
856 self.env.Replace(LIBS = val)
857 return oldLIBS
858
859 - def Display(self, msg):
860 if self.sconf.cached:
861
862
863
864 msg = "(cached) " + msg
865 self.sconf.cached = 0
866 progress_display(msg, append_newline=0)
867 self.Log("scons: Configure: " + msg + "\n")
868
869 - def Log(self, msg):
870 if self.sconf.logstream is not None:
871 self.sconf.logstream.write(msg)
872
873
874
875
887
888
889 -def CheckFunc(context, function_name, header = None, language = None):
893
894 -def CheckType(context, type_name, includes = "", language = None):
895 res = SCons.Conftest.CheckType(context, type_name,
896 header = includes, language = language)
897 context.did_show_result = 1
898 return not res
899
900 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
901 res = SCons.Conftest.CheckTypeSize(context, type_name,
902 header = includes, language = language,
903 expect = expect)
904 context.did_show_result = 1
905 return res
906
908 res = SCons.Conftest.CheckDeclaration(context, declaration,
909 includes = includes,
910 language = language)
911 context.did_show_result = 1
912 return not res
913
915
916
917 if not SCons.Util.is_List(headers):
918 headers = [headers]
919 l = []
920 if leaveLast:
921 lastHeader = headers[-1]
922 headers = headers[:-1]
923 else:
924 lastHeader = None
925 for s in headers:
926 l.append("#include %s%s%s\n"
927 % (include_quotes[0], s, include_quotes[1]))
928 return ''.join(l), lastHeader
929
931 """
932 A test for a C or C++ header file.
933 """
934 prog_prefix, hdr_to_check = \
935 createIncludesFromHeaders(header, 1, include_quotes)
936 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
937 language = language,
938 include_quotes = include_quotes)
939 context.did_show_result = 1
940 return not res
941
946
951
956
961
962
963
965 """
966 A test for a C header file.
967 """
968 return CheckHeader(context, header, include_quotes, language = "C")
969
970
971
972
974 """
975 A test for a C++ header file.
976 """
977 return CheckHeader(context, header, include_quotes, language = "C++")
978
979
980 -def CheckLib(context, library = None, symbol = "main",
981 header = None, language = None, autoadd = 1):
982 """
983 A test for a library. See also CheckLibWithHeader.
984 Note that library may also be None to test whether the given symbol
985 compiles without flags.
986 """
987
988 if library == []:
989 library = [None]
990
991 if not SCons.Util.is_List(library):
992 library = [library]
993
994
995 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
996 language = language, autoadd = autoadd)
997 context.did_show_result = 1
998 return not res
999
1000
1001
1002
1005
1006 """
1007 Another (more sophisticated) test for a library.
1008 Checks, if library and header is available for language (may be 'C'
1009 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
1010 As in CheckLib, we support library=None, to test if the call compiles
1011 without extra link flags.
1012 """
1013 prog_prefix, dummy = \
1014 createIncludesFromHeaders(header, 0)
1015 if libs == []:
1016 libs = [None]
1017
1018 if not SCons.Util.is_List(libs):
1019 libs = [libs]
1020
1021 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1022 call = call, language = language, autoadd = autoadd)
1023 context.did_show_result = 1
1024 return not res
1025
1026
1027
1028
1029
1030
1031