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.4.1:3453:73fefd3ea0b0 2015/11/09 03:25:05 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 __slots__ = ('result', 'string')
179
183
187
188
190 """
191 'Sniffer' for a file-like writable object. Similar to the unix tool tee.
192 """
194 self.orig = orig
195 self.s = io.StringIO()
196
198 if self.orig:
199 self.orig.write(str)
200 try:
201 self.s.write(str)
202 except TypeError as e:
203
204 self.s.write(str.decode())
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
359
360 binfo = SConfBuildInfo()
361 binfo.merge(t.get_binfo())
362 binfo.set_build_result(1, s.getvalue())
363 sconsign_entry = SCons.SConsign.SConsignEntry()
364 sconsign_entry.binfo = binfo
365
366
367
368
369
370
371
372 sconsign = t.dir.sconsign()
373 sconsign.set_entry(t.name, sconsign_entry)
374 sconsign.merge()
375 raise e
376 else:
377 for t in self.targets:
378
379
380 binfo = SConfBuildInfo()
381 binfo.merge(t.get_binfo())
382 binfo.set_build_result(0, s.getvalue())
383 sconsign_entry = SCons.SConsign.SConsignEntry()
384 sconsign_entry.binfo = binfo
385
386
387
388
389
390
391
392 sconsign = t.dir.sconsign()
393 sconsign.set_entry(t.name, sconsign_entry)
394 sconsign.merge()
395
397 """This is simply a class to represent a configure context. After
398 creating a SConf object, you can call any tests. After finished with your
399 tests, be sure to call the Finish() method, which returns the modified
400 environment.
401 Some words about caching: In most cases, it is not necessary to cache
402 Test results explicitely. Instead, we use the scons dependency checking
403 mechanism. For example, if one wants to compile a test program
404 (SConf.TryLink), the compiler is only called, if the program dependencies
405 have changed. However, if the program could not be compiled in a former
406 SConf run, we need to explicitely cache this error.
407 """
408
409 - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR',
410 log_file='$CONFIGURELOG', config_h = None, _depth = 0):
411 """Constructor. Pass additional tests in the custom_tests-dictinary,
412 e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest
413 defines a custom test.
414 Note also the conf_dir and log_file arguments (you may want to
415 build tests in the VariantDir, not in the SourceDir)
416 """
417 global SConfFS
418 if not SConfFS:
419 SConfFS = SCons.Node.FS.default_fs or \
420 SCons.Node.FS.FS(env.fs.pathTop)
421 if sconf_global is not None:
422 raise SCons.Errors.UserError
423 self.env = env
424 if log_file is not None:
425 log_file = SConfFS.File(env.subst(log_file))
426 self.logfile = log_file
427 self.logstream = None
428 self.lastTarget = None
429 self.depth = _depth
430 self.cached = 0
431
432
433 default_tests = {
434 'CheckCC' : CheckCC,
435 'CheckCXX' : CheckCXX,
436 'CheckSHCC' : CheckSHCC,
437 'CheckSHCXX' : CheckSHCXX,
438 'CheckFunc' : CheckFunc,
439 'CheckType' : CheckType,
440 'CheckTypeSize' : CheckTypeSize,
441 'CheckDeclaration' : CheckDeclaration,
442 'CheckHeader' : CheckHeader,
443 'CheckCHeader' : CheckCHeader,
444 'CheckCXXHeader' : CheckCXXHeader,
445 'CheckLib' : CheckLib,
446 'CheckLibWithHeader' : CheckLibWithHeader,
447 'CheckProg' : CheckProg,
448 }
449 self.AddTests(default_tests)
450 self.AddTests(custom_tests)
451 self.confdir = SConfFS.Dir(env.subst(conf_dir))
452 if config_h is not None:
453 config_h = SConfFS.File(config_h)
454 self.config_h = config_h
455 self._startup()
456
458 """Call this method after finished with your tests:
459 env = sconf.Finish()
460 """
461 self._shutdown()
462 return self.env
463
464 - def Define(self, name, value = None, comment = None):
465 """
466 Define a pre processor symbol name, with the optional given value in the
467 current config header.
468
469 If value is None (default), then #define name is written. If value is not
470 none, then #define name value is written.
471
472 comment is a string which will be put as a C comment in the
473 header, to explain the meaning of the value (appropriate C comments /* and
474 */ will be put automatically."""
475 lines = []
476 if comment:
477 comment_str = "/* %s */" % comment
478 lines.append(comment_str)
479
480 if value is not None:
481 define_str = "#define %s %s" % (name, value)
482 else:
483 define_str = "#define %s" % name
484 lines.append(define_str)
485 lines.append('')
486
487 self.config_h_text = self.config_h_text + '\n'.join(lines)
488
541
543 """Wrapper function for handling piped spawns.
544
545 This looks to the calling interface (in Action.py) like a "normal"
546 spawn, but associates the call with the PSPAWN variable from
547 the construction environment and with the streams to which we
548 want the output logged. This gets slid into the construction
549 environment as the SPAWN variable so Action.py doesn't have to
550 know or care whether it's spawning a piped command or not.
551 """
552 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
553
554
555 - def TryBuild(self, builder, text = None, extension = ""):
556 """Low level TryBuild implementation. Normally you don't need to
557 call that - you can use TryCompile / TryLink / TryRun instead
558 """
559 global _ac_build_counter
560
561
562
563 try:
564 self.pspawn = self.env['PSPAWN']
565 except KeyError:
566 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
567 try:
568 save_spawn = self.env['SPAWN']
569 except KeyError:
570 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
571
572 nodesToBeBuilt = []
573
574 f = "conftest_" + str(_ac_build_counter)
575 pref = self.env.subst( builder.builder.prefix )
576 suff = self.env.subst( builder.builder.suffix )
577 target = self.confdir.File(pref + f + suff)
578
579 try:
580
581
582 self.env['SPAWN'] = self.pspawn_wrapper
583 sourcetext = self.env.Value(text)
584
585 if text is not None:
586 textFile = self.confdir.File(f + extension)
587 textFileNode = self.env.SConfSourceBuilder(target=textFile,
588 source=sourcetext)
589 nodesToBeBuilt.extend(textFileNode)
590 source = textFileNode
591 else:
592 source = None
593
594 nodes = builder(target = target, source = source)
595 if not SCons.Util.is_List(nodes):
596 nodes = [nodes]
597 nodesToBeBuilt.extend(nodes)
598 result = self.BuildNodes(nodesToBeBuilt)
599
600 finally:
601 self.env['SPAWN'] = save_spawn
602
603 _ac_build_counter = _ac_build_counter + 1
604 if result:
605 self.lastTarget = nodes[0]
606 else:
607 self.lastTarget = None
608
609 return result
610
611 - def TryAction(self, action, text = None, extension = ""):
612 """Tries to execute the given action with optional source file
613 contents <text> and optional source file extension <extension>,
614 Returns the status (0 : failed, 1 : ok) and the contents of the
615 output file.
616 """
617 builder = SCons.Builder.Builder(action=action)
618 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
619 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
620 del self.env['BUILDERS']['SConfActionBuilder']
621 if ok:
622 outputStr = self.lastTarget.get_contents()
623 return (1, outputStr)
624 return (0, "")
625
627 """Compiles the program given in text to an env.Object, using extension
628 as file extension (e.g. '.c'). Returns 1, if compilation was
629 successful, 0 otherwise. The target is saved in self.lastTarget (for
630 further processing).
631 """
632 return self.TryBuild(self.env.Object, text, extension)
633
634 - def TryLink( self, text, extension ):
635 """Compiles the program given in text to an executable env.Program,
636 using extension as file extension (e.g. '.c'). Returns 1, if
637 compilation was successful, 0 otherwise. The target is saved in
638 self.lastTarget (for further processing).
639 """
640 return self.TryBuild(self.env.Program, text, extension )
641
642 - def TryRun(self, text, extension ):
643 """Compiles and runs the program given in text, using extension
644 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
645 (0, '') otherwise. The target (a file containing the program's stdout)
646 is saved in self.lastTarget (for further processing).
647 """
648 ok = self.TryLink(text, extension)
649 if( ok ):
650 prog = self.lastTarget
651 pname = prog.get_internal_path()
652 output = self.confdir.File(os.path.basename(pname)+'.out')
653 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
654 ok = self.BuildNodes(node)
655 if ok:
656 outputStr = output.get_contents()
657 return( 1, outputStr)
658 return (0, "")
659
661 """A wrapper around Tests (to ensure sanity)"""
663 self.test = test
664 self.sconf = sconf
666 if not self.sconf.active:
667 raise SCons.Errors.UserError
668 context = CheckContext(self.sconf)
669 ret = self.test(context, *args, **kw)
670 if self.sconf.config_h is not None:
671 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
672 context.Result("error: no result")
673 return ret
674
675 - def AddTest(self, test_name, test_instance):
676 """Adds test_class to this SConf instance. It can be called with
677 self.test_name(...)"""
678 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
679
681 """Adds all the tests given in the tests dictionary to this SConf
682 instance
683 """
684 for name in tests.keys():
685 self.AddTest(name, tests[name])
686
695
741
743 """Private method. Reset to non-piped spawn"""
744 global sconf_global, _ac_config_hs
745
746 if not self.active:
747 raise SCons.Errors.UserError("Finish may be called only once!")
748 if self.logstream is not None and not dryrun:
749 self.logstream.write("\n")
750 self.logstream.close()
751 self.logstream = None
752
753 blds = self.env['BUILDERS']
754 del blds['SConfSourceBuilder']
755 self.env.Replace( BUILDERS=blds )
756 self.active = 0
757 sconf_global = None
758 if not self.config_h is None:
759 _ac_config_hs[self.config_h] = self.config_h_text
760 self.env.fs = self.lastEnvFs
761
762 -class CheckContext(object):
763 """Provides a context for configure tests. Defines how a test writes to the
764 screen and log file.
765
766 A typical test is just a callable with an instance of CheckContext as
767 first argument:
768
769 def CheckCustom(context, ...)
770 context.Message('Checking my weird test ... ')
771 ret = myWeirdTestFunction(...)
772 context.Result(ret)
773
774 Often, myWeirdTestFunction will be one of
775 context.TryCompile/context.TryLink/context.TryRun. The results of
776 those are cached, for they are only rebuild, if the dependencies have
777 changed.
778 """
779
780 - def __init__(self, sconf):
781 """Constructor. Pass the corresponding SConf instance."""
782 self.sconf = sconf
783 self.did_show_result = 0
784
785
786 self.vardict = {}
787 self.havedict = {}
788 self.headerfilename = None
789 self.config_h = ""
790
791
792
793
794
795
796
797
798 - def Message(self, text):
799 """Inform about what we are doing right now, e.g.
800 'Checking for SOMETHING ... '
801 """
802 self.Display(text)
803 self.sconf.cached = 1
804 self.did_show_result = 0
805
806 - def Result(self, res):
807 """Inform about the result of the test. If res is not a string, displays
808 'yes' or 'no' depending on whether res is evaluated as true or false.
809 The result is only displayed when self.did_show_result is not set.
810 """
811 if isinstance(res, str):
812 text = res
813 elif res:
814 text = "yes"
815 else:
816 text = "no"
817
818 if self.did_show_result == 0:
819
820 self.Display(text + "\n")
821 self.did_show_result = 1
822
823 - def TryBuild(self, *args, **kw):
824 return self.sconf.TryBuild(*args, **kw)
825
826 - def TryAction(self, *args, **kw):
827 return self.sconf.TryAction(*args, **kw)
828
829 - def TryCompile(self, *args, **kw):
830 return self.sconf.TryCompile(*args, **kw)
831
832 - def TryLink(self, *args, **kw):
833 return self.sconf.TryLink(*args, **kw)
834
835 - def TryRun(self, *args, **kw):
836 return self.sconf.TryRun(*args, **kw)
837
838 - def __getattr__( self, attr ):
839 if( attr == 'env' ):
840 return self.sconf.env
841 elif( attr == 'lastTarget' ):
842 return self.sconf.lastTarget
843 else:
844 raise AttributeError("CheckContext instance has no attribute '%s'" % attr)
845
846
847
848 - def BuildProg(self, text, ext):
849 self.sconf.cached = 1
850
851 return not self.TryBuild(self.env.Program, text, ext)
852
853 - def CompileProg(self, text, ext):
854 self.sconf.cached = 1
855
856 return not self.TryBuild(self.env.Object, text, ext)
857
858 - def CompileSharedObject(self, text, ext):
859 self.sconf.cached = 1
860
861 return not self.TryBuild(self.env.SharedObject, text, ext)
862
863 - def RunProg(self, text, ext):
864 self.sconf.cached = 1
865
866 st, out = self.TryRun(text, ext)
867 return not st, out
868
869 - def AppendLIBS(self, lib_name_list):
870 oldLIBS = self.env.get( 'LIBS', [] )
871 self.env.Append(LIBS = lib_name_list)
872 return oldLIBS
873
874 - def PrependLIBS(self, lib_name_list):
875 oldLIBS = self.env.get( 'LIBS', [] )
876 self.env.Prepend(LIBS = lib_name_list)
877 return oldLIBS
878
879 - def SetLIBS(self, val):
880 oldLIBS = self.env.get( 'LIBS', [] )
881 self.env.Replace(LIBS = val)
882 return oldLIBS
883
884 - def Display(self, msg):
885 if self.sconf.cached:
886
887
888
889 msg = "(cached) " + msg
890 self.sconf.cached = 0
891 progress_display(msg, append_newline=0)
892 self.Log("scons: Configure: " + msg + "\n")
893
894 - def Log(self, msg):
895 if self.sconf.logstream is not None:
896 self.sconf.logstream.write(msg)
897
898
899
900
912
913
914 -def CheckFunc(context, function_name, header = None, language = None):
918
919 -def CheckType(context, type_name, includes = "", language = None):
924
925 -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None):
926 res = SCons.Conftest.CheckTypeSize(context, type_name,
927 header = includes, language = language,
928 expect = expect)
929 context.did_show_result = 1
930 return res
931
938
940
941
942 if not SCons.Util.is_List(headers):
943 headers = [headers]
944 l = []
945 if leaveLast:
946 lastHeader = headers[-1]
947 headers = headers[:-1]
948 else:
949 lastHeader = None
950 for s in headers:
951 l.append("#include %s%s%s\n"
952 % (include_quotes[0], s, include_quotes[1]))
953 return ''.join(l), lastHeader
954
956 """
957 A test for a C or C++ header file.
958 """
959 prog_prefix, hdr_to_check = \
960 createIncludesFromHeaders(header, 1, include_quotes)
961 res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix,
962 language = language,
963 include_quotes = include_quotes)
964 context.did_show_result = 1
965 return not res
966
971
976
981
986
987
988
990 """
991 A test for a C header file.
992 """
993 return CheckHeader(context, header, include_quotes, language = "C")
994
995
996
997
999 """
1000 A test for a C++ header file.
1001 """
1002 return CheckHeader(context, header, include_quotes, language = "C++")
1003
1004
1005 -def CheckLib(context, library = None, symbol = "main",
1006 header = None, language = None, autoadd = 1):
1007 """
1008 A test for a library. See also CheckLibWithHeader.
1009 Note that library may also be None to test whether the given symbol
1010 compiles without flags.
1011 """
1012
1013 if library == []:
1014 library = [None]
1015
1016 if not SCons.Util.is_List(library):
1017 library = [library]
1018
1019
1020 res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
1021 language = language, autoadd = autoadd)
1022 context.did_show_result = 1
1023 return not res
1024
1025
1026
1027
1030
1031 """
1032 Another (more sophisticated) test for a library.
1033 Checks, if library and header is available for language (may be 'C'
1034 or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'.
1035 As in CheckLib, we support library=None, to test if the call compiles
1036 without extra link flags.
1037 """
1038 prog_prefix, dummy = \
1039 createIncludesFromHeaders(header, 0)
1040 if libs == []:
1041 libs = [None]
1042
1043 if not SCons.Util.is_List(libs):
1044 libs = [libs]
1045
1046 res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
1047 call = call, language = language, autoadd = autoadd)
1048 context.did_show_result = 1
1049 return not res
1050
1052 """Simple check if a program exists in the path. Returns the path
1053 for the application, or None if not found.
1054 """
1055 res = SCons.Conftest.CheckProg(context, prog_name)
1056 context.did_show_result = 1
1057 return res
1058
1059
1060
1061
1062
1063
1064