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