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 2014/03/02 14:18:15 garyo"
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
515
517 """Wrapper function for handling piped spawns.
518
519 This looks to the calling interface (in Action.py) like a "normal"
520 spawn, but associates the call with the PSPAWN variable from
521 the construction environment and with the streams to which we
522 want the output logged. This gets slid into the construction
523 environment as the SPAWN variable so Action.py doesn't have to
524 know or care whether it's spawning a piped command or not.
525 """
526 return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream)
527
528
529 - def TryBuild(self, builder, text = None, extension = ""):
530 """Low level TryBuild implementation. Normally you don't need to
531 call that - you can use TryCompile / TryLink / TryRun instead
532 """
533 global _ac_build_counter
534
535
536
537 try:
538 self.pspawn = self.env['PSPAWN']
539 except KeyError:
540 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
541 try:
542 save_spawn = self.env['SPAWN']
543 except KeyError:
544 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
545
546 nodesToBeBuilt = []
547
548 f = "conftest_" + str(_ac_build_counter)
549 pref = self.env.subst( builder.builder.prefix )
550 suff = self.env.subst( builder.builder.suffix )
551 target = self.confdir.File(pref + f + suff)
552
553 try:
554
555
556 self.env['SPAWN'] = self.pspawn_wrapper
557 sourcetext = self.env.Value(text)
558
559 if text is not None:
560 textFile = self.confdir.File(f + extension)
561 textFileNode = self.env.SConfSourceBuilder(target=textFile,
562 source=sourcetext)
563 nodesToBeBuilt.extend(textFileNode)
564 source = textFileNode
565 else:
566 source = None
567
568 nodes = builder(target = target, source = source)
569 if not SCons.Util.is_List(nodes):
570 nodes = [nodes]
571 nodesToBeBuilt.extend(nodes)
572 result = self.BuildNodes(nodesToBeBuilt)
573
574 finally:
575 self.env['SPAWN'] = save_spawn
576
577 _ac_build_counter = _ac_build_counter + 1
578 if result:
579 self.lastTarget = nodes[0]
580 else:
581 self.lastTarget = None
582
583 return result
584
585 - def TryAction(self, action, text = None, extension = ""):
586 """Tries to execute the given action with optional source file
587 contents <text> and optional source file extension <extension>,
588 Returns the status (0 : failed, 1 : ok) and the contents of the
589 output file.
590 """
591 builder = SCons.Builder.Builder(action=action)
592 self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} )
593 ok = self.TryBuild(self.env.SConfActionBuilder, text, extension)
594 del self.env['BUILDERS']['SConfActionBuilder']
595 if ok:
596 outputStr = self.lastTarget.get_contents()
597 return (1, outputStr)
598 return (0, "")
599
601 """Compiles the program given in text to an env.Object, using extension
602 as file extension (e.g. '.c'). Returns 1, if compilation was
603 successful, 0 otherwise. The target is saved in self.lastTarget (for
604 further processing).
605 """
606 return self.TryBuild(self.env.Object, text, extension)
607
608 - def TryLink( self, text, extension ):
609 """Compiles the program given in text to an executable env.Program,
610 using extension as file extension (e.g. '.c'). Returns 1, if
611 compilation was successful, 0 otherwise. The target is saved in
612 self.lastTarget (for further processing).
613 """
614 return self.TryBuild(self.env.Program, text, extension )
615
616 - def TryRun(self, text, extension ):
617 """Compiles and runs the program given in text, using extension
618 as file extension (e.g. '.c'). Returns (1, outputStr) on success,
619 (0, '') otherwise. The target (a file containing the program's stdout)
620 is saved in self.lastTarget (for further processing).
621 """
622 ok = self.TryLink(text, extension)
623 if( ok ):
624 prog = self.lastTarget
625 pname = prog.path
626 output = self.confdir.File(os.path.basename(pname)+'.out')
627 node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
628 ok = self.BuildNodes(node)
629 if ok:
630 outputStr = output.get_contents()
631 return( 1, outputStr)
632 return (0, "")
633
635 """A wrapper around Tests (to ensure sanity)"""
637 self.test = test
638 self.sconf = sconf
640 if not self.sconf.active:
641 raise SCons.Errors.UserError
642 context = CheckContext(self.sconf)
643 ret = self.test(context, *args, **kw)
644 if self.sconf.config_h is not None:
645 self.sconf.config_h_text = self.sconf.config_h_text + context.config_h
646 context.Result("error: no result")
647 return ret
648
649 - def AddTest(self, test_name, test_instance):
650 """Adds test_class to this SConf instance. It can be called with
651 self.test_name(...)"""
652 setattr(self, test_name, SConfBase.TestWrapper(test_instance, self))
653
655 """Adds all the tests given in the tests dictionary to this SConf
656 instance
657 """
658 for name in tests.keys():
659 self.AddTest(name, tests[name])
660
670
716
718 """Private method. Reset to non-piped spawn"""
719 global sconf_global, _ac_config_hs
720
721 if not self.active:
722 raise SCons.Errors.UserError("Finish may be called only once!")
723 if self.logstream is not None and not dryrun:
724 self.logstream.write("\n")
725 self.logstream.close()
726 self.logstream = None
727
728 blds = self.env['BUILDERS']
729 del blds['SConfSourceBuilder']
730 self.env.Replace( BUILDERS=blds )
731 self.active = 0
732 sconf_global = None
733 if not self.config_h is None:
734 _ac_config_hs[self.config_h] = self.config_h_text
735 self.env.fs = self.lastEnvFs
736
737 -class CheckContext(object):
738 """Provides a context for configure tests. Defines how a test writes to the
739 screen and log file.
740
741 A typical test is just a callable with an instance of CheckContext as
742 first argument:
743
744 def CheckCustom(context, ...)
745 context.Message('Checking my weird test ... ')
746 ret = myWeirdTestFunction(...)
747 context.Result(ret)
748
749 Often, myWeirdTestFunction will be one of
750 context.TryCompile/context.TryLink/context.TryRun. The results of
751 those are cached, for they are only rebuild, if the dependencies have
752 changed.
753 """
754
755 - def __init__(self, sconf):
756 """Constructor. Pass the corresponding SConf instance."""
757 self.sconf = sconf
758 self.did_show_result = 0
759
760
761 self.vardict = {}
762 self.havedict = {}
763 self.headerfilename = None
764 self.config_h = ""
765
766
767
768
769
770
771
772
773 - def Message(self, text):
774 """Inform about what we are doing right now, e.g.
775 'Checking for SOMETHING ... '
776 """
777 self.Display(text)
778 self.sconf.cached = 1
779 self.did_show_result = 0
780
781 - def Result(self, res):
782 """Inform about the result of the test. If res is not a string, displays
783 'yes' or 'no' depending on whether res is evaluated as true or false.
784 The result is only displayed when self.did_show_result is not set.
785 """
786 if isinstance(res, str):
787 text = res
788 elif res:
789 text = "yes"
790 else:
791 text = "no"
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