http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / engine / SCons / Conftest.py
index ddb1a992f0e306251fb0be4c577588d3dacf07d9..04a6bc2aa9188b2ec93fc4eddec563693b211279 100644 (file)
@@ -64,13 +64,19 @@ Autoconf-like configuration support; low level implementation of tests.
 #                       Append "lib_name_list" to the value of LIBS.
 #                       "lib_namelist" is a list of strings.
 #                       Return the value of LIBS before changing it (any type
-#                       can be used, it is passed to SetLIBS() later.
+#                       can be used, it is passed to SetLIBS() later.)
+#
+# context.PrependLIBS(lib_name_list)
+#                       Prepend "lib_name_list" to the value of LIBS.
+#                       "lib_namelist" is a list of strings.
+#                       Return the value of LIBS before changing it (any type
+#                       can be used, it is passed to SetLIBS() later.)
 #
 # context.SetLIBS(value)
 #                       Set LIBS to "value".  The type of "value" is what
 #                       AppendLIBS() returned.
 #                       Return the value of LIBS before changing it (any type
-#                       can be used, it is passed to SetLIBS() later.
+#                       can be used, it is passed to SetLIBS() later.)
 #
 # context.headerfilename
 #                       Name of file to append configure results to, usually
@@ -95,7 +101,6 @@ Autoconf-like configuration support; low level implementation of tests.
 #
 
 import re
-import string
 from types import IntType
 
 #
