import os import subprocess import SCons import glob import re import sys #=========================================================== # The first 4 functions provide for building a library, # program, multiple-programs, or plugin from all the source # in the current directory. # # The next section contains useful utility functions. # # The functions that follow in the final section add support # for various packages (e.g. ROOT, Xerces, ...) #=========================================================== ################################## # library ################################## def library(env, libname=''): # Library name comes from directory name if libname=='': libname = os.path.split(os.getcwd())[1] env.PrependUnique(CPPPATH = ['.']) # Add C/C++, and FORTRAN targets env.AppendUnique(ALL_SOURCES = env.Glob('*.c')) #env.AppendUnique(ALL_SOURCES = env.Glob('*.cc')) env.AppendUnique(ALL_SOURCES = filter(IsNotSWIGWrapper,env.Glob('*.cc'))) env.AppendUnique(ALL_SOURCES = env.Glob('*.cpp')) env.AppendUnique(ALL_SOURCES = env.Glob('*.F')) sources = env['ALL_SOURCES'] # Build static library from all source myobjs = env.Object(sources) mylib = env.Library(target = libname, source = myobjs) # Cleaning and installation are restricted to the directory # scons was launched from or its descendents CurrentDir = env.Dir('.').srcnode().abspath if not CurrentDir.startswith(env.GetLaunchDir()): # Not in launch directory. Tell scons not to clean these targets env.NoClean([myobjs, mylib]) else: # We're in launch directory (or descendent) schedule installation # Installation directories for library and headers installdir = env.subst('$INSTALLDIR') includedir = "%s/%s" %(env.subst('$INCDIR'), libname) libdir = env.subst('$LIBDIR') # Install targets env.Install(libdir, mylib) env.Install(includedir, env.Glob('*.h')+env.Glob('*.hh')+env.Glob('*.hpp')) ################################## # executable ################################## def executable(env, exename=''): # Executable name comes from directory name if exename=='': exename = os.path.split(os.getcwd())[1] env.PrependUnique(CPPPATH = ['.']) # Add C/C++, and FORTRAN targets env.AppendUnique(ALL_SOURCES = env.Glob('*.c')) env.AppendUnique(ALL_SOURCES = env.Glob('*.cc')) env.AppendUnique(ALL_SOURCES = env.Glob('*.cpp')) env.AppendUnique(ALL_SOURCES = env.Glob('*.F')) # Push commonly used libraries to end of list ReorderCommonLibraries(env) sources = env['ALL_SOURCES'] # Build program from all source myobjs = env.Object(sources) myexe = env.Program(target = exename, source = myobjs) # Cleaning and installation are restricted to the directory # scons was launched from or its descendents CurrentDir = env.Dir('.').srcnode().abspath if not CurrentDir.startswith(env.GetLaunchDir()): # Not in launch directory. Tell scons not to clean these targets env.NoClean([myobjs, myexe]) else: # We're in launch directory (or descendent) schedule installation # Installation directories for executable and headers installdir = env.subst('$INSTALLDIR') includedir = env.subst('$INCDIR') bindir = env.subst('$BINDIR') # Install targets env.Install(bindir, myexe) ################################## # executables ################################## def executables(env): # This will generate multiple executables from the # source in the current directory. It does this # by identifying source files that define "main()" # and linking those with all source files that do not # define "main()". Program names are based on the # filename of the source file defining "main()" main_sources = [] common_sources = [] curpath = os.getcwd() srcpath = env.Dir('.').srcnode().abspath os.chdir(srcpath) files = glob.glob('*.c') + glob.glob('*.cc') + glob.glob('*.cpp') for f in files: if 'main(' in open(f).read(): main_sources.append(f) else: common_sources.append(f) for f in glob.glob('*.F'): if ' PROGRAM ' in open(f).read(): main_sources.append(f) else: common_sources.append(f) os.chdir(curpath) env.PrependUnique(CPPPATH = ['.']) # Push commonly used libraries to end of list ReorderCommonLibraries(env) common_sources.extend(env['ALL_SOURCES']) # Build program from all source main_objs = env.Object(main_sources) common_objs = env.Object(common_sources) progs = [] for obj in main_objs: exename = re.sub('\.o$', '', str(obj)) # strip off ".o" from object file name progs.append(env.Program(target = exename, source = [obj, common_objs])) # Cleaning and installation are restricted to the directory # scons was launched from or its descendents CurrentDir = env.Dir('.').srcnode().abspath if not CurrentDir.startswith(env.GetLaunchDir()): # Not in launch directory. Tell scons not to clean these targets env.NoClean([common_objs, main_objs, progs]) else: # We're in launch directory (or descendent) schedule installation bindir = env.subst('$BINDIR') env.Install(bindir, progs) ################################## # plugin ################################## def plugin(env, pluginname=''): # Library name comes from directory name if pluginname=='': pluginname = os.path.split(os.getcwd())[1] env.PrependUnique(CPPPATH = ['.']) # Add C/C++ targets env.AppendUnique(ALL_SOURCES = env.Glob('*.c')) env.AppendUnique(ALL_SOURCES = env.Glob('*.cc')) env.AppendUnique(ALL_SOURCES = env.Glob('*.cpp')) env.AppendUnique(ALL_SOURCES = env.Glob('*.F')) #print str([x.rstr() for x in env.Glob('*.cc')]) sources = env['ALL_SOURCES'] # Build static library from all source myobjs = env.SharedObject(sources) myplugin = env.SharedLibrary(target = pluginname, source = myobjs, SHLIBPREFIX='', SHLIBSUFFIX='.so') # Cleaning and installation are restricted to the directory # scons was launched from or its descendents CurrentDir = env.Dir('.').srcnode().abspath if not CurrentDir.startswith(env.GetLaunchDir()): # Not in launch directory. Tell scons not to clean these targets env.NoClean([myobjs, myplugin]) else: # We're in launch directory (or descendent) schedule installation # Installation directories for plugin and headers installdir = env.subst('$INSTALLDIR') includedir = "%s/%s" %(env.subst('$INCDIR'), pluginname) pluginsdir = env.subst('$PLUGINSDIR') # Install targets installed = env.Install(pluginsdir, myplugin) env.Install(includedir, env.Glob('*.h')+env.Glob('*.hh')+env.Glob('*.hpp')) ################################## # swig_library ################################## def swig_library(env, libname, srcs): # defaults to building python modules for now # library name and list of sources have to be specified if not libname or not srcs: return # make sure that building swig libraries is enabled try: if not env['SWIG_EXISTS'] or int(env['SWIG_EXISTS']) != 1: return if not env['BUILDSWIG'] or int(env['BUILDSWIG']) != 1: return except: return # add standard SWIG options env.AppendUnique(SWIGFLAGS = ["-O"]) # set up variables needed for building python extensions import distutils.sysconfig so_ext = ".so" env.AppendUnique(CPPPATH = [distutils.sysconfig.get_python_inc()]) env.AppendUnique(SWIGFLAGS = ["-I"+os.getcwd()]) # use LoadableModule() to build the python module, since that properly supports OS X mylib = env.LoadableModule(libname, srcs, SHLIBPREFIX="", SHLIBSUFFIX=so_ext) # Cleaning and installation are restricted to the directory # scons was launched from or its descendents CurrentDir = env.Dir('.').srcnode().abspath if not CurrentDir.startswith(env.GetLaunchDir()): # Not in launch directory. Tell scons not to clean these targets env.NoClean([mylib]) else: # We're in launch directory (or descendent) schedule installation # Installation directories for library and headers installdir = env.subst('$INSTALLDIR') includedir = "%s/%s" %(env.subst('$INCDIR'), libname) libdir = env.subst('$LIBDIR') + "/python" # Install targets the_wrapper = env.Install(libdir, libname+".py") the_lib = env.Install(libdir, mylib) env.Depends(the_wrapper, the_lib) #=========================================================== # Misc utility routines for the SBMS system #=========================================================== ################################## # AddCompileFlags ################################## def AddCompileFlags(env, allflags): # The allflags parameter should be a string containing all # of the link flags (e.g. what is returned by root-config --cflags) # It is split on white space and the parameters sorted into # the 2 lists: ccflags, cpppath ccflags = [] cxxflags = [] cpppath = [] for f in allflags.split(): if f.startswith('-I'): cpppath.append(f[2:]) else: if f.startswith('-std=c++'): cxxflags.append(f) else: ccflags.append(f) if len(ccflags)>0 : env.AppendUnique(CCFLAGS=ccflags) if len(cxxflags)>0 : env.AppendUnique(CXXFLAGS=cxxflags) if len(cpppath)>0 : env.AppendUnique(CPPPATH=cpppath) ################################## # AddLinkFlags ################################## def AddLinkFlags(env, allflags): # The allflags parameter should be a string containing all # of the link flags (e.g. what is returned by root-config --glibs) # It is split on white space and the parameters sorted into # the 3 lists: linkflags, libpath, and libs linkflags = [] libpath = [] libs = [] for f in allflags.split(): if f.startswith('-L'): libpath.append(f[2:]) elif f.startswith('-l'): libs.append(f[2:]) else: linkflags.append(f) if len(linkflags)>0 : env.AppendUnique(LINKFLAGS=linkflags) if len(libpath)>0 : env.AppendUnique(LIBPATH=libpath) if len(libs)>0 : env.AppendUnique(LIBS=libs) ################################## # ReorderCommonLibraries ################################## def ReorderCommonLibraries(env): # Some common libraries are often added by multiple packages # (e.g. libz is one that many packages use). The gcc4.8.0 # compiler that comes with Ubuntu13.10 seems particularly # sensitive to the ordering of the libraries. This means if # one package "AppendUnique"s the "z" library, it may appear # too early in the link command for another library that needs # it, even though the second library tries appending it at the # end. This routine looks for some commonly used libraries # in the LIBS variable of the given environment and moves them # to the end of the list. # If LIBS is not defined in this environment, return now if 'LIBS' not in env.Dictionary(): return # If LIBS is not set or is a simple string, return now if type(env['LIBS']) is not list: return libs = ['z', 'bz2', 'pthread', 'm', 'dl'] for lib in libs: if lib in env['LIBS']: env['LIBS'].remove(lib) env.Append(LIBS=[lib]) ################################## # ApplyPlatformSpecificSettings ################################## def ApplyPlatformSpecificSettings(env, platform): # Look for SBMS file based on this platform and run the InitENV # function in it to allow for platform-specific settings. Normally, # the BMS_OSNAME will be passed in which almost certainly contains # "."s. The Python module loader doesn't like these and we have to # replace them with "-"s to appease it. platform = re.sub('\.', '-', platform) modname = "sbms_%s" % platform if (int(env['SHOWBUILD']) > 0): print "looking for %s.py" % modname try: InitENV = getattr(__import__(modname), "InitENV") # Run the InitENV function (if found) if(InitENV != None): print "sbms : Applying settings for platform %s" % platform InitENV(env) except ImportError,e: if (int(env['SHOWBUILD']) > 0): print "%s" % e pass ################################## # OptionallyBuild ################################## def OptionallyBuild(env, dirs): # This is used to add directories that are not built as # part of the standard build, but can still be added # to the dependency tree so that the user can build them # by either invoking scons from within the specific source # directory or by specifying it on the command line. # # subdirs = [] for dir in dirs: add_dir = False if env.GetLaunchDir().endswith(dir): add_dir = True #if dir in env['COMMAND_LINE_TARGETS']: add_dir = True for target in env['COMMAND_LINE_TARGETS']: if target.endswith(dir): add_dir = True if add_dir : subdirs.extend([dir]) if len(subdirs)>0 : env.SConscript(dirs=subdirs, exports='env', duplicate=0) ################################## # ProgramExists ################################## def ProgramExists(program): # this checks the PATH environmental variable to see if a program exists # it works like the "which" command in UNIX def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None ################################## # IsNotSWIGWrapper ################################## def IsNotSWIGWrapper(fileNode): # this function takes an input of type SCons.Node.FS.File # i.e., what the Environment.Glob() function returns f = open(fileNode.rstr(),'r') f.readline() ## the first line is blank # check to see if the first line of the auto-generated file has some magic # let's check to see if it contains the string "SWIG" match = re.search( r'SWIG', f.readline() ) f.close() if match: return False else: return True #=========================================================== # Package support follows #=========================================================== ################################## # JANA ################################## def AddJANA(env): jana_home = os.getenv('JANA_HOME', '/no/default/jana/path') JANA_CFLAGS = subprocess.Popen(["%s/bin/jana-config" % jana_home, "--cflags"], stdout=subprocess.PIPE).communicate()[0] JANA_LINKFLAGS = subprocess.Popen(["%s/bin/jana-config" % jana_home, "--libs"], stdout=subprocess.PIPE).communicate()[0] AddCompileFlags(env, JANA_CFLAGS) AddLinkFlags(env, JANA_LINKFLAGS) ################################## # HDDS ################################## def AddHDDS(env): hdds_home = os.getenv('HDDS_HOME', 'hdds') env.AppendUnique(CPPPATH = ["%s/src" % hdds_home]) env.AppendUnique(LIBPATH = ["%s/lib/%s" % (hdds_home, env['OSNAME'])]) ################################## # HDDM ################################## def AddHDDM(env): env.AppendUnique(LIBS = 'HDDM') ################################## # DANA ################################## def AddDANA(env): AddHDDM(env) AddROOT(env) AddJANA(env) AddCCDB(env) AddHDDS(env) AddXERCES(env) Add_xstream(env) AddEVIO(env) DANA_LIBS = "DANA ANALYSIS PID TAGGER TRACKING START_COUNTER" DANA_LIBS += " CERE RICH CDC TRIGGER" DANA_LIBS += " FDC TOF BCAL FCAL CCAL HDGEOMETRY JANA" env.PrependUnique(LIBS = DANA_LIBS.split()) ################################## # xstream ################################## def Add_xstream(env): env.AppendUnique(CPPPATH = ['#external/xstream/include']) env.AppendUnique(CCFLAGS = ['-fPIC']) env.AppendUnique(LIBS=['xstream', 'bz2', 'z']) ################################## # CCDB ################################## def AddCCDB(env): ccdb_home = os.getenv('CCDB_HOME', 'ccdb') CCDB_CPPPATH = "%s/include" % (ccdb_home) CCDB_LIBPATH = "%s/lib" % (ccdb_home) CCDB_LIBS = "ccdb" env.AppendUnique(CPPPATH = CCDB_CPPPATH) env.AppendUnique(LIBPATH = CCDB_LIBPATH) env.AppendUnique(LIBS = CCDB_LIBS) ################################## # EVIO ################################## def AddEVIO(env): # Only add EVIO if EVIOROOT is set evioroot = os.getenv('EVIOROOT') if(evioroot != None) : env.AppendUnique(CXXFLAGS = ['-DHAVE_EVIO']) env.AppendUnique(CPPPATH = ['%s/include' % evioroot]) env.AppendUnique(LIBPATH = ['%s/lib' % evioroot]) env.AppendUnique(LIBS=['evioxx', 'evio', 'expat']) AddET(env) ################################## # ET ################################## def AddET(env): # Only add ET if ETROOT is set etroot = os.getenv('ETROOT') if(etroot != None) : env.AppendUnique(CXXFLAGS = ['-DHAVE_ET']) env.AppendUnique(CPPPATH = ['%s/include' % etroot]) env.AppendUnique(LIBPATH = ['%s/lib' % etroot]) env.AppendUnique(LIBS=['et_remote', 'et']) ################################## # CodaChannels ################################## def AddCODAChannels(env): # Only add codaChannels library if CODA is set coda = os.getenv('CODA') evioroot = os.getenv('EVIOROOT') etroot = os.getenv('ETROOT') if(coda!=None and evioroot!=None and etroot!=None) : env.AppendUnique(CXXFLAGS = ['-DHAVE_CODACHANNELS']) env.AppendUnique(CPPPATH = ['%s/include' % coda]) env.AppendUnique(LIBPATH = ['%s/lib' % coda]) env.AppendUnique(LIBS=['codaChannels']) ################################## # CMSG ################################## def AddCMSG(env): # Only add cMsg if CMSGROOT is set cmsgroot = os.getenv('CMSGROOT', 'none') if(cmsgroot != 'none') : env.AppendUnique(CXXFLAGS = ['-DHAVE_CMSG']) env.AppendUnique(CPPPATH = ['%s/include' % cmsgroot]) env.AppendUnique(LIBPATH = ['%s/lib' % cmsgroot]) env.AppendUnique(LIBS=['cmsgxx', 'cmsg', 'cmsgRegex']) ################################## # Xerces ################################## def AddXERCES(env): xercescroot = os.getenv('XERCESCROOT', 'xerces') XERCES_CPPPATH = "%s/include" % (xercescroot) XERCES_LIBPATH = "%s/lib" % (xercescroot) XERCES_LIBS = "xerces-c" env.AppendUnique(CPPPATH = XERCES_CPPPATH) env.AppendUnique(LIBPATH = XERCES_LIBPATH) env.AppendUnique(LIBS = XERCES_LIBS) ################################## # CERNLIB ################################## def AddCERNLIB(env): env.PrependUnique(FORTRANFLAGS = ['-ffixed-line-length-0', '-fno-second-underscore']) env.PrependUnique(FORTRANFLAGS = ['-fno-automatic']) env.PrependUnique(FORTRANPATH = ['include']) cern = os.getenv('CERN', '/usr/local/cern/PRO') cern_level = os.getenv('CERN_LEVEL', '2006') cern_root = '%s/%s' % (cern, cern_level) CERN_FORTRANPATH = "%s/include" % cern_root CERN_LIBPATH = "%s/lib" % cern_root env.AppendUnique(FORTRANPATH = [CERN_FORTRANPATH]) env.AppendUnique(CPPPATH = CERN_FORTRANPATH) env.AppendUnique(LIBPATH = CERN_LIBPATH) env.AppendUnique(LINKFLAGS = ['-rdynamic']) env.AppendUnique(LIBS = ['gfortran', 'geant321', 'pawlib', 'lapack3', 'blas', 'graflib', 'grafX11', 'packlib', 'mathlib', 'kernlib', 'X11', 'nsl', 'crypt', 'dl']) env.SetOption('warn', 'no-fortran-cxx-mix') # supress warnings about linking fortran with c++ ################################## # ROOT ################################## def AddROOT(env): # # Here we use the root-config program to give us the compiler # and linker options needed for ROOT. We use the AddCompileFlags() # and AddLinkFlags() routines (defined below) to split the arguments # into the categories scons wants. E.g. scons wants to know the # search path and basenames for libraries rather than just giving it # the full compiler options like "-L/path/to/lib -lmylib". # # We also create a builder for ROOT dictionaries and add targets to # build dictionaries for any headers with "ClassDef" in them. ROOT_CFLAGS = subprocess.Popen(["root-config", "--cflags"], stdout=subprocess.PIPE).communicate()[0] ROOT_LINKFLAGS = subprocess.Popen(["root-config", "--glibs"], stdout=subprocess.PIPE).communicate()[0] AddCompileFlags(env, ROOT_CFLAGS) AddLinkFlags(env, ROOT_LINKFLAGS) env.AppendUnique(LIBS = "Geom") # NOTE on (DY)LD_LIBRARY_PATH : # Linux (and most unixes) use LD_LIBRARY_PATH while Mac OS X uses # DYLD_LIBRARY_PATH. Unfortunately, the "thisroot.csh" script distributed # with ROOT sets both of these so we can't use the presence of the # DYLD_LIBRARY_PATH environment variable to decide which of these to # work with. Thus, we just append to whichever are set, which may be both. # Create Builder that can convert .h file into _Dict.cc file rootsys = os.getenv('ROOTSYS', '/usr/local/root/PRO') if os.getenv('LD_LIBRARY_PATH' ) != None : env.AppendENVPath('LD_LIBRARY_PATH' , '%s/lib' % rootsys ) if os.getenv('DYLD_LIBRARY_PATH') != None : env.AppendENVPath('DYLD_LIBRARY_PATH', '%s/lib' % rootsys ) if env['SHOWBUILD']==0: rootcintaction = SCons.Script.Action("%s/bin/rootcint -f $TARGET -c $SOURCE" % (rootsys), 'ROOTCINT [$SOURCE]') else: rootcintaction = SCons.Script.Action("%s/bin/rootcint -f $TARGET -c $SOURCE" % (rootsys)) bld = SCons.Script.Builder(action = rootcintaction, suffix='_Dict.cc', src_suffix='.h') env.Append(BUILDERS = {'ROOTDict' : bld}) if os.getenv('LD_LIBRARY_PATH' ) != None : env.Append(LD_LIBRARY_PATH = os.environ['LD_LIBRARY_PATH' ]) if os.getenv('DYLD_LIBRARY_PATH') != None : env.Append(DYLD_LIBRARY_PATH = os.environ['DYLD_LIBRARY_PATH']) # Generate ROOT dictionary file targets for each header # containing "ClassDef" # # n.b. It seems if scons is run when the build directory doesn't exist, # then the cwd is set to the source directory. Otherwise, it is the # build directory. Since the headers will only exist in the source # directory, we must temporarily cd into that to look for headers that # we wish to generate dictionaries for. (This took a long time to figure # out!) curpath = os.getcwd() srcpath = env.Dir('.').srcnode().abspath if(int(env['SHOWBUILD'])>1): print "---- Scanning for headers to generate ROOT dictionaries in: %s" % srcpath os.chdir(srcpath) for f in glob.glob('*.[h|hh|hpp]'): if 'ClassDef' in open(f).read(): env.AppendUnique(ALL_SOURCES = env.ROOTDict(f)) if(int(env['SHOWBUILD'])>1): print " ROOT dictionary for %s" % f os.chdir(curpath) ################################## # SWIG ################################## def AddSWIG(env): # SWIG is used to optionally build library wrappers for other languages (e.g. python) # check to see if the swig executable exists # if it does, set a variable to let other scripts know if ProgramExists("swig"): env.AppendUnique(SWIG_EXISTS = "1") else: env.AppendUnique(SWIG_EXISTS = "0") # TEMPORARILY DISABLE #env.Replace(USE_SWIG = "n")