Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. """custom
  2.  
  3. Custom builders and methods.
  4.  
  5. """
  6.  
  7. #
  8. # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
  9. # All Rights Reserved.
  10. #
  11. # Permission is hereby granted, free of charge, to any person obtaining a
  12. # copy of this software and associated documentation files (the
  13. # "Software"), to deal in the Software without restriction, including
  14. # without limitation the rights to use, copy, modify, merge, publish,
  15. # distribute, sub license, and/or sell copies of the Software, and to
  16. # permit persons to whom the Software is furnished to do so, subject to
  17. # the following conditions:
  18. #
  19. # The above copyright notice and this permission notice (including the
  20. # next paragraph) shall be included in all copies or substantial portions
  21. # of the Software.
  22. #
  23. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24. # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
  26. # IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
  27. # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  28. # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  29. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. #
  31.  
  32.  
  33. import os
  34. import os.path
  35. import re
  36. import sys
  37. import subprocess
  38.  
  39. import SCons.Action
  40. import SCons.Builder
  41. import SCons.Scanner
  42.  
  43. import fixes
  44.  
  45. import source_list
  46.  
  47. def quietCommandLines(env):
  48.     # Quiet command lines
  49.     # See also http://www.scons.org/wiki/HidingCommandLinesInOutput
  50.     env['ASCOMSTR'] = "  Assembling $SOURCE ..."
  51.     env['ASPPCOMSTR'] = "  Assembling $SOURCE ..."
  52.     env['CCCOMSTR'] = "  Compiling $SOURCE ..."
  53.     env['SHCCCOMSTR'] = "  Compiling $SOURCE ..."
  54.     env['CXXCOMSTR'] = "  Compiling $SOURCE ..."
  55.     env['SHCXXCOMSTR'] = "  Compiling $SOURCE ..."
  56.     env['ARCOMSTR'] = "  Archiving $TARGET ..."
  57.     env['RANLIBCOMSTR'] = "  Indexing $TARGET ..."
  58.     env['LINKCOMSTR'] = "  Linking $TARGET ..."
  59.     env['SHLINKCOMSTR'] = "  Linking $TARGET ..."
  60.     env['LDMODULECOMSTR'] = "  Linking $TARGET ..."
  61.     env['SWIGCOMSTR'] = "  Generating $TARGET ..."
  62.     env['LEXCOMSTR'] = "  Generating $TARGET ..."
  63.     env['YACCCOMSTR'] = "  Generating $TARGET ..."
  64.     env['CODEGENCOMSTR'] = "  Generating $TARGET ..."
  65.     env['INSTALLSTR'] = "  Installing $TARGET ..."
  66.  
  67.  
  68. def createConvenienceLibBuilder(env):
  69.     """This is a utility function that creates the ConvenienceLibrary
  70.    Builder in an Environment if it is not there already.
  71.  
  72.    If it is already there, we return the existing one.
  73.  
  74.    Based on the stock StaticLibrary and SharedLibrary builders.
  75.    """
  76.  
  77.     try:
  78.         convenience_lib = env['BUILDERS']['ConvenienceLibrary']
  79.     except KeyError:
  80.         action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
  81.         if env.Detect('ranlib'):
  82.             ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
  83.             action_list.append(ranlib_action)
  84.  
  85.         convenience_lib = SCons.Builder.Builder(action = action_list,
  86.                                   emitter = '$LIBEMITTER',
  87.                                   prefix = '$LIBPREFIX',
  88.                                   suffix = '$LIBSUFFIX',
  89.                                   src_suffix = '$SHOBJSUFFIX',
  90.                                   src_builder = 'SharedObject')
  91.         env['BUILDERS']['ConvenienceLibrary'] = convenience_lib
  92.  
  93.     return convenience_lib
  94.  
  95.  
  96. # TODO: handle import statements with multiple modules
  97. # TODO: handle from import statements
  98. import_re = re.compile(r'^\s*import\s+(\S+)\s*$', re.M)
  99.  
  100. def python_scan(node, env, path):
  101.     # http://www.scons.org/doc/0.98.5/HTML/scons-user/c2781.html#AEN2789
  102.     contents = node.get_contents()
  103.     source_dir = node.get_dir()
  104.     imports = import_re.findall(contents)
  105.     results = []
  106.     for imp in imports:
  107.         for dir in path:
  108.             file = os.path.join(str(dir), imp.replace('.', os.sep) + '.py')
  109.             if os.path.exists(file):
  110.                 results.append(env.File(file))
  111.                 break
  112.             file = os.path.join(str(dir), imp.replace('.', os.sep), '__init__.py')
  113.             if os.path.exists(file):
  114.                 results.append(env.File(file))
  115.                 break
  116.     #print node, map(str, results)
  117.     return results
  118.  
  119. python_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py'])
  120.  
  121.  
  122. def code_generate(env, script, target, source, command):
  123.     """Method to simplify code generation via python scripts.
  124.  
  125.    http://www.scons.org/wiki/UsingCodeGenerators
  126.    http://www.scons.org/doc/0.98.5/HTML/scons-user/c2768.html
  127.    """
  128.  
  129.     # We're generating code using Python scripts, so we have to be
  130.     # careful with our scons elements.  This entry represents
  131.     # the generator file *in the source directory*.
  132.     script_src = env.File(script).srcnode()
  133.  
  134.     # This command creates generated code *in the build directory*.
  135.     command = command.replace('$SCRIPT', script_src.path)
  136.     action = SCons.Action.Action(command, "$CODEGENCOMSTR")
  137.     code = env.Command(target, source, action)
  138.  
  139.     # Explicitly mark that the generated code depends on the generator,
  140.     # and on implicitly imported python modules
  141.     path = (script_src.get_dir(),)
  142.     deps = [script_src]
  143.     deps += script_src.get_implicit_deps(env, python_scanner, path)
  144.     env.Depends(code, deps)
  145.  
  146.     # Running the Python script causes .pyc files to be generated in the
  147.     # source directory.  When we clean up, they should go too. So add side
  148.     # effects for .pyc files
  149.     for dep in deps:
  150.         pyc = env.File(str(dep) + 'c')
  151.         env.SideEffect(pyc, code)
  152.  
  153.     return code
  154.  
  155.  
  156. def createCodeGenerateMethod(env):
  157.     env.Append(SCANNERS = python_scanner)
  158.     env.AddMethod(code_generate, 'CodeGenerate')
  159.  
  160.  
  161. def _pkg_check_modules(env, name, modules):
  162.     '''Simple wrapper for pkg-config.'''
  163.  
  164.     env['HAVE_' + name] = False
  165.  
  166.     # For backwards compatability
  167.     env[name.lower()] = False
  168.  
  169.     if env['platform'] == 'windows':
  170.         return
  171.  
  172.     if not env.Detect('pkg-config'):
  173.         return
  174.  
  175.     if subprocess.call(["pkg-config", "--exists", ' '.join(modules)]) != 0:
  176.         return
  177.  
  178.     # Strip version expressions from modules
  179.     modules = [module.split(' ', 1)[0] for module in modules]
  180.  
  181.     # Other flags may affect the compilation of unrelated targets, so store
  182.     # them with a prefix, (e.g., XXX_CFLAGS, XXX_LIBS, etc)
  183.     try:
  184.         flags = env.ParseFlags('!pkg-config --cflags --libs ' + ' '.join(modules))
  185.     except OSError:
  186.         return
  187.     prefix = name + '_'
  188.     for flag_name, flag_value in flags.iteritems():
  189.         assert '_' not in flag_name
  190.         env[prefix + flag_name] = flag_value
  191.  
  192.     env['HAVE_' + name] = True
  193.  
  194. def pkg_check_modules(env, name, modules):
  195.  
  196.     sys.stdout.write('Checking for %s (%s)...' % (name, ' '.join(modules)))
  197.     _pkg_check_modules(env, name, modules)
  198.     result = env['HAVE_' + name]
  199.     sys.stdout.write(' %s\n' % ['no', 'yes'][int(bool(result))])
  200.  
  201.     # XXX: For backwards compatability
  202.     env[name.lower()] = result
  203.  
  204.  
  205. def pkg_use_modules(env, names):
  206.     '''Search for all environment flags that match NAME_FOO and append them to
  207.    the FOO environment variable.'''
  208.  
  209.     names = env.Flatten(names)
  210.  
  211.     for name in names:
  212.         prefix = name + '_'
  213.  
  214.         if not 'HAVE_' + name in env:
  215.             raise Exception('Attempt to use unknown module %s' % name)
  216.  
  217.         if not env['HAVE_' + name]:
  218.             raise Exception('Attempt to use unavailable module %s' % name)
  219.  
  220.         flags = {}
  221.         for flag_name, flag_value in env.Dictionary().iteritems():
  222.             if flag_name.startswith(prefix):
  223.                 flag_name = flag_name[len(prefix):]
  224.                 if '_' not in flag_name:
  225.                     flags[flag_name] = flag_value
  226.         if flags:
  227.             env.MergeFlags(flags)
  228.  
  229.  
  230. def createPkgConfigMethods(env):
  231.     env.AddMethod(pkg_check_modules, 'PkgCheckModules')
  232.     env.AddMethod(pkg_use_modules, 'PkgUseModules')
  233.  
  234.  
  235. def parse_source_list(env, filename, names=None):
  236.     # parse the source list file
  237.     parser = source_list.SourceListParser()
  238.     src = env.File(filename).srcnode()
  239.  
  240.     cur_srcdir = env.Dir('.').srcnode().abspath
  241.     top_srcdir = env.Dir('#').abspath
  242.     top_builddir = os.path.join(top_srcdir, env['build_dir'])
  243.  
  244.     # Normalize everything to / slashes
  245.     cur_srcdir = cur_srcdir.replace('\\', '/')
  246.     top_srcdir = top_srcdir.replace('\\', '/')
  247.     top_builddir = top_builddir.replace('\\', '/')
  248.  
  249.     # Populate the symbol table of the Makefile parser.
  250.     parser.add_symbol('top_srcdir', top_srcdir)
  251.     parser.add_symbol('top_builddir', top_builddir)
  252.  
  253.     sym_table = parser.parse(src.abspath)
  254.  
  255.     if names:
  256.         if isinstance(names, basestring):
  257.             names = [names]
  258.  
  259.         symbols = names
  260.     else:
  261.         symbols = sym_table.keys()
  262.  
  263.     # convert the symbol table to source lists
  264.     src_lists = {}
  265.     for sym in symbols:
  266.         val = sym_table[sym]
  267.         srcs = []
  268.         for f in val.split():
  269.             if f:
  270.                 # Process source paths
  271.                 if f.startswith(top_builddir + '/src'):
  272.                     # Automake puts build output on a `src` subdirectory, but
  273.                     # SCons does not, so strip it here.
  274.                     f = top_builddir + f[len(top_builddir + '/src'):]
  275.                 if f.startswith(cur_srcdir + '/'):
  276.                     # Prefer relative source paths, as absolute files tend to
  277.                     # cause duplicate actions.
  278.                     f = f[len(cur_srcdir + '/'):]
  279.                 srcs.append(f)
  280.  
  281.         src_lists[sym] = srcs
  282.  
  283.     # if names are given, concatenate the lists
  284.     if names:
  285.         srcs = []
  286.         for name in names:
  287.             srcs.extend(src_lists[name])
  288.  
  289.         return srcs
  290.     else:
  291.         return src_lists
  292.  
  293. def createParseSourceListMethod(env):
  294.     env.AddMethod(parse_source_list, 'ParseSourceList')
  295.  
  296.  
  297. def generate(env):
  298.     """Common environment generation code"""
  299.  
  300.     verbose = env.get('verbose', False) or not env.get('quiet', True)
  301.     if not verbose:
  302.         quietCommandLines(env)
  303.  
  304.     # Custom builders and methods
  305.     createConvenienceLibBuilder(env)
  306.     createCodeGenerateMethod(env)
  307.     createPkgConfigMethods(env)
  308.     createParseSourceListMethod(env)
  309.  
  310.     # for debugging
  311.     #print env.Dump()
  312.  
  313.  
  314. def exists(env):
  315.     return 1
  316.