#!python EnsureSConsVersion(0,96) SConsignFile() import os, string, sys import re import distutils.sysconfig import distutils.util import SCons import SCons.Util pj = os.path.join # Bring in the AutoDist build helper sys.path.append('tools/build') from AutoDist import * # True and False were not defined prior to Python 2.2.1. if sys.version[:3] == '2.2' and sys.version[3] != '.': False = 0 True = 1 enable_python = False boost_version = '1.31' have_cppunit = False compiler_major_ver = 0 try: has_help_flag = SCons.Script.Main.options.help_msg except AttributeError: try: has_help_flag = SCons.Script.options.help_msg except AttributeError: has_help_flag = has_help_flag = SCons.Script.GetOption("help") #------------------------------------------------------------------------------ # Define some generally useful functions #------------------------------------------------------------------------------ def GetGMTLVersion(): "Gets the GMTL version from the gmtl/Version.h header" contents = open('gmtl/Version.h', 'r').read() major = re.compile('.*(#define *GMTL_VERSION_MAJOR *(\d+)).*', re.DOTALL).sub(r'\2', contents) minor = re.compile('.*(#define *GMTL_VERSION_MINOR *(\d+)).*', re.DOTALL).sub(r'\2', contents) patch = re.compile('.*(#define *GMTL_VERSION_PATCH *(\d+)).*', re.DOTALL).sub(r'\2', contents) return (int(major), int(minor), int(patch)) def GetPlatform(): "Determines what platform this build is being run on." if string.find(sys.platform, 'irix') != -1: return 'irix' elif string.find(sys.platform, 'linux') != -1: return 'linux' elif string.find(sys.platform, 'freebsd') != -1: return 'freebsd' elif string.find(sys.platform, 'darwin') != -1: return 'darwin' elif string.find(sys.platform, 'cygwin') != -1: return 'cygwin' elif string.find(os.name, 'win32') != -1: return 'win32' else: return sys.platform Export('GetPlatform') def BuildLinuxEnvironment(): "Builds a base environment for other modules to build on set up for linux" global optimize, profile, builders CXX = WhereIs('g++3') or 'g++' LINK = CXX CXXFLAGS = ['-ftemplate-depth-256', '-Wall', '-pipe'] LINKFLAGS = [] # Enable profiling? if profile != 'no': CXXFLAGS.extend(['-pg']) LINKFLAGS.extend(['-pg']) # Debug or optimize build? if optimize != 'no': CXXFLAGS.extend(['-DNDEBUG', '-g', '-O3']) else: CXXFLAGS.extend(['-D_DEBUG', '-g']) return Environment( ENV = os.environ, CXX = CXX, CXXFLAGS = CXXFLAGS, LINK = LINK, LINKFLAGS = LINKFLAGS, CPPPATH = [], LIBPATH = [], LIBS = [], ) def BuildCygwinEnvironment(): "Builds a base environment for other modules to build on set up for Cygwin" global optimize, profile, builders CXX = 'g++' LINK = CXX CXXFLAGS = ['-ftemplate-depth-256', '-Wall', '-pipe'] LINKFLAGS = [] # Enable profiling? if profile != 'no': CXXFLAGS.extend(['-pg']) LINKFLAGS.extend(['-pg']) # Debug or optimize build? if optimize != 'no': CXXFLAGS.extend(['-DNDEBUG', '-g', '-O3']) else: CXXFLAGS.extend(['-D_DEBUG', '-g']) env = Environment(ENV=os.environ, CXX=CXX, LINK=LINK) env.Append(CXXFLAGS=CXXFLAGS, LINKFLAGS=LINKFLAGS) return env def BuildDarwinEnvironment(): "Builds a base environment for other modules to build on set up for Darwin." global optimize, profile, builders, compiler_major_ver exp = re.compile('^(.*)\/Python\.framework.*$') m = exp.search(distutils.sysconfig.get_config_var('prefix')) framework_opt = '-F' + m.group(1) CXX = os.environ.get("CXX", WhereIs('g++')) ver_re = re.compile(r'gcc version ((\d+)\.(\d+)\.(\d+))') (gv_stdout, gv_stdin, gv_stderr) = os.popen3(CXX + ' -v') ver_str = gv_stderr.read() match_obj = ver_re.search(ver_str) LINK = CXX CXXFLAGS = ['-ftemplate-depth-256', '-DBOOST_PYTHON_DYNAMIC_LIB', '-Wall', framework_opt, '-pipe'] compiler_ver = match_obj.group(1) compiler_major_ver = int(match_obj.group(2)) compiler_minor_ver = int(match_obj.group(3)) # GCC 4.0 in Mac OS X 10.4 and newer does not have -fcoalesce-templates. if compiler_major_ver < 4: CXXFLAGS.append('-fcoalesce-templates') else: if compiler_minor_ver < 2: CXXFLAGS += ['-Wno-long-double', '-no-cpp-precomp'] SHLIBSUFFIX = distutils.sysconfig.get_config_var('SO') SHLINKFLAGS = ['-bundle', framework_opt, '-framework', 'Python'] LINKFLAGS = [] # Enable profiling? if profile != 'no': CXXFLAGS.extend(['-pg']) LINKFLAGS.extend(['-pg']) # Debug or optimize build? if optimize != 'no': CXXFLAGS.extend(['-DNDEBUG', '-O3']) else: CXXFLAGS.extend(['-D_DEBUG', '-g']) return Environment( ENV = os.environ, CXX = CXX, CXXFLAGS = CXXFLAGS, CXXVERSION = compiler_ver, LINK = LINK, LINKFLAGS = LINKFLAGS, CPPPATH = [], LIBPATH = [], LIBS = [], SHLINKFLAGS = SHLINKFLAGS, SHLIBSUFFIX = SHLIBSUFFIX, ) def BuildIRIXEnvironment(): "Builds a base environment for other modules to build on set up for IRIX" global optimize, profile, builders CXX = 'CC' LINK = 'CC' CXXFLAGS = ['-n32', '-mips3', '-LANG:std', '-w2'] LINKFLAGS = CXXFLAGS # Enable profiling? if profile != 'no': CXXFLAGS.extend([]) LINKFLAGS.extend([]) # Debug or optimize build? if optimize != 'no': CXXFLAGS.extend(['-DNDEBUG', '-O2']) else: CXXFLAGS.extend(['-D_DEBUG', '-g', '-gslim']) return Environment( ENV = os.environ, CXX = CXX, CXXFLAGS = CXXFLAGS, LINK = LINK, LINKFLAGS = LINKFLAGS, CPPPATH = [], LIBPATH = [], LIBS = [], ) def BuildWin32Environment(): global optimize, compiler_major_ver if ARGUMENTS.has_key("MSVS_VERSION"): # Python extension modules can only be built using Visual C++ 7.1 or # 9.0. msvs_ver = ARGUMENTS["MSVS_VERSION"] if msvs_ver not in ("7.1", "9.0"): print "Cannot build Python extensions using MSVS version %s" % \ msvs_ver sys.exit(1) python_ver = sys.version[:3] if msvs_ver == "7.1" and python_ver not in ("2.4", "2.5"): print "Python 2.4 or 2.5 must be used with Visual C++ 7.1" sys.exit(1) elif msvs_ver == "9.0" and python_ver not in ("2.6"): print "Python 2.6 must be used with Visual C++ 9.0" sys.exit(1) env = Environment(MSVS_VERSION = msvs_ver) # Use the following line instead of the preceding when building with # Visual C++ 9.0. SCons does not know how to find the VC++ 9.0 paths. #env = Environment(MSVS_VERSION = msvs_ver, ENV = os.environ) else: env = Environment(ENV = os.environ) print "Using MSVS version:", env["MSVS"]["VERSION"] compiler_major_ver = env["MSVS"]["VERSION"] # We need exception handling support turned on for Boost.Python. env.Append(LINKFLAGS = ['/subsystem:console', '/incremental:no']) env.Append(CXXFLAGS = ['/Zm800', '/EHsc', '/GR', '/Zc:wchar_t,forScope', '/DBOOST_PYTHON_DYNAMIC_LIB']) if compiler_major_ver < '8.0': env.Append(CXXFLAGS = '/Op') else: env['SHLINKCOM'] = \ [env['SHLINKCOM'], 'mt.exe -manifest ${TARGET}.manifest -outputresource:$TARGET;2'] if optimize != 'no': if compiler_major_ver < '8.0': env.Append(CXXFLAGS = ['/Ogity']) else: env.Append(CXXFLAGS = ['/Oity']) env.Append(CXXFLAGS = '/O2 /Gs /Ob2 /MD /D_OPT /DNDEBUG'.split()) env.Append(LINKFLAGS = ['/RELEASE']) else: env.Append(CXXFLAGS = ['/Z7', '/Od', '/Ob0', '/MDd', '/D_DEBUG']) env.Append(LINKFLAGS = ['/DEBUG']) return env def BuildPlatformEnv(): env = None # Create and export the base environment if GetPlatform() == 'irix': env = BuildIRIXEnvironment() elif GetPlatform() == 'linux' or GetPlatform()[:7] == 'freebsd': env = BuildLinuxEnvironment() elif GetPlatform() == 'darwin': env = BuildDarwinEnvironment() elif GetPlatform() == 'win32': env = BuildWin32Environment() elif GetPlatform() == 'cygwin': env = BuildCygwinEnvironment() else: print 'Unsupported build environment: ' + GetPlatform(), "Trying default" env = Environment() return env # ######################################## # Options # ######################################## def ValidateBoostVersion(key, value, environ): global boost_version if "BoostVersion" == key: exp = re.compile('^(\d+\.\d+(\.\d+)?)\D*$') match = exp.search(value) boost_version = match.group(1) print "Using Boost version", boost_version else: assert False, "Invalid Boost key" def ValidateBoostOption(key, value, environ): "Validate the boost option settings" global enable_python, optimize, compiler_major_ver req_boost_version = 103100 print "checking for %s [%s]...\n" % (key, value) if "BoostPythonDir" == key: version_dir = 'boost-' + re.sub(r'\.', '_', boost_version) boost_inc_dir = value # Default to 'boost' directly off base potential_inc_paths = [pj('include', version_dir), "include"] for pth in potential_inc_paths: if os.path.isdir(pj(value,pth)): boost_inc_dir = pj(value,pth) break print " trying boost include dir: [%s]"%boost_inc_dir # Get the boost version. boost_ver_filename = pj(boost_inc_dir, 'boost', 'version.hpp') if not os.path.isfile(boost_ver_filename): sys.stdout.write("%s not found.\n" % boost_ver_filename) enable_python = False return False ver_file = file(boost_ver_filename) # Matches 103000 ver_num = int(re.search("define\s+?BOOST_VERSION\s+?(\d*)", ver_file.read()).group(1)) sys.stdout.write("found version: %s\n" % ver_num) if ver_num < req_boost_version: print " Boost version too old! Required version: %s" % req_boost_version Exit() return False # Check on the libraries that I need to use if enable_python: platform = GetPlatform() if platform == 'win32': major, minor = compiler_major_ver.split(".") tool = "-vc%s%s" % (major, minor) elif platform == 'irix': tool = '-mp' elif platform == 'darwin': if boost_version >= '1.37': tool = '-xgcc' + "".join(environ["CXXVERSION"].split('.')[:2]) else: tool = '' else: tool = '-gcc' if boost_version >= '1.34': tool += "".join(environ["CXXVERSION"].split('.')[:2]) if platform == 'darwin': if boost_version >= '1.35': threading = '-mt' else: threading = '' else: threading = '-mt' if optimize == 'no': if platform == 'win32': dbg = '-gd' else: dbg = '-d' else: dbg = '' if platform == 'win32': shlib_prefix = '' shlib_ext = 'dll' elif platform == 'darwin': shlib_prefix = 'lib' shlib_ext = 'dylib' elif platform == 'cygwin': shlib_prefix = 'lib' shlib_ext = 'dll' else: shlib_prefix = 'lib' shlib_ext = 'so' boost_sub_minor_ver = ver_num % 100 boost_minor_ver = ver_num / 100 % 1000 boost_major_ver = ver_num / 100000 version = '-%d_%d' % (boost_major_ver, boost_minor_ver) if boost_sub_minor_ver > 0: version += '_%d' % boost_sub_minor_ver bpl_found = False libdirs = ['lib'] if GetPlatform() != 'win32' and os.uname()[4] == 'x86_64': libdirs.append('lib64') full_bpl = 'boost_python%s%s%s%s' % (tool, threading, dbg, version) min_bpl = 'boost_python' names = [full_bpl, min_bpl] # We would prefer to use the full Boost.Python name, but the search # performed below will give us something valid. bpl_name = full_bpl for l in libdirs: for n in names: boost_python_lib_name = pj(value, l, '%s%s.%s' % (shlib_prefix, n, shlib_ext)) print "Checking for '%s'" % boost_python_lib_name if os.path.isfile(boost_python_lib_name): print "Using '%s'" % boost_python_lib_name bpl_libdir = l bpl_name = n bpl_found = True break if not bpl_found: print "No Boost.Python library was found in", libdirs Exit() return False if platform == 'irix': environ.Append(BoostCPPPATH = [pj(boost_inc_dir), pj(boost_inc_dir, 'compatibility', 'cpp_c_headers')]) else: environ.Append(BoostCPPPATH = [pj(boost_inc_dir)]) environ.Append(BoostLIBPATH = [pj(value, bpl_libdir)]) environ.Append(BoostLIBS = [bpl_name]) else: assert False, "Invalid Boost key" def ApplyBoostOptions(env): global enable_python if enable_python: env.Append(CPPPATH = env["BoostCPPPATH"]) env.Append(LIBPATH = env["BoostLIBPATH"]) env.Append(LIBS = env["BoostLIBS"]) def AddBoostOptions(opts): opts.Add('BoostVersion', help = 'Boost version number in the form major.minor', default = boost_version, validator = ValidateBoostVersion) opts.Add('BoostPythonDir', help = 'Boost.Python installation directory (boost include dir must exist under this directory)', default = '/usr/local', validator = ValidateBoostOption) def ValidatePythonOption(key, value, environ): "Validate the Python option settings" global enable_python sys.stdout.write("checking for %s [%s]...\n" % (key, value)) if "EnablePython" == key: value = value.lower() if value == 'true' or value == '1' or value == 'yes': enable_python = True else: enable_python = False if enable_python: required_version = 2.2 python = WhereIs('python') if not python: enable_python = False print 'WARNING: Can\'t find python executable' return False py_cmd = python + ' -c \'import sys; print sys.prefix; print sys.version[:3]\'' prefix = '' py_ver = '' # XXX: Something is broken with trying to use os.popen() on Windows. # This is a hack to work around that until something real can be # found to fix it. Basically, this makes the assumption that the # Python installation being used to invoke scons is the same as the # one against which PyGMTL will be compiled. if GetPlatform() == 'win32': (prefix, py_ver) = (sys.prefix, sys.version[:3]) else: (prefix, py_ver) = string.split(os.popen(py_cmd).read()) environ.Append(PythonSHLIBSUFFIX = distutils.sysconfig.get_config_var('SO')) # Version must match if float(py_ver) < required_version: print 'WARNING: Python version ' + py_ver + ' != ' + str(required_version) enable_python = False else: # Build up the env information environ.Append(PythonCXXFLAGS = ['-DBOOST_PYTHON_MAX_ARITY=16']) if GetPlatform() == 'win32': environ.Append(PythonCPPPATH = [pj(prefix, 'include')]) environ.Append(PythonLIBPATH = [pj(prefix, 'libs')]) else: environ.Append(PythonCPPPATH = [pj(prefix, 'include', 'python'+py_ver)]) environ.Append(PythonLIBPATH = []) else: assert False, "Invalid Python key" return enable_python def ApplyPythonOptions(env): env.Append(CXXFLAGS = env["PythonCXXFLAGS"]) env.Append(CPPPATH = env["PythonCPPPATH"]) env.Append(LIBPATH = env["PythonLIBPATH"]) env['SHLIBSUFFIX'] = env["PythonSHLIBSUFFIX"] def AddPythonOptions(opts): opts.Add('EnablePython', help = 'Enable compilation of PyGMTL; default: EnablePython=False', default = False, validator = ValidatePythonOption) def ValidateCppUnitOption(key, value, environ): "Validate the CppUnit option settings" global have_cppunit sys.stdout.write("checking for %s [%s]...\n" % (key, value)) if "CppUnitDir" == key: cfg = os.path.join(environ['CppUnitDir'], 'bin', 'cppunit-config') found = os.path.isfile(cfg) if found: sys.stdout.write('yes\n') have_cppunit = True else: sys.stdout.write('no\n') have_cppunit = False else: assert False, "Invalid CppUnit key" found = 0 return found def ApplyCppUnitOptions(env): global have_cppunit if have_cppunit: cfg = pj(env['CppUnitDir'], 'bin', 'cppunit-config') env.ParseConfig(cfg + ' --cflags --libs') def AddCppUnitOptions(opts): opts.Add('CppUnitDir', help = 'CppUnit installation directory (cppunit dir must exist under this directory": default: CppUnitDir="/usr/local/include"', default = '/usr/local/include', validator = ValidateCppUnitOption) def CreateSubst(target, source, env): """ Custom builder helpful for creating *-config scripts and just about anything else that can be based on substitutability from a map. The builder works by pulling the variable 'submap' out of the environment and then any place in the source where key from the map exists, that content is replaced with the value of the key in the dictionary. Example: submap = { '@prefix@' : my_prefix, '@version@' : version_str } my_file = env.SubstBuilder('file.out','file.in', submap=submap) env.AddPostAction (my_file, Chmod('$TARGET', 0644)) env.Depends(my_file, 'version.h') """ targets = map(lambda x: str(x), target) sources = map(lambda x: str(x), source) submap = env['submap'] # Build each target from its source for i in range(len(targets)): #print "Generating file " + targets[i] contents = open(sources[i], 'r').read() # Go through the substitution dictionary and modify the contents read in # from the source file for key, value in submap.items(): contents = contents.replace(key, value); # Write out the target file with the new contents open(targets[i], 'w').write(contents) os.chmod(targets[i], 0755) def generate_builder_str(target, source, env): builderStr = "generating: "; for i in range(len(target)): if i > 0: builderStr += ", " + str(target) else: builderStr += str(target); return builderStr Export('ApplyBoostOptions ApplyPythonOptions ApplyCppUnitOptions') #------------------------------------------------------------------------------ # Grok the arguments to this build #------------------------------------------------------------------------------ # Figure out what version of GMTL we're building GMTL_VERSION = GetGMTLVersion() print 'Building GMTL Version: %i.%i.%i' % GMTL_VERSION help_text = "\n---- GMTL Build System ----\n" # Get command-line arguments optimize = ARGUMENTS.get('optimize', 'no') profile = ARGUMENTS.get('profile', 'no') PREFIX = ARGUMENTS.get('prefix', '/usr/local') Prefix(PREFIX) Export('PREFIX') Export('optimize') print "Install prefix: ", Prefix() baseEnv = BuildPlatformEnv() baseEnv["BUILDERS"]["SubstBuilder"] = \ SCons.Builder.Builder(action = SCons.Action.Action(CreateSubst, generate_builder_str, varlist = ['submap',])) baseEnv['enable_python'] = False Export('baseEnv') options_cache = 'options.cache.' + distutils.util.get_platform() opts = Options(options_cache) AddCppUnitOptions(opts) AddPythonOptions(opts) AddBoostOptions(opts) opts.Add('versioning', 'If no then build headers without versioning', 'yes') if SCons.Util.WhereIs('distcc'): baseEnv.Prepend(CXX = "distcc ", CC = "distcc ") if not has_help_flag: opts.Update(baseEnv); opts.Save(options_cache, baseEnv); help_text += "Platform: " + GetPlatform() + "\n"; help_text += str(baseEnv["TOOLS"]) + "\n"; help_text += opts.GenerateHelpText(baseEnv); help_text += """\nOther Options: optimize=no Should we build optimize profile=no Should we build profiled code prefix=/usr/local Installation prefix """ help_text += """ You can store configuration options in the file: options.custom This file will be loaded each time. Note: Options are cached in the file %s """ % options_cache installed_targets = [] # List of targets in the install location Export('installed_targets') if not has_help_flag: print "Preparing build settings...\n" # Create the GMTL package pkg = Package('gmtl', '%i.%i.%i' % GMTL_VERSION) pkg.addExtraDist(Split(""" AUTHORS ChangeLog COPYING gmtl-config gmtl.fpc.in SConstruct docs/Makefile docs/docbook.mk docs/gmtl.doxy docs/gmtl.latex.doxy docs/gmtl.man.doxy docs/version.mk.doxy docs/programmer.guide/Makefile docs/programmer.guide/guide.xml gmtl/gmtl.doxygen python/SConscript Test/SConscript Test/TestSuite/SConscript Test/TestSuite/TestCases/SConscript Test/TestSuite/TestCases/InfoTests/SConscript tools/build/AutoDist.py """)) Export('pkg') # Find gmtl headers, set up install rule and add to package gmtl_headers = [] def get_headers(hdrs, dirname, flist): hdrs.extend( [pj(dirname,f) for f in flist if f.endswith('.h')]) os.path.walk('gmtl',get_headers,gmtl_headers) #print "GMTL Headers:\n", gmtl_headers, "\n" # --- Setup installation paths --- # base_inst_paths = {} base_inst_paths['base'] = os.path.abspath(PREFIX) base_inst_paths['lib'] = pj(base_inst_paths['base'], 'lib') base_inst_paths['share'] = pj(base_inst_paths['base'], 'share') base_inst_paths['flagpoll'] = pj(base_inst_paths['share'], 'flagpoll') base_inst_paths['bin'] = pj(base_inst_paths['base'], 'bin') include_dir = pj(base_inst_paths['base'], 'include') base_inst_paths['include'] = pj('${fp_file_cwd}' ,'..' ,'..', 'include') if baseEnv['versioning'] == 'yes' and not sys.platform.startswith("win"): include_version = "gmtl-%s.%s.%s" % GetGMTLVersion() include_dir = pj(include_dir, include_version) base_inst_paths['include'] = pj('${fp_file_cwd}' ,'..' ,'..', 'include', include_version) print "using prefix:", base_inst_paths['base'] for h in gmtl_headers: installed_targets += baseEnv.InstallAs(pj(PREFIX, include_dir, h), h) pkg.addExtraDist([File(h)]) # Process subdirectories subdirs = [] if enable_python: build_dir = 'build.' + distutils.util.get_platform() BuildDir(build_dir, 'python', duplicate = 0) subdirs.append(build_dir) if have_cppunit: subdirs.append('Test') SConscript(dirs = subdirs) env = baseEnv.Copy() # Build up the provides vars for the .fpc files provides = "gmtl" # XXX: provides data #if combo["type"] != "optimized": # provides += "_%s"%combo["type"] arch = "noarch" gmtl_version_str = "%i.%i.%i" % GMTL_VERSION # - Define a builder for the gmtl.fpc file # ------------------ Build .fpc file ------------------ # # Build up substitution map submap = { '@provides@' : provides, '@prefix@' : pj('${fp_file_cwd}', '..', '..'), '@exec_prefix@' : '${prefix}', '@gmtl_cxxflags@' : '', '@includedir@' : base_inst_paths['include'], '@gmtl_extra_cxxflags@' : '', '@gmtl_extra_include_dirs@' : '', '@arch@' : arch, '@version@' : gmtl_version_str } # Setup the builder for gmtl.fpc name_parts = ['gmtl', gmtl_version_str, arch] fpc_filename = "-".join(name_parts) + ".fpc" gmtl_fpc = env.SubstBuilder(pj(base_inst_paths['flagpoll'], fpc_filename), 'gmtl.fpc.in', submap = submap) env.AddPostAction(gmtl_fpc, Chmod('$TARGET', 0644)) env.Depends(gmtl_fpc, 'gmtl/Version.h') installed_targets += env.Install(base_inst_paths['bin'], 'gmtl-config') installed_targets += gmtl_fpc pkg.build() installed_targets += pkg.getInstalledTargets() MakeSourceDist(pkg, env) # Build everything by default Default('.') Alias('install',installed_targets) Help(help_text)