OSDN Git Service

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