Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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)