1848b8605Smrg"""custom 2848b8605Smrg 3848b8605SmrgCustom builders and methods. 4848b8605Smrg 5848b8605Smrg""" 6848b8605Smrg 7848b8605Smrg# 8848b8605Smrg# Copyright 2008 VMware, Inc. 9848b8605Smrg# All Rights Reserved. 10848b8605Smrg# 11848b8605Smrg# Permission is hereby granted, free of charge, to any person obtaining a 12848b8605Smrg# copy of this software and associated documentation files (the 13848b8605Smrg# "Software"), to deal in the Software without restriction, including 14848b8605Smrg# without limitation the rights to use, copy, modify, merge, publish, 15848b8605Smrg# distribute, sub license, and/or sell copies of the Software, and to 16848b8605Smrg# permit persons to whom the Software is furnished to do so, subject to 17848b8605Smrg# the following conditions: 18848b8605Smrg# 19848b8605Smrg# The above copyright notice and this permission notice (including the 20848b8605Smrg# next paragraph) shall be included in all copies or substantial portions 21848b8605Smrg# of the Software. 22848b8605Smrg# 23848b8605Smrg# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 24848b8605Smrg# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25848b8605Smrg# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 26848b8605Smrg# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 27848b8605Smrg# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 28848b8605Smrg# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 29848b8605Smrg# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30848b8605Smrg# 31848b8605Smrg 32848b8605Smrg 33848b8605Smrgimport os.path 34848b8605Smrgimport sys 35848b8605Smrgimport subprocess 36b8e80941Smrgimport modulefinder 37848b8605Smrg 38848b8605Smrgimport SCons.Action 39848b8605Smrgimport SCons.Builder 40848b8605Smrgimport SCons.Scanner 41848b8605Smrg 42848b8605Smrgimport fixes 43848b8605Smrg 44848b8605Smrgimport source_list 45848b8605Smrg 46b8e80941Smrg# the get_implicit_deps() method changed between 2.4 and 2.5: now it expects 47b8e80941Smrg# a callable that takes a scanner as argument and returns a path, rather than 48b8e80941Smrg# a path directly. We want to support both, so we need to detect the SCons version, 49b8e80941Smrg# for which no API is provided by SCons 8-P 50b8e80941Smrg 51b8e80941Smrg# Scons version string has consistently been in this format: 52b8e80941Smrg# MajorVersion.MinorVersion.Patch[.alpha/beta.yyyymmdd] 53b8e80941Smrg# so this formula should cover all versions regardless of type 54b8e80941Smrg# stable, alpha or beta. 55b8e80941Smrg# For simplicity alpha and beta flags are removed. 56b8e80941Smrgscons_version = tuple(map(int, SCons.__version__.split('.')[:3])) 57b8e80941Smrg 58848b8605Smrgdef quietCommandLines(env): 59848b8605Smrg # Quiet command lines 60848b8605Smrg # See also http://www.scons.org/wiki/HidingCommandLinesInOutput 61848b8605Smrg env['ASCOMSTR'] = " Assembling $SOURCE ..." 62848b8605Smrg env['ASPPCOMSTR'] = " Assembling $SOURCE ..." 63848b8605Smrg env['CCCOMSTR'] = " Compiling $SOURCE ..." 64848b8605Smrg env['SHCCCOMSTR'] = " Compiling $SOURCE ..." 65848b8605Smrg env['CXXCOMSTR'] = " Compiling $SOURCE ..." 66848b8605Smrg env['SHCXXCOMSTR'] = " Compiling $SOURCE ..." 67848b8605Smrg env['ARCOMSTR'] = " Archiving $TARGET ..." 68848b8605Smrg env['RANLIBCOMSTR'] = " Indexing $TARGET ..." 69848b8605Smrg env['LINKCOMSTR'] = " Linking $TARGET ..." 70848b8605Smrg env['SHLINKCOMSTR'] = " Linking $TARGET ..." 71848b8605Smrg env['LDMODULECOMSTR'] = " Linking $TARGET ..." 72848b8605Smrg env['SWIGCOMSTR'] = " Generating $TARGET ..." 73848b8605Smrg env['LEXCOMSTR'] = " Generating $TARGET ..." 74848b8605Smrg env['YACCCOMSTR'] = " Generating $TARGET ..." 75848b8605Smrg env['CODEGENCOMSTR'] = " Generating $TARGET ..." 76848b8605Smrg env['INSTALLSTR'] = " Installing $TARGET ..." 77848b8605Smrg 78848b8605Smrg 79848b8605Smrgdef createConvenienceLibBuilder(env): 80848b8605Smrg """This is a utility function that creates the ConvenienceLibrary 81848b8605Smrg Builder in an Environment if it is not there already. 82848b8605Smrg 83848b8605Smrg If it is already there, we return the existing one. 84848b8605Smrg 85848b8605Smrg Based on the stock StaticLibrary and SharedLibrary builders. 86848b8605Smrg """ 87848b8605Smrg 88848b8605Smrg try: 89848b8605Smrg convenience_lib = env['BUILDERS']['ConvenienceLibrary'] 90848b8605Smrg except KeyError: 91848b8605Smrg action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ] 92848b8605Smrg if env.Detect('ranlib'): 93848b8605Smrg ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR") 94848b8605Smrg action_list.append(ranlib_action) 95848b8605Smrg 96848b8605Smrg convenience_lib = SCons.Builder.Builder(action = action_list, 97848b8605Smrg emitter = '$LIBEMITTER', 98848b8605Smrg prefix = '$LIBPREFIX', 99848b8605Smrg suffix = '$LIBSUFFIX', 100848b8605Smrg src_suffix = '$SHOBJSUFFIX', 101848b8605Smrg src_builder = 'SharedObject') 102848b8605Smrg env['BUILDERS']['ConvenienceLibrary'] = convenience_lib 103848b8605Smrg 104848b8605Smrg return convenience_lib 105848b8605Smrg 106848b8605Smrg 107848b8605Smrgdef python_scan(node, env, path): 108848b8605Smrg # http://www.scons.org/doc/0.98.5/HTML/scons-user/c2781.html#AEN2789 109b8e80941Smrg # https://docs.python.org/2/library/modulefinder.html 110848b8605Smrg contents = node.get_contents() 111b8e80941Smrg 112b8e80941Smrg # Tell ModuleFinder to search dependencies in the script dir, and the glapi 113b8e80941Smrg # dirs 114b8e80941Smrg source_dir = node.get_dir().abspath 115b8e80941Smrg GLAPI = env.Dir('#src/mapi/glapi/gen').abspath 116b8e80941Smrg path = [source_dir, GLAPI] + sys.path 117b8e80941Smrg 118b8e80941Smrg finder = modulefinder.ModuleFinder(path=path) 119b8e80941Smrg finder.run_script(node.abspath) 120848b8605Smrg results = [] 121b8e80941Smrg for name, mod in finder.modules.items(): 122b8e80941Smrg if mod.__file__ is None: 123b8e80941Smrg continue 124b8e80941Smrg assert os.path.exists(mod.__file__) 125b8e80941Smrg results.append(env.File(mod.__file__)) 126848b8605Smrg return results 127848b8605Smrg 128848b8605Smrgpython_scanner = SCons.Scanner.Scanner(function = python_scan, skeys = ['.py']) 129848b8605Smrg 130848b8605Smrg 131848b8605Smrgdef code_generate(env, script, target, source, command): 132848b8605Smrg """Method to simplify code generation via python scripts. 133848b8605Smrg 134848b8605Smrg http://www.scons.org/wiki/UsingCodeGenerators 135848b8605Smrg http://www.scons.org/doc/0.98.5/HTML/scons-user/c2768.html 136848b8605Smrg """ 137848b8605Smrg 138848b8605Smrg # We're generating code using Python scripts, so we have to be 139848b8605Smrg # careful with our scons elements. This entry represents 140848b8605Smrg # the generator file *in the source directory*. 141848b8605Smrg script_src = env.File(script).srcnode() 142848b8605Smrg 143848b8605Smrg # This command creates generated code *in the build directory*. 144848b8605Smrg command = command.replace('$SCRIPT', script_src.path) 145848b8605Smrg action = SCons.Action.Action(command, "$CODEGENCOMSTR") 146848b8605Smrg code = env.Command(target, source, action) 147848b8605Smrg 148848b8605Smrg # Explicitly mark that the generated code depends on the generator, 149848b8605Smrg # and on implicitly imported python modules 150b8e80941Smrg path = (script_src.get_dir(),) if scons_version < (2, 5, 0) else lambda x: script_src 151848b8605Smrg deps = [script_src] 152848b8605Smrg deps += script_src.get_implicit_deps(env, python_scanner, path) 153848b8605Smrg env.Depends(code, deps) 154848b8605Smrg 155848b8605Smrg # Running the Python script causes .pyc files to be generated in the 156848b8605Smrg # source directory. When we clean up, they should go too. So add side 157848b8605Smrg # effects for .pyc files 158848b8605Smrg for dep in deps: 159848b8605Smrg pyc = env.File(str(dep) + 'c') 160848b8605Smrg env.SideEffect(pyc, code) 161848b8605Smrg 162848b8605Smrg return code 163848b8605Smrg 164848b8605Smrg 165848b8605Smrgdef createCodeGenerateMethod(env): 166848b8605Smrg env.Append(SCANNERS = python_scanner) 167848b8605Smrg env.AddMethod(code_generate, 'CodeGenerate') 168848b8605Smrg 169848b8605Smrg 170848b8605Smrgdef _pkg_check_modules(env, name, modules): 171848b8605Smrg '''Simple wrapper for pkg-config.''' 172848b8605Smrg 173848b8605Smrg env['HAVE_' + name] = False 174848b8605Smrg 175848b8605Smrg # For backwards compatability 176848b8605Smrg env[name.lower()] = False 177848b8605Smrg 178848b8605Smrg if env['platform'] == 'windows': 179848b8605Smrg return 180848b8605Smrg 181848b8605Smrg if not env.Detect('pkg-config'): 182848b8605Smrg return 183848b8605Smrg 184848b8605Smrg if subprocess.call(["pkg-config", "--exists", ' '.join(modules)]) != 0: 185848b8605Smrg return 186848b8605Smrg 187848b8605Smrg # Strip version expressions from modules 188848b8605Smrg modules = [module.split(' ', 1)[0] for module in modules] 189848b8605Smrg 190848b8605Smrg # Other flags may affect the compilation of unrelated targets, so store 191848b8605Smrg # them with a prefix, (e.g., XXX_CFLAGS, XXX_LIBS, etc) 192848b8605Smrg try: 193848b8605Smrg flags = env.ParseFlags('!pkg-config --cflags --libs ' + ' '.join(modules)) 194848b8605Smrg except OSError: 195848b8605Smrg return 196848b8605Smrg prefix = name + '_' 197b8e80941Smrg for flag_name, flag_value in flags.items(): 198848b8605Smrg assert '_' not in flag_name 199848b8605Smrg env[prefix + flag_name] = flag_value 200848b8605Smrg 201848b8605Smrg env['HAVE_' + name] = True 202848b8605Smrg 203848b8605Smrgdef pkg_check_modules(env, name, modules): 204848b8605Smrg 205848b8605Smrg sys.stdout.write('Checking for %s (%s)...' % (name, ' '.join(modules))) 206848b8605Smrg _pkg_check_modules(env, name, modules) 207848b8605Smrg result = env['HAVE_' + name] 208848b8605Smrg sys.stdout.write(' %s\n' % ['no', 'yes'][int(bool(result))]) 209848b8605Smrg 210848b8605Smrg # XXX: For backwards compatability 211848b8605Smrg env[name.lower()] = result 212848b8605Smrg 213848b8605Smrg 214848b8605Smrgdef pkg_use_modules(env, names): 215848b8605Smrg '''Search for all environment flags that match NAME_FOO and append them to 216848b8605Smrg the FOO environment variable.''' 217848b8605Smrg 218848b8605Smrg names = env.Flatten(names) 219848b8605Smrg 220848b8605Smrg for name in names: 221848b8605Smrg prefix = name + '_' 222848b8605Smrg 223848b8605Smrg if not 'HAVE_' + name in env: 224848b8605Smrg raise Exception('Attempt to use unknown module %s' % name) 225848b8605Smrg 226848b8605Smrg if not env['HAVE_' + name]: 227848b8605Smrg raise Exception('Attempt to use unavailable module %s' % name) 228848b8605Smrg 229848b8605Smrg flags = {} 230b8e80941Smrg for flag_name, flag_value in env.Dictionary().items(): 231848b8605Smrg if flag_name.startswith(prefix): 232848b8605Smrg flag_name = flag_name[len(prefix):] 233848b8605Smrg if '_' not in flag_name: 234848b8605Smrg flags[flag_name] = flag_value 235848b8605Smrg if flags: 236848b8605Smrg env.MergeFlags(flags) 237848b8605Smrg 238848b8605Smrg 239848b8605Smrgdef createPkgConfigMethods(env): 240848b8605Smrg env.AddMethod(pkg_check_modules, 'PkgCheckModules') 241848b8605Smrg env.AddMethod(pkg_use_modules, 'PkgUseModules') 242848b8605Smrg 243848b8605Smrg 244848b8605Smrgdef parse_source_list(env, filename, names=None): 245848b8605Smrg # parse the source list file 246848b8605Smrg parser = source_list.SourceListParser() 247848b8605Smrg src = env.File(filename).srcnode() 248848b8605Smrg 249848b8605Smrg cur_srcdir = env.Dir('.').srcnode().abspath 250848b8605Smrg top_srcdir = env.Dir('#').abspath 251848b8605Smrg top_builddir = os.path.join(top_srcdir, env['build_dir']) 252848b8605Smrg 253848b8605Smrg # Normalize everything to / slashes 254848b8605Smrg cur_srcdir = cur_srcdir.replace('\\', '/') 255848b8605Smrg top_srcdir = top_srcdir.replace('\\', '/') 256848b8605Smrg top_builddir = top_builddir.replace('\\', '/') 257848b8605Smrg 258848b8605Smrg # Populate the symbol table of the Makefile parser. 259848b8605Smrg parser.add_symbol('top_srcdir', top_srcdir) 260848b8605Smrg parser.add_symbol('top_builddir', top_builddir) 261848b8605Smrg 262848b8605Smrg sym_table = parser.parse(src.abspath) 263848b8605Smrg 264848b8605Smrg if names: 265848b8605Smrg if isinstance(names, basestring): 266848b8605Smrg names = [names] 267848b8605Smrg 268848b8605Smrg symbols = names 269848b8605Smrg else: 270b8e80941Smrg symbols = list(sym_table.keys()) 271848b8605Smrg 272848b8605Smrg # convert the symbol table to source lists 273848b8605Smrg src_lists = {} 274848b8605Smrg for sym in symbols: 275848b8605Smrg val = sym_table[sym] 276848b8605Smrg srcs = [] 277848b8605Smrg for f in val.split(): 278848b8605Smrg if f: 279848b8605Smrg # Process source paths 280848b8605Smrg if f.startswith(top_builddir + '/src'): 281848b8605Smrg # Automake puts build output on a `src` subdirectory, but 282848b8605Smrg # SCons does not, so strip it here. 283848b8605Smrg f = top_builddir + f[len(top_builddir + '/src'):] 284848b8605Smrg if f.startswith(cur_srcdir + '/'): 285848b8605Smrg # Prefer relative source paths, as absolute files tend to 286848b8605Smrg # cause duplicate actions. 287848b8605Smrg f = f[len(cur_srcdir + '/'):] 288848b8605Smrg # do not include any headers 289b8e80941Smrg if f.endswith(tuple(['.h','.hpp','.inl'])): 290848b8605Smrg continue 291848b8605Smrg srcs.append(f) 292848b8605Smrg 293848b8605Smrg src_lists[sym] = srcs 294848b8605Smrg 295848b8605Smrg # if names are given, concatenate the lists 296848b8605Smrg if names: 297848b8605Smrg srcs = [] 298848b8605Smrg for name in names: 299848b8605Smrg srcs.extend(src_lists[name]) 300848b8605Smrg 301848b8605Smrg return srcs 302848b8605Smrg else: 303848b8605Smrg return src_lists 304848b8605Smrg 305848b8605Smrgdef createParseSourceListMethod(env): 306848b8605Smrg env.AddMethod(parse_source_list, 'ParseSourceList') 307848b8605Smrg 308848b8605Smrg 309848b8605Smrgdef generate(env): 310848b8605Smrg """Common environment generation code""" 311848b8605Smrg 312848b8605Smrg verbose = env.get('verbose', False) or not env.get('quiet', True) 313848b8605Smrg if not verbose: 314848b8605Smrg quietCommandLines(env) 315848b8605Smrg 316848b8605Smrg # Custom builders and methods 317848b8605Smrg createConvenienceLibBuilder(env) 318848b8605Smrg createCodeGenerateMethod(env) 319848b8605Smrg createPkgConfigMethods(env) 320848b8605Smrg createParseSourceListMethod(env) 321848b8605Smrg 322848b8605Smrg # for debugging 323848b8605Smrg #print env.Dump() 324848b8605Smrg 325848b8605Smrg 326848b8605Smrgdef exists(env): 327848b8605Smrg return 1 328