Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. # Create an application package.
  2.  
  3. # https://www.scons.org/wiki/SubstInFileBuilder
  4.  
  5. import re
  6. from SCons.Script import *
  7. from SCons.Builder import *
  8.  
  9. def TOOL_SUBST(env):
  10.     """Adds SubstInFile builder, which substitutes the keys->values of SUBST_DICT
  11.    from the source to the target.
  12.    The values of SUBST_DICT first have any construction variables expanded
  13.    (its keys are not expanded).
  14.    If a value of SUBST_DICT is a python callable function, it is called and
  15.    the result is expanded as the value.
  16.    If there's more than one source and more than one target, each target gets
  17.    substituted from the corresponding source.
  18.    """
  19.     env.Append(TOOLS = 'SUBST')
  20.     def do_subst_in_file(targetfile, sourcefile, dict):
  21.         """Replace all instances of the keys of dict with their values.
  22.        For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
  23.        then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
  24.        """
  25.         try:
  26.             f = open(sourcefile, 'rb')
  27.             contents = f.read()
  28.             f.close()
  29.         except:
  30.             raise SCons.Errors.UserError("Can't read source file %s" % (sourcefile,))
  31.         for (k,v) in dict.items():
  32.             contents = re.sub(k, v, contents)
  33.         try:
  34.             f = open(targetfile, 'wb')
  35.             f.write(contents)
  36.             f.close()
  37.         except:
  38.             raise SCons.Errors.UserError("Can't write target file %s" % (targetfile,))
  39.         return 0 # success
  40.  
  41.     def subst_in_file(target, source, env):
  42.         if not env.has_key('SUBST_DICT'):
  43.             raise SCons.Errors.UserError("SubstInFile requires SUBST_DICT to be set.")
  44.         d = dict(env['SUBST_DICT']) # copy it
  45.         for (k,v) in d.items():
  46.             if callable(v):
  47.                 d[k] = env.subst(v())
  48.             elif SCons.Util.is_String(v):
  49.                 d[k]=env.subst(v)
  50.             else:
  51.                 raise SCons.Errors.UserError("SubstInFile: key %s: %r must be a string or callable" % (k, v))
  52.         for (t,s) in zip(target, source):
  53.             return do_subst_in_file(str(t), str(s), d)
  54.  
  55.     def subst_in_file_string(target, source, env):
  56.         """This is what gets printed on the console."""
  57. #        return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
  58. #                          for (t,s) in zip(target, source)])
  59.  
  60.     def subst_emitter(target, source, env):
  61.         """Add dependency from substituted SUBST_DICT to target.
  62.        Returns original target, source tuple unchanged.
  63.        """
  64.         d = env['SUBST_DICT'].copy() # copy it
  65.         for (k,v) in d.items():
  66.             if callable(v):
  67.                 d[k] = env.subst(v())
  68.             elif SCons.Util.is_String(v):
  69.                 d[k]=env.subst(v)
  70.         env.Depends(target, SCons.Node.Python.Value(d))
  71.         return target, source
  72.  
  73.     subst_action=SCons.Action.Action(subst_in_file, subst_in_file_string)
  74.     env['BUILDERS']['SubstInFile'] = env.Builder(action=subst_action, emitter=subst_emitter)
  75.  
  76. # https://www.scons.org/wiki/MacOSX (modified to suit)
  77.  
  78. from SCons.Defaults import SharedCheck, ProgScan
  79. from SCons.Script.SConscript import SConsEnvironment
  80. import sys
  81. import os
  82. import SCons.Node
  83. import SCons.Util
  84. import string
  85.  
  86. def TOOL_BUNDLE(env):
  87.     """defines env.LinkBundle() for linking bundles on Darwin/OSX, and
  88.       env.MakeBundle() for installing a bundle into its dir.
  89.       A bundle has this structure: (filenames are case SENSITIVE)
  90.       sapphire.bundle/
  91.         Contents/
  92.           Info.plist (an XML key->value database; defined by BUNDLE_INFO_PLIST)
  93.           PkgInfo (trivially short; defined by value of BUNDLE_PKGINFO)
  94.           MacOS/
  95.             executable (the executable or shared lib, linked with Bundle())
  96.    Resources/
  97.         """
  98.     if 'BUNDLE' in env['TOOLS']: return
  99.     if sys.platform == 'darwin':
  100.         #if tools_verbose:
  101.         print(" running tool: TOOL_BUNDLE")
  102.         env.Append(TOOLS = 'BUNDLE')
  103.         # This is like the regular linker, but uses different vars.
  104.         # XXX: NOTE: this may be out of date now, scons 0.96.91 has some bundle linker stuff built in.
  105.         # Check the docs before using this.
  106.         """LinkBundle = env.Builder(action=[SharedCheck, "$BUNDLECOM"],
  107.                                           emitter="$SHLIBEMITTER",
  108.                                           prefix = '$BUNDLEPREFIX',
  109.                                           suffix = '$BUNDLESUFFIX',
  110.                                           target_scanner = ProgScan,
  111.                                           src_suffix = '$BUNDLESUFFIX',
  112.                                           src_builder = 'SharedObject')
  113.        env['BUILDERS']['LinkBundle'] = LinkBundle"""
  114.         env['BUNDLEEMITTER'] = None
  115.         env['BUNDLEPREFIX'] = ''
  116.         env['BUNDLESUFFIX'] = ''
  117.         env['BUNDLEDIRSUFFIX'] = '.bundle'
  118.         #env['FRAMEWORKS'] = ['-framework Carbon', '-framework System']
  119.         env['BUNDLE'] = '$SHLINK'
  120.         env['BUNDLEFLAGS'] = ' -bundle'
  121.         env['BUNDLECOM'] = '$BUNDLE $BUNDLEFLAGS -o ${TARGET} $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $FRAMEWORKS'
  122.         # This requires some other tools:
  123.         TOOL_WRITE_VAL(env)
  124.         TOOL_SUBST(env)
  125.         # Common type codes are BNDL for generic bundle and APPL for application.
  126.         def MakeBundle(env, bundledir, app,
  127.                        key, info_plist,
  128.                        typecode='BNDL', creator='SapP',
  129.                        icon_file='#macosx-install/sapphire-icon.icns',
  130.                        subst_dict=None,
  131.                        resources=[]):
  132.             """Install a bundle into its dir, in the proper format"""
  133.             # Substitute construction vars:
  134.             for a in [bundledir, key, info_plist, icon_file, typecode, creator]:
  135.                 a = env.subst(a)
  136.             if SCons.Util.is_List(app):
  137.                 app = app[0]
  138.             if SCons.Util.is_String(app):
  139.                 app = env.subst(app)
  140.                 appbase = os.path.basename(app)
  141.             else:
  142.                 appbase = os.path.basename(str(app))
  143.             if not ('.' in bundledir):
  144.                 bundledir += '.$BUNDLEDIRSUFFIX'
  145.             bundledir = env.subst(bundledir) # substitute again
  146.             suffix=bundledir[string.rfind(bundledir,'.'):]
  147.             if (suffix=='.app' and typecode != 'APPL' or
  148.                 suffix!='.app' and typecode == 'APPL'):
  149.                 raise Error("MakeBundle: inconsistent dir suffix %s and type code %s: app bundles should end with .app and type code APPL." % (suffix, typecode))
  150.             if subst_dict is None:
  151.                 subst_dict={'%SHORTVERSION%': '$VERSION_NUM',
  152.                             '%LONGVERSION%': '$VERSION_NAME',
  153.                             '%YEAR%': '$COMPILE_YEAR',
  154.                             '%BUNDLE_EXECUTABLE%': appbase,
  155.                             '%ICONFILE%': os.path.basename(icon_file),
  156.                             '%CREATOR%': creator,
  157.                             '%TYPE%': typecode,
  158.                             '%BUNDLE_KEY%': key}
  159.             env.Install(bundledir+'/Contents/MacOS', app)
  160.             f=env.SubstInFile(bundledir+'/Contents/Info.plist', info_plist,
  161.                             SUBST_DICT=subst_dict)
  162.             env.Depends(f,SCons.Node.Python.Value(key+creator+typecode+env['VERSION_NUM']+env['VERSION_NAME']))
  163.             env.WriteVal(target=bundledir+'/Contents/PkgInfo',
  164.                          source=SCons.Node.Python.Value(typecode+creator))
  165.             resources.append(icon_file)
  166.             for r in resources:
  167.                 if SCons.Util.is_List(r):
  168.                     env.InstallAs(bundledir+'/Contents/Resources/'+r[1],
  169.                                   r[0])
  170.                 else:
  171.                     env.Install(bundledir+'/Contents/Resources', r)
  172.             return [ SCons.Node.FS.default_fs.Dir(bundledir) ]
  173.         # This is not a regular Builder; it's a wrapper function.
  174.         # So just make it available as a method of Environment.
  175.         SConsEnvironment.MakeBundle = MakeBundle
  176. def TOOL_WRITE_VAL(env):
  177.     #if tools_verbose:
  178.     print(" running tool: TOOL_WRITE_VAL")
  179.     env.Append(TOOLS = 'WRITE_VAL')
  180.     def write_val(target, source, env):
  181.         """Write the contents of the first source into the target.
  182.        source is usually a Value() node, but could be a file."""
  183.         f = open(str(target[0]), 'wb')
  184.         f.write(source[0].get_contents())
  185.         f.close()
  186.     env['BUILDERS']['WriteVal'] = env.Builder(action=write_val)
  187.