@@ -142,6 +147,101 @@ int main() {
     _YesNoResult(context, ret, None, text)
     return ret
 
+def CheckCC(context):
+    """
+    Configure check for a working C compiler.
+
+    This checks whether the C compiler, as defined in the $CC construction
+    variable, can compile a C source file. It uses the current $CCCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the C compiler works")
+    text = """
+int main()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'CC', text, 'C')
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def CheckSHCC(context):
+    """
+    Configure check for a working shared C compiler.
+
+    This checks whether the C compiler, as defined in the $SHCC construction
+    variable, can compile a C source file. It uses the current $SHCCCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the (shared) C compiler works")
+    text = """
+int foo()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True)
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def CheckCXX(context):
+    """
+    Configure check for a working CXX compiler.
+
+    This checks whether the CXX compiler, as defined in the $CXX construction
+    variable, can compile a CXX source file. It uses the current $CXXCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the C++ compiler works")
+    text = """
+int main()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'CXX', text, 'C++')
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def CheckSHCXX(context):
+    """
+    Configure check for a working shared CXX compiler.
+
+    This checks whether the CXX compiler, as defined in the $SHCXX construction
+    variable, can compile a CXX source file. It uses the current $SHCXXCOM value
+    too, so that it can test against non working flags.
+
+    """
+    context.Display("Checking whether the (shared) C++ compiler works")
+    text = """
+int main()
+{
+    return 0;
+}
+"""
+    ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True)
+    _YesNoResult(context, ret, None, text)
+    return ret
+
+def _check_empty_program(context, comp, text, language, use_shared = False):
+    """Return 0 on success, 1 otherwise."""
+    if comp not in context.env or not context.env[comp]:
+        # The compiler construction variable is not set or empty
+        return 1
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        return 1
+
+    if use_shared:
+        return context.CompileSharedObject(text, suffix)
+    else:
+        return context.CompileProg(text, suffix)
+
 
 def CheckFunc(context, function_name, header = None, language = None):
     """
@@ -206,7 +306,9 @@ int main() {
 
     context.Display("Checking for %s function %s()... " % (lang, function_name))
     ret = context.BuildProg(text, suffix)
-    _YesNoResult(context, ret, "HAVE_" + function_name, text)
+    _YesNoResult(context, ret, "HAVE_" + function_name, text,
+                 "Define to 1 if the system has the function `%s'." %\
+                 function_name)
     return ret
 
 
@@ -253,7 +355,8 @@ def CheckHeader(context, header_name, header = None, language = None,
 
     context.Display("Checking for %s header file %s... " % (lang, header_name))
     ret = context.CompileProg(text, suffix)
-    _YesNoResult(context, ret, "HAVE_" + header_name, text)
+    _YesNoResult(context, ret, "HAVE_" + header_name, text, 
+                 "Define to 1 if you have the <%s> header file." % header_name)
     return ret
 
 
@@ -310,7 +413,8 @@ int main() {
 
     context.Display("Checking for %s type %s... " % (lang, type_name))
     ret = context.BuildProg(text, suffix)
-    _YesNoResult(context, ret, "HAVE_" + type_name, text)
+    _YesNoResult(context, ret, "HAVE_" + type_name, text,
+                 "Define to 1 if the system has the type `%s'." % type_name)
     if ret and fallback and context.headerfilename:
         f = open(context.headerfilename, "a")
         f.write("typedef %s %s;\n" % (fallback, type_name))
@@ -318,9 +422,163 @@ int main() {
 
     return ret
 
+def CheckTypeSize(context, type_name, header = None, language = None, expect = None):
+    """This check can be used to get the size of a given type, or to check whether
+    the type is of expected size.
+
+    Arguments:
+        - type : str
+            the type to check
+        - includes : sequence
+            list of headers to include in the test code before testing the type
+        - language : str
+            'C' or 'C++'
+        - expect : int
+            if given, will test wether the type has the given number of bytes.
+            If not given, will automatically find the size.
+
+        Returns:
+            status : int
+                0 if the check failed, or the found size of the type if the check succeeded."""
+    
+    # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+    if context.headerfilename:
+        includetext = '#include "%s"' % context.headerfilename
+    else:
+        includetext = ''
+
+    if not header:
+        header = ""
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("Cannot check for %s type: %s\n" % (type_name, msg))
+        return msg
+
+    src = includetext + header 
+    if not expect is None:
+        # Only check if the given size is the right one
+        context.Display('Checking %s is %d bytes... ' % (type_name, expect))
+
+        # test code taken from autoconf: this is a pretty clever hack to find that
+        # a type is of a given size using only compilation. This speeds things up
+        # quite a bit compared to straightforward code using TryRun
+        src = src + r"""
+typedef %s scons_check_type;
+
+int main()
+{
+    static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)];
+    test_array[0] = 0;
+
+    return 0;
+}
+"""
+
+        st = context.CompileProg(src % (type_name, expect), suffix)
+        if not st:
+            context.Display("yes\n")
+            _Have(context, "SIZEOF_%s" % type_name, expect, 
+                  "The size of `%s', as computed by sizeof." % type_name)
+            return expect
+        else:
+            context.Display("no\n")
+            _LogFailed(context, src, st)
+            return 0
+    else:
+        # Only check if the given size is the right one
+        context.Message('Checking size of %s ... ' % type_name)
+
+        # We have to be careful with the program we wish to test here since
+        # compilation will be attempted using the current environment's flags.
+        # So make sure that the program will compile without any warning. For
+        # example using: 'int main(int argc, char** argv)' will fail with the
+        # '-Wall -Werror' flags since the variables argc and argv would not be
+        # used in the program...
+        #
+        src = src + """
+#include <stdlib.h>
+#include <stdio.h>
+int main() {
+    printf("%d", (int)sizeof(""" + type_name + """));
+    return 0;
+}
+    """
+        st, out = context.RunProg(src, suffix)
+        try:
+            size = int(out)
+        except ValueError:
+            # If cannot convert output of test prog to an integer (the size),
+            # something went wront, so just fail
+            st = 1
+            size = 0
+
+        if not st:
+            context.Display("yes\n")
+            _Have(context, "SIZEOF_%s" % type_name, size,
+                  "The size of `%s', as computed by sizeof." % type_name)
+            return size
+        else:
+            context.Display("no\n")
+            _LogFailed(context, src, st)
+            return 0
+
+    return 0
+
+def CheckDeclaration(context, symbol, includes = None, language = None):
+    """Checks whether symbol is declared.
+
+    Use the same test as autoconf, that is test whether the symbol is defined
+    as a macro or can be used as an r-value.
+
+    Arguments:
+        symbol : str
+            the symbol to check
+        includes : str
+            Optional "header" can be defined to include a header file.
+        language : str
+            only C and C++ supported.
+
+    Returns:
+        status : bool
+            True if the check failed, False if succeeded."""
+    
+    # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
+    if context.headerfilename:
+        includetext = '#include "%s"' % context.headerfilename
+    else:
+        includetext = ''
+
+    if not includes:
+        includes = ""
+
+    lang, suffix, msg = _lang2suffix(language)
+    if msg:
+        context.Display("Cannot check for declaration %s: %s\n" % (type_name, msg))
+        return msg
+
+    src = includetext + includes 
+    context.Display('Checking whether %s is declared... ' % symbol)
+
+    src = src + r"""
+int main()
+{
+#ifndef %s
+    (void) %s;
+#endif
+    ;
+    return 0;
+}
+""" % (symbol, symbol)
+
+    st = context.CompileProg(src, suffix)
+    _YesNoResult(context, st, "HAVE_DECL_" + symbol, src,
+                 "Set to 1 if %s is defined." % symbol)
+    return st
 
 def CheckLib(context, libs, func_name = None, header = None,
-                 extra_libs = None, call = None, language = None, autoadd = 1):
+             extra_libs = None, call = None, language = None, autoadd = 1,
+             append = True):
     """
     Configure check for a C or C++ libraries "libs".  Searches through
     the list of libraries, until one is found where the test succeeds.
