LUMIERA.clone/admin/scons/LumieraEnvironment.py

311 lines
12 KiB
Python
Raw Normal View History

# -*- python -*-
##
## LumieraEnvironment.py - custom SCons Environment
##
# Copyright (C) Lumiera.org
# 2008, Hermann Vosseler <Ichthyostega@web.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#####################################################################
import os
from os import path
import SCons
import SCons.SConf
from SCons.Environment import Environment
from Buildhelper import *
class LumieraEnvironment(Environment):
""" Custom SCons build environment for Lumiera
This allows us to carry structured config data without
using global vars. Idea inspired by Ardour.
"""
def __init__(self, pathConfig, **kw):
Environment.__init__ (self,**kw)
self.path = Record (pathConfig)
self.libInfo = {}
self.Tool("BuilderGCH")
self.Tool("BuilderDoxygen")
self.Tool("ToolDistCC")
self.Tool("ToolCCache")
RegisterIcon_Builder(self)
register_LumieraCustomBuilders(self)
def Configure (self, *args, **kw):
kw['env'] = self
return apply(LumieraConfigContext, args, kw)
def mergeConf (self,other):
""" extract the library/compiler flags from other Environment.
Optionally accepts a list or just sting(s) representing keys
in our own libInfo Dictionary
"""
if isinstance(other, list):
for elm in other:
self.mergeConf(elm)
elif isinstance(other, str):
if other in self.libInfo:
self.mergeConf(self.libInfo[other])
else:
self.Append (LIBS = other.get ('LIBS',[]))
self.Append (LIBPATH = other.get ('LIBPATH', []))
self.Append (CPPPATH = other.get('CPPPATH', []))
self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
return self
def addLibInfo (self, libID, minVersion=0, alias=None):
""" use pkg-config to create an Environment describing the lib.
Don't add this defs to the current Environment, rather store
them in the libInfo Dictionary.
"""
minVersion = str(minVersion)
if 0 != os.system('pkg-config --print-errors --exists "%s >= %s"' % (libID,minVersion)):
print "Problems configuring the Library %s (>= %s)" % (libID,minVersion)
return False
self.libInfo[libID] = libInfo = Environment()
libInfo["ENV"]["PKG_CONFIG_PATH"] = os.environ.get("PKG_CONFIG_PATH")
libInfo.ParseConfig ('pkg-config --cflags --libs '+ libID )
if alias:
self.libInfo[alias] = libInfo
return libInfo
def SymLink(self, target, source, linktext=None):
""" use python to create a symlink
"""
def makeLink(target,source,env):
if linktext:
dest = linktext
else:
dest = str(source[0])
link = str(target[0])
os.symlink(dest, link)
def reportLink(target,source,env):
dest = str(source[0])
link = str(target[0])
return "Install link %s -> %s" % (link,dest)
action = Action(makeLink,reportLink)
self.Command (target,source, action)
# extending the 'Configure' functionality of SCons,
# especially for library dependency checking
ConfigBase = SCons.SConf.SConfBase
class LumieraConfigContext(ConfigBase):
""" Extends the SCons Configure context with some convenience methods
"""
def __init__(self, *args,**kw):
ConfigBase.__init__(self,*args,**kw)
def CheckPkgConfig (self, libID, minVersion=0, alias=None):
print "Checking for library configuration: %s " % libID
# self.Message(self,"Checking for library configuration: %s " % libID)
return self.env.addLibInfo (libID, minVersion, alias)
###############################################################################
####### Lumiera custom tools and builders #####################################
def RegisterIcon_Builder(env):
""" Registers Custom Builders for generating and installing Icons.
Additionally you need to build the tool (rsvg-convert.c)
used to generate png from the svg source using librsvg.
"""
import render_icon as renderer # load Joel's python script for invoking the rsvg-convert (SVG render)
renderer.rsvgPath = env.subst("$TARGDIR/rsvg-convert")
def invokeRenderer(target, source, env):
source = str(source[0])
targetdir = env.subst("$TARGDIR")
renderer.main([source,targetdir])
return 0
def createIconTargets(target,source,env):
""" parse the SVG to get the target file names """
source = str(source[0])
targetdir = os.path.basename(str(target[0]))
targetfiles = renderer.getTargetNames(source) # parse SVG
return (["$TARGDIR/%s" % name for name in targetfiles], source)
def IconCopy(env, source):
"""Copy icon to corresponding icon dir. """
subdir = getDirname(source)
return env.Install("$TARGDIR/%s" % subdir, source)
buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS")
, single_source = True
, emitter = createIconTargets
)
env.Append(BUILDERS = {'IconRender' : buildIcon})
env.AddMethod(IconCopy)
class WrappedStandardExeBuilder(SCons.Util.Proxy):
""" Helper to add customisations and default configurations to SCons standard builders.
The original builder object is wrapped and most calls are simply forwarded to this
wrapped object by Python magic. But some calls are intecepted in order to inject
suitalbe default configuration based on the project setup.
"""
def __init__(self, originalBuilder):
SCons.Util.Proxy.__init__ (self, originalBuilder)
def __call__(self, env, target=None, source=None, **kw):
""" when the builder gets invoked from the SConscript...
create a clone environment for specific configuration
and then pass on the call to the wrapped original builder
"""
customisedEnv = self.getCustomEnvironment(env, target=target, **kw) # defined in subclasses
buildTarget = self.buildLocation(customisedEnv, target)
buildTarget = self.invokeOriginalBuilder (customisedEnv, buildTarget, source, **kw)
return buildTarget + self.installTarget(customisedEnv, buildTarget, **kw)
def invokeOriginalBuilder(self, env, target, source, **kw):
return self.get().__call__ (env, target, source, **kw)
def buildLocation(self, env, target):
""" prefix project output directory """
prefix = self.getBuildDestination(env)
return list(prefix+str(name) for name in target)
def installTarget(self, env, buildTarget, **kw):
""" create an additional installation target
for the generated executable artifact
"""
indeedInstall = lambda p: p and p.get('install')
if indeedInstall(kw):
return env.Install (dir = self.getInstallDestination(env), source=buildTarget)
else:
return []
class LumieraExeBuilder(WrappedStandardExeBuilder):
def getCustomEnvironment(self, lumiEnv, **kw):
""" augments the built-in Program() builder to add a fixed rpath based on $ORIGIN
That is: after searching LD_LIBRARY_PATH, but before the standard linker search,
the directory relative to the position of the executable ($ORIGIN) is searched.
This search path is active not only for the executable, but for all libraries
it is linked with.
@note: enabling the new ELF dynamic tags. This causes a DT_RUNPATH to be set,
which results in LD_LIBRARY_PATH being searched *before* the RPATH
"""
custEnv = lumiEnv.Clone()
custEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$MODULES,--enable-new-dtags" )
return custEnv
def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildExe
def getInstallDestination(self, lumiEnv): return lumiEnv.path.installExe
class LumieraModuleBuilder(WrappedStandardExeBuilder):
def getCustomEnvironment(self, lumiEnv, target, **kw):
""" augments the built-in SharedLibrary() builder to add some tweaks missing in SCons 1.0,
like setting a SONAME proper instead of just passing the relative pathname to the linker
"""
custEnv = lumiEnv.Clone()
custEnv.Append(LINKFLAGS = "-Wl,-soname="+self.defineSoname(target,**kw))
return custEnv
def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildLib
def getInstallDestination(self, lumiEnv): return lumiEnv.path.installLib
def defineSoname (self, target, **kw):
""" internal helper to extract or guess
a suitable library SONAME, either using an
explicit spec, falling back on the lib filename
"""
if 'soname' in kw:
soname = self.subst(kw['soname']) # explicitely defined by user
else: # else: use the library filename as DT_SONAME
if SCons.Util.is_String(target):
pathname = target.strip()
elif 1 == len(target):
pathname = str(target[0]).strip()
else:
raise SyntaxError("Lumiera Library builder requires exactly one target spec. Found target="+str(target))
assert pathname
(dirprefix, libname) = path.split(pathname)
if not libname:
raise ValueError("Library name missing. Only got a directory: "+pathname)
soname = "${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname
assert soname
return soname
class LumieraPluginBuilder(LumieraModuleBuilder):
def getCustomEnvironment(self, lumiEnv, target, **kw):
""" in addition to the ModuleBuilder, define the Lumiera plugin suffix
"""
custEnv = LumieraModuleBuilder.getCustomEnvironment(self, lumiEnv, target, **kw)
custEnv.Append (CPPDEFINES='LUMIERA_PLUGIN')
custEnv.Replace(SHLIBPREFIX='', SHLIBSUFFIX='.lum')
return custEnv
def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildPlug
def getInstallDestination(self, lumiEnv): return lumiEnv.path.installPlug
def register_LumieraCustomBuilders (lumiEnv):
""" install the customised builder versions tightly integrated with our buildsystem.
Especially, these builders automatically add the build and installation locations
and set the RPATH and SONAME in a way to allow a relocatable Lumiera directory structure
"""
programBuilder = LumieraExeBuilder (lumiEnv['BUILDERS']['Program'])
libraryBuilder = LumieraModuleBuilder (lumiEnv['BUILDERS']['SharedLibrary'])
smoduleBuilder = LumieraModuleBuilder (lumiEnv['BUILDERS']['LoadableModule'])
lpluginBuilder = LumieraPluginBuilder (lumiEnv['BUILDERS']['LoadableModule'])
lumiEnv['BUILDERS']['Program'] = programBuilder
lumiEnv['BUILDERS']['SharedLibrary'] = libraryBuilder
lumiEnv['BUILDERS']['LoadableModule'] = smoduleBuilder
lumiEnv['BUILDERS']['LumieraPlugin'] = lpluginBuilder