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 |