19 from SCons.Action
import Action
20 from SCons.Environment
import Environment
22 from Buildhelper
import *
27 """ Custom SCons build environment for Lumiera 28 This allows us to carry structured config data without 29 using global vars. Idea inspired by Ardour. 31 def __init__(self, buildSetup, buildVars, **kw):
32 kw.update(VERSION = buildSetup.VERSION
33 ,TARGDIR = buildSetup.TARGDIR
34 ,DESTDIR =
'$INSTALLDIR/$PREFIX' 35 ,toolpath = [buildSetup.TOOLDIR ]
36 ,variables = buildVars
38 Environment.__init__ (self, **kw)
39 self.
path = Record (extract_localPathDefs(buildSetup))
41 self.Tool(
"BuilderGCH")
42 self.Tool(
"BuilderDoxygen")
43 self.Tool(
"ToolDistCC")
44 self.Tool(
"ToolCCache")
45 register_LumieraResourceBuilder(self)
46 register_LumieraCustomBuilders(self)
49 def Configure (self, *args, **kw):
51 return apply(LumieraConfigContext, args, kw)
54 """ extract the library/compiler flags from other Environment. 55 Optionally accepts a list or just sting(s) representing keys 56 in our own libInfo Dictionary 58 if isinstance(other, list):
61 elif isinstance(other, str):
65 self.Append (LIBS = other.get (
'LIBS',[]))
66 self.Append (LIBPATH = other.get (
'LIBPATH', []))
67 self.Append (CPPPATH = other.get(
'CPPPATH', []))
68 self.Append (LINKFLAGS = other.get(
'LINKFLAGS', []))
74 """ use pkg-config to create an Environment describing the lib. 75 Don't add this defs to the current Environment, rather store 76 them in the libInfo Dictionary. 78 minVersion = str(minVersion)
79 if 0 != os.system(
'pkg-config --print-errors --exists "%s >= %s"' % (libID,minVersion)):
80 print "Problems configuring the Library %s (>= %s)" % (libID,minVersion)
83 self.
libInfo[libID] = libInfo = Environment()
84 libInfo[
"ENV"][
"PKG_CONFIG_PATH"] = os.environ.get(
"PKG_CONFIG_PATH")
85 libInfo.ParseConfig (
'pkg-config --cflags --libs '+ libID )
96 ConfigBase = SCons.SConf.SConfBase
101 """ Extends the SCons Configure context with some convenience methods 103 def __init__(self, *args,**kw):
104 ConfigBase.__init__(self,*args,**kw)
106 def CheckPkgConfig (self, libID, minVersion=0, alias=None):
107 print "Checking for library configuration: %s " % libID
109 return self.env.addLibInfo (libID, minVersion, alias)
117 def register_LumieraResourceBuilder(env):
118 """ Registers Custom Builders for generating and installing Icons. 119 Additionally you need to build the tool (rsvg-convert.c) 120 used to generate png from the svg source using librsvg. 123 import IconSvgRenderer
as renderer
124 renderer.rsvgPath = env.subst(
"$TARGDIR/rsvg-convert")
126 def invokeRenderer(target, source, env):
127 source = str(source[0])
128 targetdir = env.subst(env.path.buildIcon)
129 if targetdir.startswith(
'#'): targetdir = targetdir[1:]
130 renderer.main([source,targetdir])
133 def createIconTargets(target,source,env):
134 """ parse the SVG to get the target file names """ 135 source = str(source[0])
136 targetdir = env.path.buildIcon
137 targetfiles = renderer.getTargetNames(source)
140 installLocation = env.path.installIcon
142 for icon
in targetfiles:
143 icon = targetdir+icon
144 subdir = getDirname(str(icon))
145 env.Install (installLocation+subdir, icon)
146 generateTargets.append(icon)
148 return (generateTargets, source)
150 def IconResource(env, source):
151 """ copy icon pixmap to corresponding icon dir. """ 152 subdir = getDirname(str(source))
153 toBuild = env.path.buildIcon+subdir
154 toInstall = env.path.installIcon+subdir
155 env.Install (toInstall, source)
156 return env.Install(toBuild, source)
158 def GuiResource(env, source):
159 """ pick up giben source resource and install 160 them (flat) into the configured target 162 toBuild = env.path.buildUIRes
163 toInstall = env.path.installUIRes
164 env.Install (toInstall, source)
165 return env.Install(toBuild, source)
167 def ConfigData(env, prefix, source, targetDir=None):
168 """ install (copy) configuration- and metadata. 169 target dir is either the install location configured (in SConstruct), 170 or an explicitly given absolute or relative path segment, which might refer 171 to the location of the executable through the $ORIGIN token 173 source = path.join(prefix,str(source))
174 subdir = getDirname(source, prefix)
176 if path.isabs(targetDir):
177 toBuild = toInstall = path.join(targetDir,subdir)
179 if targetDir.startswith(
'$ORIGIN'):
180 targetDir = targetDir[len(
'$ORIGIN'):]
181 toBuild = path.join(env.path.buildExe, targetDir, subdir)
182 toInstall = path.join(env.path.installExe, targetDir, subdir)
184 toBuild = path.join(env.path.buildConf, targetDir, subdir)
185 toInstall = path.join(env.path.installConf, targetDir, subdir)
187 toBuild = path.join(env.path.buildConf,subdir)
188 toInstall = path.join(env.path.installConf,subdir)
189 env.Install (toInstall, source)
190 return env.Install(toBuild, source)
193 buildIcon = env.Builder( action = Action(invokeRenderer,
"rendering Icon: $SOURCE --> $TARGETS")
194 , single_source =
True 195 , emitter = createIconTargets
197 env.Append(BUILDERS = {
'IconRender' : buildIcon})
198 env.AddMethod(IconResource)
199 env.AddMethod(GuiResource)
200 env.AddMethod(ConfigData)
206 """ Helper to add customisations and default configurations to SCons standard builders. 207 The original builder object is wrapped and most calls are simply forwarded to this 208 wrapped object by Python magic. But some calls are intercepted in order to inject 209 suitable default configuration based on the project setup. 212 def __init__(self, originalBuilder):
213 SCons.Util.Proxy.__init__ (self, originalBuilder)
215 def __nonzero__(self):
return True 217 def __call__(self, env, target=
None, source=
None, **kw):
218 """ when the builder gets invoked from the SConscript... 219 create a clone environment for specific configuration 220 and then pass on the call to the wrapped original builder. 221 Automatically define installation targets for build results. 222 @note only returning the build targets, not the install targets 224 customisedEnv = self.getCustomEnvironment(env, target=target, **kw)
231 def invokeOriginalBuilder(self, env, target, source, **kw):
232 return self.get().__call__ (env, target, source, **kw)
235 """ prefix project output directory """ 236 prefix = self.getBuildDestination(env)
237 return list(prefix+str(name)
for name
in target)
240 """ create an additional installation target 241 for the generated executable artifact 243 indeedInstall =
lambda p: p
and p.get(
'install')
245 if indeedInstall(kw):
246 return env.Install (dir = self.getInstallDestination(env), source=buildTarget)
256 """ augments the built-in Program() builder to add a fixed rpath based on $ORIGIN 257 That is: after searching LD_LIBRARY_PATH, but before the standard linker search, 258 the directory relative to the position of the executable ($ORIGIN) is searched. 259 This search path is active not only for the executable, but for all libraries 261 @note: enabling the new ELF dynamic tags. This causes a DT_RUNPATH to be set, 262 which results in LD_LIBRARY_PATH being searched *before* the RPATH 264 custEnv = lumiEnv.Clone()
265 custEnv.Append( LINKFLAGS =
"-Wl,-rpath=\\$$ORIGIN/modules,--enable-new-dtags" )
266 custEnv.Append( LINKFLAGS =
"-Wl,-rpath-link=target/modules" )
268 custEnv.Append(LIBS = kw[
'addLibs'])
271 def getBuildDestination(self, lumiEnv):
return lumiEnv.path.buildExe
272 def getInstallDestination(self, lumiEnv):
return lumiEnv.path.installExe
280 """ augments the built-in SharedLibrary() builder to add some tweaks missing in SCons 1.0, 281 like setting a SONAME proper instead of just passing the relative pathname to the linker. 282 Besides, we override the library search path to allow for transitive dependencies between 283 Lumiera modules; modules are assumed to reside in a subdirectory below the executable. 285 custEnv = lumiEnv.Clone()
286 custEnv.Append(LINKFLAGS =
"-Wl,-soname="+self.
defineSoname(target,**kw))
287 custEnv.Append( LINKFLAGS =
"-Wl,-rpath=\\$$ORIGIN/../modules,--enable-new-dtags" )
289 custEnv.Append(LIBS = kw[
'addLibs'])
292 def getBuildDestination(self, lumiEnv):
return lumiEnv.path.buildLib
293 def getInstallDestination(self, lumiEnv):
return lumiEnv.path.installLib
297 """ internal helper to extract or guess 298 a suitable library SONAME, either using an 299 explicit spec, falling back on the lib filename 302 soname = self.subst(kw[
'soname'])
304 if SCons.Util.is_String(target):
305 pathname = target.strip()
306 elif 1 == len(target):
307 pathname = str(target[0]).strip()
309 raise SyntaxError(
"Lumiera Library builder requires exactly one target spec. Found target="+str(target))
312 (dirprefix, libname) = path.split(pathname)
314 raise ValueError(
"Library name missing. Only got a directory: "+pathname)
316 soname =
"${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname
326 """ in addition to the ModuleBuilder, define the Lumiera plugin suffix 328 custEnv = LumieraModuleBuilder.getCustomEnvironment(self, lumiEnv, target, **kw)
329 custEnv.Append (CPPDEFINES=
'LUMIERA_PLUGIN')
330 custEnv.Replace(SHLIBPREFIX=
'', SHLIBSUFFIX=
'.lum')
333 def getBuildDestination(self, lumiEnv):
return lumiEnv.path.buildPlug
334 def getInstallDestination(self, lumiEnv):
return lumiEnv.path.installPlug
341 def register_LumieraCustomBuilders (lumiEnv):
342 """ install the customised builder versions tightly integrated with our build system. 343 Especially, these builders automatically add the build and installation locations 344 and set the RPATH and SONAME in a way to allow a relocatable Lumiera directory structure 346 programBuilder = LumieraExeBuilder (lumiEnv[
'BUILDERS'][
'Program'])
347 libraryBuilder = LumieraModuleBuilder (lumiEnv[
'BUILDERS'][
'SharedLibrary'])
348 smoduleBuilder = LumieraModuleBuilder (lumiEnv[
'BUILDERS'][
'LoadableModule'])
349 lpluginBuilder = LumieraPluginBuilder (lumiEnv[
'BUILDERS'][
'LoadableModule'])
351 lumiEnv[
'BUILDERS'][
'Program'] = programBuilder
352 lumiEnv[
'BUILDERS'][
'SharedLibrary'] = libraryBuilder
353 lumiEnv[
'BUILDERS'][
'LoadableModule'] = smoduleBuilder
354 lumiEnv[
'BUILDERS'][
'LumieraPlugin'] = lpluginBuilder
357 def SymLink(env, target, source, linktext=None):
358 """ use python to create a symlink 360 def makeLink(target,source,env):
364 dest = str(source[0])
365 link = str(target[0])
366 os.symlink(dest, link)
368 if linktext: srcSpec=linktext
369 else: srcSpec=
'$SOURCE' 370 action = Action(makeLink,
"Install link: $TARGET -> "+srcSpec)
371 env.Command (target,source, action)
378 lumiEnv.AddMethod(SymLink)
def getCustomEnvironment(self, lumiEnv, target, kw)
def getCustomEnvironment(self, lumiEnv, target, kw)
def buildLocation(self, env, target)
def defineSoname(self, target, kw)
def invokeOriginalBuilder(self, env, target, source, kw)
def getCustomEnvironment(self, lumiEnv, kw)
def installTarget(self, env, buildTarget, kw)
def mergeConf(self, other)
def addLibInfo(self, libID, minVersion=0, alias=None)