Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
5564 serge 1
"""custom
2
 
3
Custom builders and methods.
4
 
5
"""
6
 
7
#
8
# Copyright 2008 VMware, Inc.
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 VMWARE 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
                # do not include any headers
280
                if f.endswith('.h'):
281
                    continue
282
                srcs.append(f)
283
 
284
        src_lists[sym] = srcs
285
 
286
    # if names are given, concatenate the lists
287
    if names:
288
        srcs = []
289
        for name in names:
290
            srcs.extend(src_lists[name])
291
 
292
        return srcs
293
    else:
294
        return src_lists
295
 
296
def createParseSourceListMethod(env):
297
    env.AddMethod(parse_source_list, 'ParseSourceList')
298
 
299
 
300
def generate(env):
301
    """Common environment generation code"""
302
 
303
    verbose = env.get('verbose', False) or not env.get('quiet', True)
304
    if not verbose:
305
        quietCommandLines(env)
306
 
307
    # Custom builders and methods
308
    createConvenienceLibBuilder(env)
309
    createCodeGenerateMethod(env)
310
    createPkgConfigMethods(env)
311
    createParseSourceListMethod(env)
312
 
313
    # for debugging
314
    #print env.Dump()
315
 
316
 
317
def exists(env):
318
    return 1