OSDN Git Service

vc4: Fix leak of the bo_handles table.
[android-x86/external-mesa.git] / scons / custom.py
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.path
34 import sys
35 import subprocess
36 import modulefinder
37
38 import SCons.Action
39 import SCons.Builder
40 import SCons.Scanner
41
42 import fixes
43
44 import source_list
45
46 # the get_implicit_deps() method changed between 2.4 and 2.5: now it expects
47 # a callable that takes a scanner as argument and returns a path, rather than
48 # a path directly. We want to support both, so we need to detect the SCons version,
49 # for which no API is provided by SCons 8-P
50
51 scons_version = tuple(map(int, SCons.__version__.split('.')))
52
53 def quietCommandLines(env):
54     # Quiet command lines
55     # See also http://www.scons.org/wiki/HidingCommandLinesInOutput
56     env['ASCOMSTR'] = "  Assembling $SOURCE ..."
57     env['ASPPCOMSTR'] = "  Assembling $SOURCE ..."
58     env['CCCOMSTR'] = "  Compiling $SOURCE ..."
59     env['SHCCCOMSTR'] = "  Compiling $SOURCE ..."
60     env['CXXCOMSTR'] = "  Compiling $SOURCE ..."
61     env['SHCXXCOMSTR'] = "  Compiling $SOURCE ..."
62     env['ARCOMSTR'] = "  Archiving $TARGET ..."
63     env['RANLIBCOMSTR'] = "  Indexing $TARGET ..."
64     env['LINKCOMSTR'] = "  Linking $TARGET ..."
65     env['SHLINKCOMSTR'] = "  Linking $TARGET ..."
66     env['LDMODULECOMSTR'] = "  Linking $TARGET ..."
67     env['SWIGCOMSTR'] = "  Generating $TARGET ..."
68     env['LEXCOMSTR'] = "  Generating $TARGET ..."
69     env['YACCCOMSTR'] = "  Generating $TARGET ..."
70     env['CODEGENCOMSTR'] = "  Generating $TARGET ..."
71     env['INSTALLSTR'] = "  Installing $TARGET ..."
72
73
74 def createConvenienceLibBuilder(env):
75     """This is a utility function that creates the ConvenienceLibrary
76     Builder in an Environment if it is not there already.
77
78     If it is already there, we return the existing one.
79
80     Based on the stock StaticLibrary and SharedLibrary builders.
81     """
82
83     try:
84         convenience_lib = env['BUILDERS']['ConvenienceLibrary']
85     except KeyError:
86         action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ]
87         if env.Detect('ranlib'):
88             ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR")
89             action_list.append(ranlib_action)
90
91         convenience_lib = SCons.Builder.Builder(action = action_list,
92                                   emitter = '$LIBEMITTER',
93                                   prefix = '$LIBPREFIX',
94                                   suffix = '$LIBSUFFIX',
95                                   src_suffix = '$SHOBJSUFFIX',
96                                   src_builder = 'SharedObject')
97         env['BUILDERS']['ConvenienceLibrary'] = convenience_lib
98
99     return convenience_lib
100
101
102 def python_scan(node, env, path):
103     # http://www.scons.org/doc/0.98.5/HTML/scons-user/c2781.html#AEN2789
104     # https://docs.python.org/2/library/modulefinder.html
105     contents = node.get_contents()
106     source_dir = node.get_dir()
107     finder = modulefinder.ModuleFinder()
108     finder.run_script(node.abspath)
109     results = []
110     for name, mod in finder.modules.iteritems():
111         if mod.__file__ is None:
112             continue
113         assert os.path.exists(mod.__file__)
114         results.append(env.File(mod.__file__))
115     return results
116
117 python_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py'])
118
119
120 def code_generate(env, script, target, source, command):
121     """Method to simplify code generation via python scripts.
122
123     http://www.scons.org/wiki/UsingCodeGenerators
124     http://www.scons.org/doc/0.98.5/HTML/scons-user/c2768.html
125     """
126
127     # We're generating code using Python scripts, so we have to be
128     # careful with our scons elements.  This entry represents
129     # the generator file *in the source directory*.
130     script_src = env.File(script).srcnode()
131
132     # This command creates generated code *in the build directory*.
133     command = command.replace('$SCRIPT', script_src.path)
134     action = SCons.Action.Action(command, "$CODEGENCOMSTR")
135     code = env.Command(target, source, action)
136
137     # Explicitly mark that the generated code depends on the generator,
138     # and on implicitly imported python modules
139     path = (script_src.get_dir(),) if scons_version < (2, 5, 0) else lambda x: script_src
140     deps = [script_src]
141     deps += script_src.get_implicit_deps(env, python_scanner, path)
142     env.Depends(code, deps)
143
144     # Running the Python script causes .pyc files to be generated in the
145     # source directory.  When we clean up, they should go too. So add side
146     # effects for .pyc files
147     for dep in deps:
148         pyc = env.File(str(dep) + 'c')
149         env.SideEffect(pyc, code)
150
151     return code
152
153
154 def createCodeGenerateMethod(env):
155     env.Append(SCANNERS = python_scanner)
156     env.AddMethod(code_generate, 'CodeGenerate')
157
158
159 def _pkg_check_modules(env, name, modules):
160     '''Simple wrapper for pkg-config.'''
161
162     env['HAVE_' + name] = False
163
164     # For backwards compatability
165     env[name.lower()] = False
166
167     if env['platform'] == 'windows':
168         return
169
170     if not env.Detect('pkg-config'):
171         return
172
173     if subprocess.call(["pkg-config", "--exists", ' '.join(modules)]) != 0:
174         return
175
176     # Strip version expressions from modules
177     modules = [module.split(' ', 1)[0] for module in modules]
178
179     # Other flags may affect the compilation of unrelated targets, so store
180     # them with a prefix, (e.g., XXX_CFLAGS, XXX_LIBS, etc)
181     try:
182         flags = env.ParseFlags('!pkg-config --cflags --libs ' + ' '.join(modules))
183     except OSError:
184         return
185     prefix = name + '_'
186     for flag_name, flag_value in flags.iteritems():
187         assert '_' not in flag_name
188         env[prefix + flag_name] = flag_value
189
190     env['HAVE_' + name] = True
191
192 def pkg_check_modules(env, name, modules):
193
194     sys.stdout.write('Checking for %s (%s)...' % (name, ' '.join(modules)))
195     _pkg_check_modules(env, name, modules)
196     result = env['HAVE_' + name]
197     sys.stdout.write(' %s\n' % ['no', 'yes'][int(bool(result))])
198
199     # XXX: For backwards compatability
200     env[name.lower()] = result
201
202
203 def pkg_use_modules(env, names):
204     '''Search for all environment flags that match NAME_FOO and append them to
205     the FOO environment variable.'''
206
207     names = env.Flatten(names)
208
209     for name in names:
210         prefix = name + '_'
211
212         if not 'HAVE_' + name in env:
213             raise Exception('Attempt to use unknown module %s' % name)
214
215         if not env['HAVE_' + name]:
216             raise Exception('Attempt to use unavailable module %s' % name)
217
218         flags = {}
219         for flag_name, flag_value in env.Dictionary().iteritems():
220             if flag_name.startswith(prefix):
221                 flag_name = flag_name[len(prefix):]
222                 if '_' not in flag_name:
223                     flags[flag_name] = flag_value
224         if flags:
225             env.MergeFlags(flags)
226
227
228 def createPkgConfigMethods(env):
229     env.AddMethod(pkg_check_modules, 'PkgCheckModules')
230     env.AddMethod(pkg_use_modules, 'PkgUseModules')
231
232
233 def parse_source_list(env, filename, names=None):
234     # parse the source list file
235     parser = source_list.SourceListParser()
236     src = env.File(filename).srcnode()
237
238     cur_srcdir = env.Dir('.').srcnode().abspath
239     top_srcdir = env.Dir('#').abspath
240     top_builddir = os.path.join(top_srcdir, env['build_dir'])
241
242     # Normalize everything to / slashes
243     cur_srcdir = cur_srcdir.replace('\\', '/')
244     top_srcdir = top_srcdir.replace('\\', '/')
245     top_builddir = top_builddir.replace('\\', '/')
246
247     # Populate the symbol table of the Makefile parser.
248     parser.add_symbol('top_srcdir', top_srcdir)
249     parser.add_symbol('top_builddir', top_builddir)
250
251     sym_table = parser.parse(src.abspath)
252
253     if names:
254         if isinstance(names, basestring):
255             names = [names]
256
257         symbols = names
258     else:
259         symbols = sym_table.keys()
260
261     # convert the symbol table to source lists
262     src_lists = {}
263     for sym in symbols:
264         val = sym_table[sym]
265         srcs = []
266         for f in val.split():
267             if f:
268                 # Process source paths
269                 if f.startswith(top_builddir + '/src'):
270                     # Automake puts build output on a `src` subdirectory, but
271                     # SCons does not, so strip it here.
272                     f = top_builddir + f[len(top_builddir + '/src'):]
273                 if f.startswith(cur_srcdir + '/'):
274                     # Prefer relative source paths, as absolute files tend to
275                     # cause duplicate actions.
276                     f = f[len(cur_srcdir + '/'):]
277                 # do not include any headers
278                 if f.endswith('.h'):
279                     continue
280                 srcs.append(f)
281
282         src_lists[sym] = srcs
283
284     # if names are given, concatenate the lists
285     if names:
286         srcs = []
287         for name in names:
288             srcs.extend(src_lists[name])
289
290         return srcs
291     else:
292         return src_lists
293
294 def createParseSourceListMethod(env):
295     env.AddMethod(parse_source_list, 'ParseSourceList')
296
297
298 def generate(env):
299     """Common environment generation code"""
300
301     verbose = env.get('verbose', False) or not env.get('quiet', True)
302     if not verbose:
303         quietCommandLines(env)
304
305     # Custom builders and methods
306     createConvenienceLibBuilder(env)
307     createCodeGenerateMethod(env)
308     createPkgConfigMethods(env)
309     createParseSourceListMethod(env)
310
311     # for debugging
312     #print env.Dump()
313
314
315 def exists(env):
316     return 1