@@ -341,7 +599,6 @@ def CheckLib(context, libs, func_name = None, header = None,
     sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
     Returns an empty string for success, an error message for failure.
     """
-    from SCons.Debug import Trace
     # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
     if context.headerfilename:
         includetext = '#include "%s"' % context.headerfilename
@@ -378,7 +635,7 @@ return 0;
 """ % (call or "")
 
     if call:
-        i = string.find(call, "\n")
+        i = call.find("\n")
         if i > 0:
             calltext = call[:i] + ".."
         elif call[-1] == ';':
@@ -406,7 +663,10 @@ return 0;
             l = [ lib_name ]
             if extra_libs:
                 l.extend(extra_libs)
-            oldLIBS = context.AppendLIBS(l)
+            if append:
+                oldLIBS = context.AppendLIBS(l)
+            else:
+                oldLIBS = context.PrependLIBS(l)
             sym = "HAVE_LIB" + lib_name
         else:
             oldLIBS = -1
@@ -414,7 +674,8 @@ return 0;
 
         ret = context.BuildProg(text, suffix)
 
-        _YesNoResult(context, ret, sym, text)
+        _YesNoResult(context, ret, sym, text,
+                     "Define to 1 if you have the `%s' library." % lib_name)
         if oldLIBS != -1 and (ret or not autoadd):
             context.SetLIBS(oldLIBS)
             
@@ -427,15 +688,17 @@ return 0;
 # END OF PUBLIC FUNCTIONS
 #
 
-def _YesNoResult(context, ret, key, text):
+def _YesNoResult(context, ret, key, text, comment = None):
     """
     Handle the result of a test with a "yes" or "no" result.
     "ret" is the return value: empty if OK, error message when not.
     "key" is the name of the symbol to be defined (HAVE_foo).
     "text" is the source code of the program used for testing.
+    "comment" is the C comment to add above the line defining the symbol (the
+    comment is automatically put inside a /* */). If None, no comment is added.
     """
     if key:
-        _Have(context, key, not ret)
+        _Have(context, key, not ret, comment)
     if ret:
         context.Display("no\n")
         _LogFailed(context, text, ret)
@@ -443,7 +706,7 @@ def _YesNoResult(context, ret, key, text):
         context.Display("yes\n")
 
 
-def _Have(context, key, have):
+def _Have(context, key, have, comment = None):
     """
     Store result of a test in context.havedict and context.headerfilename.
     "key" is a "HAVE_abc" name.  It is turned into all CAPITALS and non-
@@ -459,24 +722,29 @@ def _Have(context, key, have):
              Give "have" as is should appear in the header file, include quotes
              when desired and escape special characters!
     """
-    key_up = string.upper(key)
+    key_up = key.upper()
     key_up = re.sub('[^A-Z0-9_]', '_', key_up)
     context.havedict[key_up] = have
     if have == 1:
-        line = "#define %s\n" % key_up
+        line = "#define %s 1\n" % key_up
     elif have == 0:
         line = "/* #undef %s */\n" % key_up
-    elif type(have) == IntType:
+    elif isinstance(have, IntType):
         line = "#define %s %d\n" % (key_up, have)
     else:
         line = "#define %s %s\n" % (key_up, str(have))
     
+    if comment is not None:
+        lines = "\n/* %s */\n" % comment + line
+    else:
+        lines = "\n" + line
+
     if context.headerfilename:
         f = open(context.headerfilename, "a")
-        f.write(line)
+        f.write(lines)
         f.close()
     elif hasattr(context,'config_h'):
-        context.config_h = context.config_h + line
+        context.config_h = context.config_h + lines
 
 
 def _LogFailed(context, text, msg):
@@ -486,7 +754,7 @@ def _LogFailed(context, text, msg):
     """
     if LogInputFiles:
         context.Log("Failed program was:\n")
-        lines = string.split(text, '\n')
+        lines = text.split('\n')
         if len(lines) and lines[-1] == '':
             lines = lines[:-1]              # remove trailing empty line
         n = 1
@@ -517,3 +785,9 @@ def _lang2suffix(lang):
 
 
 # vim: set sw=4 et sts=4 tw=79 fo+=l:
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: