1 """SCons.SConf
2
3 Autoconf-like configuration support.
4
5 In other words, this package allows to run series of tests to detect
6 capabilities of current system and generate config files (header files
7 in C/C++) that turn on system-specific options and optimizations.
8
9 For example, it is possible to detect if optional libraries are present
10 on current system and generate config that makes compiler include them.
11 C compilers do not have ability to catch ImportError if some library is
12 not found, so these checks should be done externally.
13 """
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 __revision__ = "src/engine/SCons/SConf.py 2014/07/05 09:42:21 garyo"
39
40 import SCons.compat
41
42 import io
43 import os
44 import re
45 import sys
46 import traceback
47
48 import SCons.Action
49 import SCons.Builder
50 import SCons.Errors
51 import SCons.Job
52 import SCons.Node.FS
53 import SCons.Taskmaster
54 import SCons.Util
55 import SCons.Warnings
56 import SCons.Conftest
57
58 from SCons.Debug import Trace
59
60
61 SCons.Conftest.LogInputFiles = 0
62 SCons.Conftest.LogErrorMessages = 0
63
64
65 build_type = None
66 build_types = ['clean', 'help']
67
71
72
73 dryrun = 0
74
75 AUTO=0
76 FORCE=1
77 CACHE=2
78 cache_mode = AUTO
79
81 """Set the Configure cache mode. mode must be one of "auto", "force",
82 or "cache"."""
83 global cache_mode
84 if mode == "auto":
85 cache_mode = AUTO
86 elif mode == "force":
87 cache_mode = FORCE
88 elif mode == "cache":
89 cache_mode = CACHE
90 else:
91 raise ValueError("SCons.SConf.SetCacheMode: Unknown mode " + mode)
92
93 progress_display = SCons.Util.display
98
99 SConfFS = None
100
101 _ac_build_counter = 0
102 _ac_config_logs = {}
103 _ac_config_hs = {}
104 sconf_global = None
105
107 t = open(str(target[0]), "w")
108 defname = re.sub('[^A-Za-z0-9_]', '_', str(target[0]).upper())
109 t.write("""#ifndef %(DEFNAME)s_SEEN
110 #define %(DEFNAME)s_SEEN
111
112 """ % {'DEFNAME' : defname})
113 t.write(source[0].get_contents())
114 t.write("""
115 #endif /* %(DEFNAME)s_SEEN */
116 """ % {'DEFNAME' : defname})
117 t.close()
118
120 return "scons: Configure: creating " + str(target[0])
121
122
124 if len(_ac_config_hs) == 0:
125 return False
126 else:
127 return True
128
137
138
141 SCons.Warnings.enableWarningClass(SConfWarning)
142
143
147
157
163
164
170 return (str(target[0]) + ' <-\n |' +
171 source[0].get_contents().replace( '\n', "\n |" ) )
172
174 """
175 Special build info for targets of configure tests. Additional members
176 are result (did the builder succeed last time?) and string, which
177 contains messages of the original build phase.
178 """
179 result = None
180 string = None
181
185
186
188 """
189 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
190 """
192 self.orig = orig
193 self.s = io.StringIO()
194
196 if self.orig:
197 self.orig.write(str)
198 try:
199 self.s.write(str)
200 except TypeError as e:
201 if e.message.startswith('unicode argument expected'):
202 self.s.write(str.decode())
203 else:
204 raise
205
207 for l in lines:
208 self.write(l + '\n')
209
211 """
212 Return everything written to orig since the Streamer was created.
213 """
214 return self.s.getvalue()
215
217 if self.orig:
218 self.orig.flush()
219 self.s.flush()
220
221
223 """
224 This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
225 correctly and knows about the current cache_mode.
226 """
230
232 """
233 Logs the original builder messages, given the SConfBuildInfo instance
234 bi.
235 """
236 if not isinstance(bi, SConfBuildInfo):
237 SCons.Warnings.warn(SConfWarning,
238 "The stored build information has an unexpected class: %s" % bi.__class__)
239 else:
240 self.display("The original builder output was:\n" +
241 (" |" + str(bi.string)).replace("\n", "\n |"))
242
244
245
246 exc_type = self.exc_info()[0]
247 if issubclass(exc_type, SConfError):
248 raise
249 elif issubclass(exc_type, SCons.Errors.BuildError):
250
251
252
253 self.exc_clear()
254 else:
255 self.display('Caught exception while building "%s":\n' %
256 self.targets[0])
257 try:
258 excepthook = sys.excepthook
259 except AttributeError:
260
261 def excepthook(type, value, tb):
262 traceback.print_tb(tb)
263 print type, value
264 excepthook(*self.exc_info())
265 return SCons.Taskmaster.Task.failed(self)
266
301
342 if env.decide_source.func_code is not force_build.func_code:
343 env.Decider(force_build)
344 env['PSTDOUT'] = env['PSTDERR'] = s
345 try:
346 sconf.cached = 0
347 self.targets[0].build()
348 finally:
349 sys.stdout = sys.stderr = env['PSTDOUT'] = \
350 env['PSTDERR'] = sconf.logstream
351 except KeyboardInterrupt:
352 raise
353 except SystemExit:
354 exc_value = sys.exc_info()[1]
355 raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
356 except Exception, e:
357 for t in self.targets:
358 binfo = t.get_binfo()
359 binfo.__class__ = SConfBuildInfo
360 binfo.set_build_result(1, s.getvalue())
361 sconsign_entry = SCons.SConsign.SConsignEntry()
362 sconsign_entry.binfo = binfo
363
364
365
366
367
368
369
370 sconsign = t.dir.sconsign()
371 sconsign.set_entry(t.name, sconsign_entry)
372 sconsign.merge()
373 raise e
374 else:
375 for t in self.targets:
376 binfo = t.get_binfo()
377 binfo.__class__ = SConfBuildInfo
378 binfo.set_build_result(0, s.getvalue())
379 sconsign_entry = SCons.SConsign.SConsignEntry()
380 sconsign_entry.binfo = binfo
381
382
383
384
385
386
387
388 sconsign = t.dir.sconsign()
389 sconsign.set_entry(t.name, sconsign_entry)
390 sconsign.merge()
391
393 """This is simply a class to represent a configure context. After
394 creating a SConf object, you can call any tests. After finished with your
395 tests, be sure to call the Finish() method, which returns the modified
396 environment.
397 Some words about caching: In most cases, it is not necessary to cache
398 Test results explicitely. Instead, we use the scons dependency checking
399 mechanism. For example, if one wants to compile a test program
400 (SConf.TryLink), the compiler is only called, if the program dependencies
401 have changed. However, if the program could not be compiled in a former
402 SConf run, we need to explicitely cache this error.
403 """
404
405 - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
406 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
407 """Constructor. Pass additional tests in the custom_tests-dictinary,
408 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
409 defines a custom test.
410 Note also the conf_dir and log_file arguments (you may want to
411 build tests in the VariantDir, not in the SourceDir)
412 """
413 global SConfFS
414 if not SConfFS:
415 SConfFS = SCons.Node.FS.default_fs or \
416 SCons.Node.FS.FS(env.fs.pathTop)
417 if sconf_global is not None:
418 raise SCons.Errors.UserError
419 self.env = env
420 if log_file is not None:
421 log_file = SConfFS.File(env.subst(log_file))
422 self.logfile = log_file
423 self.logstream = None
424 self.lastTarget = None
425 self.depth = _depth
426 self.cached = 0
427
428
429 default_tests = {
430 'CheckCC' : CheckCC,
431 'CheckCXX' : CheckCXX,
432 'CheckSHCC' : CheckSHCC,
433 'CheckSHCXX' : CheckSHCXX,
434 'CheckFunc' : CheckFunc,
435 'CheckType' : CheckType,
436 'CheckTypeSize' : CheckTypeSize,
437 'CheckDeclaration' : CheckDeclaration,
438 'CheckHeader' : CheckHeader,
439 'CheckCHeader' : CheckCHeader,
440 'CheckCXXHeader' : CheckCXXHeader,
441 'CheckLib' : CheckLib,
442 'CheckLibWithHeader' : CheckLibWithHeader,
443 }
444 self.AddTests(default_tests)
445 self.AddTests(custom_tests)
446 self.confdir = SConfFS.Dir(env.subst(conf_dir))
447 if config_h is not None:
448 config_h = SConfFS.File(config_h)
449 self.config_h = config_h
450 self._startup()
451
453 """Call this method after finished with your tests:
454 env = sconf.Finish()
455 """
456 self._shutdown()
457 return self.env
458
459 - def Define(self, name, value = None, comment = None):
460 """
461 Define a pre processor symbol name, with the optional given value in the
462 current config header.
463
464 If value is None (default), then #define name is written. If value is not
465 none, then #define name value is written.
466
467 comment is a string which will be put as a C comment in the
468 header, to explain the meaning of the value (appropriate C comments /* and
469 */ will be put automatically."""
470 lines = []
471 if comment:
472 comment_str = "/* %s */" % comment
473 lines.append(comment_str)
474
475 if value is not None:
476 define_str = "#define %s %s" % (name, value)
477 else:
478 define_str = "#define %s" % name
479 lines.append(define_str)
480 lines.append('')
481
482 self.config_h_text = self.config_h_text + '\n'.join(lines)
483
536
538 """Wrapper function for handling piped spawns.
539
540 This looks to the calling interface (in Action.py) like a "normal"
541 spawn, but associates the call with the PSPAWN variable from
542 the construction environment and with the streams to which we
543 want the output logged. This gets slid into the construction
544 environment as the SPAWN variable so Action.py doesn't have to
545 know or care whether it's spawning a piped command or not.
546 """
547 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
548
549
550 - def TryBuild(self, builder, text = None, extension = ""):
551 """Low level TryBuild implementation. Normally you don't need to
552 call that - you can use TryCompile / TryLink / TryRun instead
553 """
554 global _ac_build_counter
555
556
557
558 try:
559 self.pspawn = self.env['PSPAWN']
560 except KeyError:
561 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
562 try:
563 save_spawn = self.env['SPAWN']
564 except KeyError:
565 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
566
567 nodesToBeBuilt = []
568
569 f = "conftest_" + str(_ac_build_counter)
570 pref = self.env.subst( builder.builder.prefix )
571 suff = self.env.subst( builder.builder.suffix )
572 target = self.confdir.File(pref + f + suff)
573
574 try:
575
576
577 self.env['SPAWN'] = self.pspawn_wrapper
578 sourcetext = self.env.Value(text)
579
580 if text is not None:
581 textFile = self.confdir.File(f + extension)
582 textFileNode = self.env.SConfSourceBuilder(target=textFile,
583 source=sourcetext)
584 nodesToBeBuilt.extend(textFileNode)
585 source = textFileNode
586 else:
587 source = None
588
589 nodes = builder(target = target, source = source)
590 if not SCons.Util.is_List(nodes):
591 nodes = [nodes]
592 nodesToBeBuilt.extend(nodes)
593 result = self.BuildNodes(nodesToBeBuilt)
594
595 finally:
596 self.env['SPAWN'] = save_spawn
597
598 _ac_build_counter = _ac_build_counter + 1
599 if result:
600 self.lastTarget = nodes[0]
601 else:
602 self.lastTarget = None
603
604 return result
605
606 - def TryAction(self, action, text = None, extension = ""):
607 """Tries to execute the given action with optional source file
608 contents <text> and optional source file extension <extension>,
609 Returns the status (0 : failed, 1 : ok) and the contents of the
610 output file.
611 """
612 builder = SCons.Builder.Builder(action=action)
613 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
614 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
615 del self.env['BUILDERS']['SConfActionBuilder']
616 if ok:
617 outputStr = self.lastTarget.get_contents()
618 return (1, outputStr)
619 return (0, "")
620
622 """Compiles the program given in text to an env.Object, using extension
623 as file extension (e.g. '.c'). Returns 1, if compilation was
624 successful, 0 otherwise. The target is saved in self.lastTarget (for
625 further processing).
626 """
627 return self.TryBuild(self.env.Object, text, extension)
628
629 - def TryLink( self, text, extension ):
630 """Compiles the program given in text to an executable env.Program,
631 using extension as file extension (e.g. '.c'). Returns 1, if
632 compilation was successful, 0 otherwise. The target is saved in
633 self.lastTarget (for further processing).
634 """
635 return self.TryBuild(self.env.Program, text, extension )
636
637 - def TryRun(self, text, extension ):
638 """Compiles and runs the program given in text, using extension
639 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
640 (0, '') otherwise. The target (a file containing the program's stdout)
641 is saved in self.lastTarget (for further processing).
642 """
643 ok = self.TryLink(text, extension)
644 if( ok ):
645 prog = self.lastTarget
646 pname = prog.path
647 output = self.confdir.File(os.path.basename(pname)+'.out')
648 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
649 ok = self.BuildNodes(node)
650 if ok:
651 outputStr = output.get_contents()
652 return( 1, outputStr)
653 return (0, "")
654
656 """A wrapper around Tests (to ensure sanity)"""
658 self.test = test
659 self.sconf = sconf
661 if not self.sconf.active:
662 raise SCons.Errors.UserError
663 context = CheckContext(self.sconf)
664 ret = self.test(context, *args, **kw)
665 if self.sconf.config_h is not None:
666 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
667 context.Result("error: no result")
668 return ret
669
670 - def AddTest(self, test_name, test_instance):
671 """Adds test_class to this SConf instance. It can be called with
672 self.test_name(...)"""
673 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
674
676 """Adds all the tests given in the tests dictionary to this SConf
677 instance
678 """
679 for name in tests.keys():
680 self.AddTest(name, tests[name])
681
691
737
739 """Private method. Reset to non-piped spawn"""
740 global sconf_global, _ac_config_hs
741
742 if not self.active:
743 raise SCons.Errors.UserError("Finish may be called only once!")
744 if self.logstream is not None and not dryrun:
745 self.logstream.write("\n")
746 self.logstream.close()
747 self.logstream = None
748
749 blds = self.env['BUILDERS']
750 del blds['SConfSourceBuilder']
751 self.env.Replace( BUILDERS=blds )
752 self.active = 0
753 sconf_global = None
754 if not self.config_h is None:
755 _ac_config_hs[self.config_h] = self.config_h_text
756 self.env.fs = self.lastEnvFs
757
758 -class CheckContext(object):
759 """Provides a context for configure tests. Defines how a test writes to the
760 screen and log file.
761
762 A typical test is just a callable with an instance of CheckContext as
763 first argument:
764
765 def CheckCustom(context, ...)
766 context.Message('Checking my weird test ... ')
767 ret = myWeirdTestFunction(...)
768 context.Result(ret)
769
770 Often, myWeirdTestFunction will be one of
771 context.TryCompile/context.TryLink/context.TryRun. The results of
772 those are cached, for they are only rebuild, if the dependencies have
773 changed.
774 """
775
776 - def __init__(self, sconf):
777 """Constructor. Pass the corresponding SConf instance."""
778 self.sconf = sconf
779 self.did_show_result = 0
780
781
782 self.vardict = {}
783 self.havedict = {}
784 self.headerfilename = None
785 self.config_h = ""
786
787
788
789
790
791
792
793
794 - def Message(self, text):
795 """Inform about what we are doing right now, e.g.
796 'Checking for SOMETHING ... '
797 """
798 self.Display(text)
799 self.sconf.cached = 1
800 self.did_show_result = 0
801
802 - def Result(self, res):
803 """Inform about the result of the test. If res is not a string, displays
804 'yes' or 'no' depending on whether res is evaluated as true or false.
805 The result is only displayed when self.did_show_result is not set.
806 """
807 if isinstance(res, str):
808 text = res
809 elif res:
810 text = "yes"
811 else:
812 text = "no"
813
814 if self.did_show_result == 0:
815
816 self.Display(text + "\n")
817 self.did_show_result = 1
818
819 - def TryBuild(self, *args, **kw):
820 return self.sconf.TryBuild(*args, **kw)
821
822 - def TryAction(self, *args, **kw):
823 return self.sconf.TryAction(*args, **kw)
824
825 - def TryCompile(self, *args, **kw):
826 return self.sconf.TryCompile(*args, **kw)
827
828 - def TryLink(self, *args, **kw):
829 return self.sconf.TryLink(*args, **kw)
830
831 - def TryRun(self, *args, **kw):
832 return self.sconf.TryRun(*args, **kw)
833
834 - def __getattr__( self, attr ):
835 if( attr == 'env' ):
836 return self.sconf.env
837 elif( attr == 'lastTarget' ):
838 return self.sconf.lastTarget
839 else:
840 raise AttributeError("CheckContext instance has no attribute '%s'" % attr)
841
842
843
844 - def BuildProg(self, text, ext):
845 self.sconf.cached = 1
846
847 return not self.TryBuild(self.env.Program, text, ext)
848
849 - def CompileProg(self, text, ext):
850 self.sconf.cached = 1
851
852 return not self.TryBuild(self.env.Object, text, ext)
853
854 - def CompileSharedObject(self, text, ext):
855 self.sconf.cached = 1
856
857 return not self.TryBuild(self.env.SharedObject, text, ext)
858
859 - def RunProg(self, text, ext):
860 self.sconf.cached = 1
861
862 st, out = self.TryRun(text, ext)
863 return not st, out
864
865 - def AppendLIBS(self, lib_name_list):
866 oldLIBS = self.env.get( 'LIBS', [] )
867 self.env.Append(LIBS = lib_name_list)
868 return oldLIBS
869
870 - def PrependLIBS(self, lib_name_list):
871 oldLIBS = self.env.get( 'LIBS', [] )
872 self.env.Prepend(LIBS = lib_name_list)
873 return oldLIBS
874
875 - def SetLIBS(self, val):
876 oldLIBS = self.env.get( 'LIBS', [] )
877 self.env.Replace(LIBS = val)
878 return oldLIBS
879
880 - def Display(self, msg):
881 if self.sconf.cached:
882
883
884
885 msg = "(cached) " + msg
886 self.sconf.cached = 0
887 progress_display(msg, append_newline=0)
888 self.Log("scons: Configure: " + msg + "\n")
889
890 - def Log(self, msg):
891 if self.sconf.logstream is not None:
892 self.sconf.logstream.write(msg)
893
894
895
896
908
909
910 -def CheckFunc(context, function_name, header = None, language = None):
914
915 -def CheckType(context, type_name, includes = "", language = None):
916 res = SCons.Conftest.CheckType(context, type_name,
917 header = includes, language = language)
918 context.did_show_result = 1
919 return not res
920
921 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
922 res = SCons.Conftest.CheckTypeSize(context, type_name,
923 header = includes, language = language,
924 expect = expect)
925 context.did_show_result = 1
926 return res
927
929 res = SCons.Conftest.CheckDeclaration(context, declaration,
930 includes = includes,
931 language = language)
932 context.did_show_result = 1
933 return not res
934
936
937
938 if not SCons.Util.is_List(headers):
939 headers = [headers]
940 l = []
941 if leaveLast:
942 lastHeader = headers[-1]
943 headers = headers[:-1]
944 else:
945 lastHeader = None
946 for s in headers:
947 l.append("#include %s%s%s\n"
948 % (include_quotes[0], s, include_quotes[1]))
949 return ''.join(l), lastHeader
950
952 """
953 A test for a C or C++ header file.
954 """
955 prog_prefix, hdr_to_check = \
956 createIncludesFromHeaders(header, 1, include_quotes)
957 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
958 language = language,
959 include_quotes = include_quotes)
960 context.did_show_result = 1
961 return not res
962
967
972
977
982
983
984
986 """
987 A test for a C header file.
988 """
989 return CheckHeader(context, header, include_quotes, language = "C")
990
991
992
993
995 """
996 A test for a C++ header file.
997 """
998 return CheckHeader(context, header, include_quotes, language = "C++")
999
1000
1001 -def CheckLib(context, library = None, symbol = "main",
1002 header = None, language = None, autoadd = 1):
1003 """
1004 A test for a library. See also CheckLibWithHeader.
1005 Note that library may also be None to test whether the given symbol
1006 compiles without flags.
1007 """
1008
1009 if library == []:
1010 library = [None]
1011
1012 if not SCons.Util.is_List(library):
1013 library = [library]
1014
1015
1016 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
1017 language = language, autoadd = autoadd)
1018 context.did_show_result = 1
1019 return not res
1020
1021
1022
1023
1026
1027 """
1028 Another (more sophisticated) test for a library.
1029 Checks, if library and header is available for language (may be 'C'
1030 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
1031 As in CheckLib, we support library=None, to test if the call compiles
1032 without extra link flags.
1033 """
1034 prog_prefix, dummy = \
1035 createIncludesFromHeaders(header, 0)
1036 if libs == []:
1037 libs = [None]
1038
1039 if not SCons.Util.is_List(libs):
1040 libs = [libs]
1041
1042 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1043 call = call, language = language, autoadd = autoadd)
1044 context.did_show_result = 1
1045 return not res
1046
1047
1048
1049
1050
1051
1052