radeonsi: add a common function for getting the size of gs_ngg_scratch
[mesa.git] / scons / custom.py
index 572b963388a8819fd4ed3c8adcbf5c9640ee3104..2fad8f5b6d4398dfb0fe18c771579b8194f71192 100644 (file)
@@ -5,7 +5,7 @@ Custom builders and methods.
 """
 
 #
-# Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
+# Copyright 2008 VMware, Inc.
 # All Rights Reserved.
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
@@ -23,16 +23,17 @@ Custom builders and methods.
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
-# IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
 
 
-import os
 import os.path
-import re
+import sys
+import subprocess
+import modulefinder
 
 import SCons.Action
 import SCons.Builder
@@ -40,6 +41,19 @@ import SCons.Scanner
 
 import fixes
 
+import source_list
+
+# the get_implicit_deps() method changed between 2.4 and 2.5: now it expects
+# a callable that takes a scanner as argument and returns a path, rather than
+# a path directly. We want to support both, so we need to detect the SCons version,
+# for which no API is provided by SCons 8-P
+
+# Scons version string has consistently been in this format:
+# MajorVersion.MinorVersion.Patch[.alpha/beta.yyyymmdd]
+# so this formula should cover all versions regardless of type
+# stable, alpha or beta.
+# For simplicity alpha and beta flags are removed.
+scons_version = tuple(map(int, SCons.__version__.split('.')[:3]))
 
 def quietCommandLines(env):
     # Quiet command lines
@@ -56,6 +70,10 @@ def quietCommandLines(env):
     env['SHLINKCOMSTR'] = "  Linking $TARGET ..."
     env['LDMODULECOMSTR'] = "  Linking $TARGET ..."
     env['SWIGCOMSTR'] = "  Generating $TARGET ..."
+    env['LEXCOMSTR'] = "  Generating $TARGET ..."
+    env['YACCCOMSTR'] = "  Generating $TARGET ..."
+    env['CODEGENCOMSTR'] = "  Generating $TARGET ..."
+    env['INSTALLSTR'] = "  Installing $TARGET ..."
 
 
 def createConvenienceLibBuilder(env):
@@ -86,26 +104,25 @@ def createConvenienceLibBuilder(env):
     return convenience_lib
 
 
-# TODO: handle import statements with multiple modules
-# TODO: handle from import statements
-import_re = re.compile(r'^import\s+(\S+)$', re.M)
-
 def python_scan(node, env, path):
     # http://www.scons.org/doc/0.98.5/HTML/scons-user/c2781.html#AEN2789
+    # https://docs.python.org/2/library/modulefinder.html
     contents = node.get_contents()
-    source_dir = node.get_dir()
-    imports = import_re.findall(contents)
+
+    # Tell ModuleFinder to search dependencies in the script dir, and the glapi
+    # dirs
+    source_dir = node.get_dir().abspath
+    GLAPI = env.Dir('#src/mapi/glapi/gen').abspath
+    path = [source_dir, GLAPI] + sys.path
+
+    finder = modulefinder.ModuleFinder(path=path)
+    finder.run_script(node.abspath)
     results = []
-    for imp in imports:
-        for dir in path:
-            file = os.path.join(str(dir), imp.replace('.', os.sep) + '.py')
-            if os.path.exists(file):
-                results.append(env.File(file))
-                break
-            file = os.path.join(str(dir), imp.replace('.', os.sep), '__init__.py')
-            if os.path.exists(file):
-                results.append(env.File(file))
-                break
+    for name, mod in finder.modules.items():
+        if mod.__file__ is None:
+            continue
+        assert os.path.exists(mod.__file__)
+        results.append(env.File(mod.__file__))
     return results
 
 python_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py'])
@@ -125,11 +142,12 @@ def code_generate(env, script, target, source, command):
 
     # This command creates generated code *in the build directory*.
     command = command.replace('$SCRIPT', script_src.path)
-    code = env.Command(target, source, command)
+    action = SCons.Action.Action(command, "$CODEGENCOMSTR")
+    code = env.Command(target, source, action)
 
     # Explicitly mark that the generated code depends on the generator,
     # and on implicitly imported python modules
-    path = (script_src.get_dir(),)
+    path = (script_src.get_dir(),) if scons_version < (2, 5, 0) else lambda x: script_src
     deps = [script_src]
     deps += script_src.get_implicit_deps(env, python_scanner, path)
     env.Depends(code, deps)
@@ -149,15 +167,161 @@ def createCodeGenerateMethod(env):
     env.AddMethod(code_generate, 'CodeGenerate')
 
 
+def _pkg_check_modules(env, name, modules):
+    '''Simple wrapper for pkg-config.'''
+
+    env['HAVE_' + name] = False
+
+    # For backwards compatability
+    env[name.lower()] = False
+
+    if env['platform'] == 'windows':
+        return
+
+    if not env.Detect('pkg-config'):
+        return
+
+    if subprocess.call(["pkg-config", "--exists", ' '.join(modules)]) != 0:
+        return
+
+    # Strip version expressions from modules
+    modules = [module.split(' ', 1)[0] for module in modules]
+
+    # Other flags may affect the compilation of unrelated targets, so store
+    # them with a prefix, (e.g., XXX_CFLAGS, XXX_LIBS, etc)
+    try:
+        flags = env.ParseFlags('!pkg-config --cflags --libs ' + ' '.join(modules))
+    except OSError:
+        return
+    prefix = name + '_'
+    for flag_name, flag_value in flags.items():
+        assert '_' not in flag_name
+        env[prefix + flag_name] = flag_value
+
+    env['HAVE_' + name] = True
+
+def pkg_check_modules(env, name, modules):
+
+    sys.stdout.write('Checking for %s (%s)...' % (name, ' '.join(modules)))
+    _pkg_check_modules(env, name, modules)
+    result = env['HAVE_' + name]
+    sys.stdout.write(' %s\n' % ['no', 'yes'][int(bool(result))])
+
+    # XXX: For backwards compatability
+    env[name.lower()] = result
+
+
+def pkg_use_modules(env, names):
+    '''Search for all environment flags that match NAME_FOO and append them to
+    the FOO environment variable.'''
+
+    names = env.Flatten(names)
+
+    for name in names:
+        prefix = name + '_'
+
+        if not 'HAVE_' + name in env:
+            raise Exception('Attempt to use unknown module %s' % name)
+
+        if not env['HAVE_' + name]:
+            raise Exception('Attempt to use unavailable module %s' % name)
+
+        flags = {}
+        for flag_name, flag_value in env.Dictionary().items():
+            if flag_name.startswith(prefix):
+                flag_name = flag_name[len(prefix):]
+                if '_' not in flag_name:
+                    flags[flag_name] = flag_value
+        if flags:
+            env.MergeFlags(flags)
+
+
+def createPkgConfigMethods(env):
+    env.AddMethod(pkg_check_modules, 'PkgCheckModules')
+    env.AddMethod(pkg_use_modules, 'PkgUseModules')
+
+
+def parse_source_list(env, filename, names=None):
+    # parse the source list file
+    parser = source_list.SourceListParser()
+    src = env.File(filename).srcnode()
+
+    cur_srcdir = env.Dir('.').srcnode().abspath
+    top_srcdir = env.Dir('#').abspath
+    top_builddir = os.path.join(top_srcdir, env['build_dir'])
+
+    # Normalize everything to / slashes
+    cur_srcdir = cur_srcdir.replace('\\', '/')
+    top_srcdir = top_srcdir.replace('\\', '/')
+    top_builddir = top_builddir.replace('\\', '/')
+
+    # Populate the symbol table of the Makefile parser.
+    parser.add_symbol('top_srcdir', top_srcdir)
+    parser.add_symbol('top_builddir', top_builddir)
+
+    sym_table = parser.parse(src.abspath)
+
+    if names:
+        if sys.version_info[0] >= 3:
+            if isinstance(names, str):
+                names = [names]
+        else:
+            if isinstance(names, basestring):
+                names = [names]
+
+        symbols = names
+    else:
+        symbols = list(sym_table.keys())
+
+    # convert the symbol table to source lists
+    src_lists = {}
+    for sym in symbols:
+        val = sym_table[sym]
+        srcs = []
+        for f in val.split():
+            if f:
+                # Process source paths
+                if f.startswith(top_builddir + '/src'):
+                    # Automake puts build output on a `src` subdirectory, but
+                    # SCons does not, so strip it here.
+                    f = top_builddir + f[len(top_builddir + '/src'):]
+                if f.startswith(cur_srcdir + '/'):
+                    # Prefer relative source paths, as absolute files tend to
+                    # cause duplicate actions.
+                    f = f[len(cur_srcdir + '/'):]
+                # do not include any headers
+                if f.endswith(tuple(['.h','.hpp','.inl'])):
+                    continue
+                srcs.append(f)
+
+        src_lists[sym] = srcs
+
+    # if names are given, concatenate the lists
+    if names:
+        srcs = []
+        for name in names:
+            srcs.extend(src_lists[name])
+
+        return srcs
+    else:
+        return src_lists
+
+def createParseSourceListMethod(env):
+    env.AddMethod(parse_source_list, 'ParseSourceList')
+
+
 def generate(env):
     """Common environment generation code"""
 
-    if env.get('quiet', True):
+    verbose = env.get('verbose', False) or not env.get('quiet', True)
+    if not verbose:
         quietCommandLines(env)
 
     # Custom builders and methods
     createConvenienceLibBuilder(env)
     createCodeGenerateMethod(env)
+    createPkgConfigMethods(env)
+    createParseSourceListMethod(env)
 
     # for debugging
     #print env.Dump()