28 from SCons.Action
import Action
29 from SCons.Environment
import Environment
31 from Buildhelper
import *
36 """ Custom SCons build environment for Lumiera 37 This allows us to carry structured config data without 38 using global vars. Idea inspired by Ardour. 40 def __init__(self, buildSetup, buildVars, **kw):
41 kw.update(VERSION = buildSetup.VERSION
42 ,TARGDIR = buildSetup.TARGDIR
43 ,DESTDIR =
'$INSTALLDIR/$PREFIX' 44 ,toolpath = [buildSetup.TOOLDIR ]
45 ,variables = buildVars
47 Environment.__init__ (self, **kw)
48 self.
path = Record (extract_localPathDefs(buildSetup))
50 self.Tool(
"BuilderGCH")
51 self.Tool(
"BuilderDoxygen")
52 self.Tool(
"ToolDistCC")
53 self.Tool(
"ToolCCache")
54 register_LumieraResourceBuilder(self)
55 register_LumieraCustomBuilders(self)
58 def Configure (self, *args, **kw):
60 return apply(LumieraConfigContext, args, kw)
63 """ extract the library/compiler flags from other Environment. 64 Optionally accepts a list or just sting(s) representing keys 65 in our own libInfo Dictionary 67 if isinstance(other, list):
70 elif isinstance(other, str):
74 self.Append (LIBS = other.get (
'LIBS',[]))
75 self.Append (LIBPATH = other.get (
'LIBPATH', []))
76 self.Append (CPPPATH = other.get(
'CPPPATH', []))
77 self.Append (LINKFLAGS = other.get(
'LINKFLAGS', []))
83 """ use pkg-config to create an Environment describing the lib. 84 Don't add this defs to the current Environment, rather store 85 them in the libInfo Dictionary. 87 minVersion = str(minVersion)
88 if 0 != os.system(
'pkg-config --print-errors --exists "%s >= %s"' % (libID,minVersion)):
89 print "Problems configuring the Library %s (>= %s)" % (libID,minVersion)
92 self.
libInfo[libID] = libInfo = Environment()
93 libInfo[
"ENV"][
"PKG_CONFIG_PATH"] = os.environ.get(
"PKG_CONFIG_PATH")
94 libInfo.ParseConfig (
'pkg-config --cflags --libs '+ libID )
105 ConfigBase = SCons.SConf.SConfBase
110 """ Extends the SCons Configure context with some convenience methods 112 def __init__(self, *args,**kw):
113 ConfigBase.__init__(self,*args,**kw)
115 def CheckPkgConfig (self, libID, minVersion=0, alias=None):
116 print "Checking for library configuration: %s " % libID
118 return self.env.addLibInfo (libID, minVersion, alias)
126 def register_LumieraResourceBuilder(env):
127 """ Registers Custom Builders for generating and installing Icons. 128 Additionally you need to build the tool (rsvg-convert.c) 129 used to generate png from the svg source using librsvg. 132 import IconSvgRenderer
as renderer
133 renderer.rsvgPath = env.subst(
"$TARGDIR/rsvg-convert")
135 def invokeRenderer(target, source, env):
136 source = str(source[0])
137 targetdir = env.subst(env.path.buildIcon)
138 if targetdir.startswith(
'#'): targetdir = targetdir[1:]
139 renderer.main([source,targetdir])
142 def createIconTargets(target,source,env):
143 """ parse the SVG to get the target file names """ 144 source = str(source[0])
145 targetdir = env.path.buildIcon
146 targetfiles = renderer.getTargetNames(source)
149 installLocation = env.path.installIcon
151 for icon
in targetfiles:
152 icon = targetdir+icon
153 subdir = getDirname(str(icon))
154 env.Install (installLocation+subdir, icon)
155 generateTargets.append(icon)
157 return (generateTargets, source)
159 def IconResource(env, source):
160 """ copy icon pixmap to corresponding icon dir. """ 161 subdir = getDirname(str(source))
162 toBuild = env.path.buildIcon+subdir
163 toInstall = env.path.installIcon+subdir
164 env.Install (toInstall, source)
165 return env.Install(toBuild, source)
167 def GuiResource(env, source):
168 """ pick up giben source resource and install 169 them (flat) into the configured target 171 toBuild = env.path.buildUIRes
172 toInstall = env.path.installUIRes
173 env.Install (toInstall, source)
174 return env.Install(toBuild, source)
176 def ConfigData(env, prefix, source, targetDir=None):
177 """ install (copy) configuration- and metadata. 178 target dir is either the install location configured (in SConstruct), 179 or an explicitly given absolute or relative path segment, which might refer 180 to the location of the executable through the $ORIGIN token 182 source = path.join(prefix,str(source))
183 subdir = getDirname(source, prefix)
185 if path.isabs(targetDir):
186 toBuild = toInstall = path.join(targetDir,subdir)
188 if targetDir.startswith(
'$ORIGIN'):
189 targetDir = targetDir[len(
'$ORIGIN'):]
190 toBuild = path.join(env.path.buildExe, targetDir, subdir)
191 toInstall = path.join(env.path.installExe, targetDir, subdir)
193 toBuild = path.join(env.path.buildConf, targetDir, subdir)
194 toInstall = path.join(env.path.installConf, targetDir, subdir)
196 toBuild = path.join(env.path.buildConf,subdir)
197 toInstall = path.join(env.path.installConf,subdir)
198 env.Install (toInstall, source)
199 return env.Install(toBuild, source)
202 buildIcon = env.Builder( action = Action(invokeRenderer,
"rendering Icon: $SOURCE --> $TARGETS")
203 , single_source =
True 204 , emitter = createIconTargets
206 env.Append(BUILDERS = {
'IconRender' : buildIcon})
207 env.AddMethod(IconResource)
208 env.AddMethod(GuiResource)
209 env.AddMethod(ConfigData)
215 """ Helper to add customisations and default configurations to SCons standard builders. 216 The original builder object is wrapped and most calls are simply forwarded to this 217 wrapped object by Python magic. But some calls are intercepted in order to inject 218 suitable default configuration based on the project setup. 221 def __init__(self, originalBuilder):
222 SCons.Util.Proxy.__init__ (self, originalBuilder)
224 def __nonzero__(self):
return True 226 def __call__(self, env, target=
None, source=
None, **kw):
227 """ when the builder gets invoked from the SConscript... 228 create a clone environment for specific configuration 229 and then pass on the call to the wrapped original builder. 230 Automatically define installation targets for build results. 231 @note only returning the build targets, not the install targets 233 customisedEnv = self.getCustomEnvironment(env, target=target, **kw)
240 def invokeOriginalBuilder(self, env, target, source, **kw):
241 return self.get().__call__ (env, target, source, **kw)
244 """ prefix project output directory """ 245 prefix = self.getBuildDestination(env)
246 return list(prefix+str(name)
for name
in target)
249 """ create an additional installation target 250 for the generated executable artifact 252 indeedInstall =
lambda p: p
and p.get(
'install')
254 if indeedInstall(kw):
255 return env.Install (dir = self.getInstallDestination(env), source=buildTarget)
265 """ augments the built-in Program() builder to add a fixed rpath based on $ORIGIN 266 That is: after searching LD_LIBRARY_PATH, but before the standard linker search, 267 the directory relative to the position of the executable ($ORIGIN) is searched. 268 This search path is active not only for the executable, but for all libraries 270 @note: enabling the new ELF dynamic tags. This causes a DT_RUNPATH to be set, 271 which results in LD_LIBRARY_PATH being searched *before* the RPATH 273 custEnv = lumiEnv.Clone()
274 custEnv.Append( LINKFLAGS =
"-Wl,-rpath=\\$$ORIGIN/modules,--enable-new-dtags" )
275 custEnv.Append( LINKFLAGS =
"-Wl,-rpath-link=target/modules" )
277 custEnv.Append(LIBS = kw[
'addLibs'])
280 def getBuildDestination(self, lumiEnv):
return lumiEnv.path.buildExe
281 def getInstallDestination(self, lumiEnv):
return lumiEnv.path.installExe
289 """ augments the built-in SharedLibrary() builder to add some tweaks missing in SCons 1.0, 290 like setting a SONAME proper instead of just passing the relative pathname to the linker. 291 Besides, we override the library search path to allow for transitive dependencies between 292 Lumiera modules; modules are assumed to reside in a subdirectory below the executable. 294 custEnv = lumiEnv.Clone()
295 custEnv.Append(LINKFLAGS =
"-Wl,-soname="+self.
defineSoname(target,**kw))
296 custEnv.Append( LINKFLAGS =
"-Wl,-rpath=\\$$ORIGIN/../modules,--enable-new-dtags" )
298 custEnv.Append(LIBS = kw[
'addLibs'])
301 def getBuildDestination(self, lumiEnv):
return lumiEnv.path.buildLib
302 def getInstallDestination(self, lumiEnv):
return lumiEnv.path.installLib
306 """ internal helper to extract or guess 307 a suitable library SONAME, either using an 308 explicit spec, falling back on the lib filename 311 soname = self.subst(kw[
'soname'])
313 if SCons.Util.is_String(target):
314 pathname = target.strip()
315 elif 1 == len(target):
316 pathname = str(target[0]).strip()
318 raise SyntaxError(
"Lumiera Library builder requires exactly one target spec. Found target="+str(target))
321 (dirprefix, libname) = path.split(pathname)
323 raise ValueError(
"Library name missing. Only got a directory: "+pathname)
325 soname =
"${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname
335 """ in addition to the ModuleBuilder, define the Lumiera plugin suffix 337 custEnv = LumieraModuleBuilder.getCustomEnvironment(self, lumiEnv, target, **kw)
338 custEnv.Append (CPPDEFINES=
'LUMIERA_PLUGIN')
339 custEnv.Replace(SHLIBPREFIX=
'', SHLIBSUFFIX=
'.lum')
342 def getBuildDestination(self, lumiEnv):
return lumiEnv.path.buildPlug
343 def getInstallDestination(self, lumiEnv):
return lumiEnv.path.installPlug
350 def register_LumieraCustomBuilders (lumiEnv):
351 """ install the customised builder versions tightly integrated with our build system. 352 Especially, these builders automatically add the build and installation locations 353 and set the RPATH and SONAME in a way to allow a relocatable Lumiera directory structure 355 programBuilder = LumieraExeBuilder (lumiEnv[
'BUILDERS'][
'Program'])
356 libraryBuilder = LumieraModuleBuilder (lumiEnv[
'BUILDERS'][
'SharedLibrary'])
357 smoduleBuilder = LumieraModuleBuilder (lumiEnv[
'BUILDERS'][
'LoadableModule'])
358 lpluginBuilder = LumieraPluginBuilder (lumiEnv[
'BUILDERS'][
'LoadableModule'])
360 lumiEnv[
'BUILDERS'][
'Program'] = programBuilder
361 lumiEnv[
'BUILDERS'][
'SharedLibrary'] = libraryBuilder
362 lumiEnv[
'BUILDERS'][
'LoadableModule'] = smoduleBuilder
363 lumiEnv[
'BUILDERS'][
'LumieraPlugin'] = lpluginBuilder
366 def SymLink(env, target, source, linktext=None):
367 """ use python to create a symlink 369 def makeLink(target,source,env):
373 dest = str(source[0])
374 link = str(target[0])
375 os.symlink(dest, link)
377 if linktext: srcSpec=linktext
378 else: srcSpec=
'$SOURCE' 379 action = Action(makeLink,
"Install link: $TARGET -> "+srcSpec)
380 env.Command (target,source, action)
387 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)