Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
# coding: utf-8
|
2008-07-11 05:35:48 +02:00
|
|
|
|
##
|
|
|
|
|
|
## LumieraEnvironment.py - custom SCons Environment
|
|
|
|
|
|
##
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
# Copyright (C)
|
2025-11-25 02:14:21 +01:00
|
|
|
|
# 2008-2025 Hermann Vosseler <Ichthyostega@web.de>
|
2008-07-11 05:35:48 +02:00
|
|
|
|
#
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
# **Lumiera** 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. See the file COPYING for further details.
|
2008-07-11 05:35:48 +02:00
|
|
|
|
#####################################################################
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-29 16:45:22 +01:00
|
|
|
|
from os import path
|
|
|
|
|
|
|
2008-08-21 01:24:46 +02:00
|
|
|
|
import SCons.SConf
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
from SCons.Action import Action, FunctionAction
|
|
|
|
|
|
from SCons.Script import File as SConsFile
|
2008-07-11 05:35:48 +02:00
|
|
|
|
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
|
2025-06-07 23:59:57 +02:00
|
|
|
|
using global vars. Idea inspired by Ardour.
|
2008-07-11 05:35:48 +02:00
|
|
|
|
"""
|
2012-01-10 05:09:32 +01:00
|
|
|
|
def __init__(self, buildSetup, buildVars, **kw):
|
2025-11-19 22:40:08 +01:00
|
|
|
|
Environment.__init__ (self, toolpath = [buildSetup.TOOLDIR ]
|
|
|
|
|
|
, variables = buildVars # ◁───── reads settings from the commandline (see Options.py)
|
|
|
|
|
|
, **kw)
|
|
|
|
|
|
#
|
|
|
|
|
|
self['TARGDIR'] = buildSetup.TARGDIR
|
|
|
|
|
|
self['VERSION'] = buildSetup.VERSION
|
|
|
|
|
|
self['DESTDIR'] = '$INSTALLDIR/$PREFIX'
|
2025-11-20 22:55:38 +01:00
|
|
|
|
self['SHARE' ] = '$DESTDIR/share'
|
2025-11-19 22:40:08 +01:00
|
|
|
|
self._anchor_relative('INSTALLDIR')
|
|
|
|
|
|
self._anchor_relative('TARGDIR')
|
|
|
|
|
|
#
|
|
|
|
|
|
self.path = Record (extract_localPathDefs(buildSetup)) # ◁───── e.g. buildExe -> env.path.buildExe
|
2008-07-11 05:35:48 +02:00
|
|
|
|
self.libInfo = {}
|
2025-04-08 05:07:58 +02:00
|
|
|
|
self.Tool("BuilderDoxygen")
|
2011-01-30 15:27:21 +01:00
|
|
|
|
self.Tool("ToolDistCC")
|
|
|
|
|
|
self.Tool("ToolCCache")
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
register_LumieraIconBuilder(self)
|
|
|
|
|
|
register_LumieraResourceBuilders(self)
|
2011-01-30 16:47:03 +01:00
|
|
|
|
register_LumieraCustomBuilders(self)
|
2008-07-11 05:35:48 +02:00
|
|
|
|
|
2025-11-19 22:40:08 +01:00
|
|
|
|
def _anchor_relative(self, key):
|
|
|
|
|
|
""" ensure that a relative path spec becomes anchored at build-root
|
|
|
|
|
|
@note: a special convention within scons: '#' implies directory of SConstruct
|
|
|
|
|
|
"""
|
|
|
|
|
|
spec = self[key].strip()
|
|
|
|
|
|
if not (spec.startswith('/') or spec.startswith('#')):
|
|
|
|
|
|
spec = '#'+spec
|
|
|
|
|
|
self[key] = spec
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-01-10 05:09:32 +01:00
|
|
|
|
|
2008-07-11 05:35:48 +02:00
|
|
|
|
def Configure (self, *args, **kw):
|
|
|
|
|
|
kw['env'] = self
|
2022-09-05 23:02:29 +02:00
|
|
|
|
return LumieraConfigContext(*args, **kw)
|
2008-07-11 05:35:48 +02:00
|
|
|
|
|
|
|
|
|
|
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:
|
2025-11-25 02:14:21 +01:00
|
|
|
|
self.Append (LIBS = other.get('LIBS',[]))
|
|
|
|
|
|
self.Append (LIBPATH = other.get('LIBPATH', []))
|
|
|
|
|
|
self.Append (CPPPATH = other.get('CPPPATH', []))
|
2008-07-11 05:35:48 +02:00
|
|
|
|
self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
|
|
|
|
|
|
|
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-04-20 01:58:34 +02:00
|
|
|
|
def addLibInfo (self, libID, minVersion=0, alias=None):
|
2008-07-11 05:35:48 +02:00
|
|
|
|
""" use pkg-config to create an Environment describing the lib.
|
|
|
|
|
|
Don't add this defs to the current Environment, rather store
|
2025-06-07 23:59:57 +02:00
|
|
|
|
them in the libInfo Dictionary.
|
2008-07-11 05:35:48 +02:00
|
|
|
|
"""
|
|
|
|
|
|
minVersion = str(minVersion)
|
|
|
|
|
|
if 0 != os.system('pkg-config --print-errors --exists "%s >= %s"' % (libID,minVersion)):
|
2022-09-05 23:02:29 +02:00
|
|
|
|
print("Problems configuring the Library %s (>= %s)" % (libID,minVersion))
|
2008-07-11 05:35:48 +02:00
|
|
|
|
return False
|
|
|
|
|
|
|
2011-01-30 15:27:21 +01:00
|
|
|
|
self.libInfo[libID] = libInfo = Environment()
|
2010-01-17 00:42:07 +01:00
|
|
|
|
libInfo["ENV"]["PKG_CONFIG_PATH"] = os.environ.get("PKG_CONFIG_PATH")
|
2008-07-11 05:35:48 +02:00
|
|
|
|
libInfo.ParseConfig ('pkg-config --cflags --libs '+ libID )
|
2009-04-20 01:58:34 +02:00
|
|
|
|
if alias:
|
|
|
|
|
|
self.libInfo[alias] = libInfo
|
2008-07-11 05:35:48 +02:00
|
|
|
|
return libInfo
|
2010-05-21 01:59:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# extending the 'Configure' functionality of SCons,
|
|
|
|
|
|
# especially for library dependency checking
|
|
|
|
|
|
ConfigBase = SCons.SConf.SConfBase
|
2008-08-21 01:24:46 +02:00
|
|
|
|
|
2008-07-11 05:35:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
2008-08-21 01:24:46 +02:00
|
|
|
|
class LumieraConfigContext(ConfigBase):
|
2008-07-11 05:35:48 +02:00
|
|
|
|
""" Extends the SCons Configure context with some convenience methods
|
|
|
|
|
|
"""
|
|
|
|
|
|
def __init__(self, *args,**kw):
|
2008-08-21 01:24:46 +02:00
|
|
|
|
ConfigBase.__init__(self,*args,**kw)
|
2008-07-11 05:35:48 +02:00
|
|
|
|
|
2009-04-20 01:58:34 +02:00
|
|
|
|
def CheckPkgConfig (self, libID, minVersion=0, alias=None):
|
2022-09-05 23:02:29 +02:00
|
|
|
|
print("Checking for library configuration: %s " % libID)
|
2008-07-11 05:35:48 +02:00
|
|
|
|
# self.Message(self,"Checking for library configuration: %s " % libID)
|
2009-04-20 01:58:34 +02:00
|
|
|
|
return self.env.addLibInfo (libID, minVersion, alias)
|
2008-07-11 05:35:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
2011-01-29 23:09:02 +01:00
|
|
|
|
|
2025-11-25 02:14:21 +01:00
|
|
|
|
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
|
2011-01-30 18:56:51 +01:00
|
|
|
|
###############################################################################
|
|
|
|
|
|
####### Lumiera custom tools and builders #####################################
|
2011-01-29 23:09:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
def register_LumieraIconBuilder(env):
|
|
|
|
|
|
""" Registers a custom Builder for generating and installing Icons from SVG.
|
2011-01-29 23:09:02 +01:00
|
|
|
|
Additionally you need to build the tool (rsvg-convert.c)
|
2025-06-07 23:59:57 +02:00
|
|
|
|
used to generate png from the svg source using librsvg.
|
2011-01-29 23:09:02 +01:00
|
|
|
|
"""
|
|
|
|
|
|
|
2012-01-10 04:02:29 +01:00
|
|
|
|
import IconSvgRenderer as renderer # load Joel's python script for invoking the rsvg-convert (SVG render)
|
2025-11-19 22:40:08 +01:00
|
|
|
|
renderer.rsvgPath = env.subst("$TARGDIR/rsvg-convert").removeprefix('#')
|
|
|
|
|
|
# # the prefix '#' is a SCons specific convention,
|
|
|
|
|
|
# # which the external tool can not handle
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
#
|
|
|
|
|
|
# MD5 signature for this specific python source code...
|
|
|
|
|
|
thisCodeSignature = SConsFile(__file__).get_csig() + SConsFile(renderer.__file__).get_csig()
|
|
|
|
|
|
thisCodeSignature = bytearray(thisCodeSignature, 'utf-8')
|
2011-01-29 23:09:02 +01:00
|
|
|
|
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
|
|
|
|
|
|
class IconRenderAction(FunctionAction):
|
|
|
|
|
|
""" SCons Action subclass to provide a controlled cache signature.
|
|
|
|
|
|
@note: usually it would be sufficient to pass just a callable to the Builder,
|
|
|
|
|
|
however, our implementation calls into an external Python module and thus
|
|
|
|
|
|
the default signature from SCons would not be stable, since it relies
|
|
|
|
|
|
on a code representation including memory addresses. Without this,
|
|
|
|
|
|
the icons would be frequently rebuilt unnecessarily.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
FunctionAction.__init__(self, IconRenderAction.invokeRenderer
|
|
|
|
|
|
, {'cmdstr' : "rendering Icon: $SOURCE --> $TARGETS"}
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def get_contents(self, target, source, env):
|
|
|
|
|
|
""" a stable signature based on the source code """
|
|
|
|
|
|
return thisCodeSignature
|
2011-01-29 23:09:02 +01:00
|
|
|
|
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
@staticmethod
|
|
|
|
|
|
def invokeRenderer(target, source, env):
|
|
|
|
|
|
""" render the SVG icon with libRSVG """
|
|
|
|
|
|
source = str(source[0])
|
|
|
|
|
|
targetdir = env.subst(env.path.buildIcon).removeprefix('#')
|
|
|
|
|
|
renderer.main([source,targetdir])
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-29 23:09:02 +01:00
|
|
|
|
def createIconTargets(target,source,env):
|
|
|
|
|
|
""" parse the SVG to get the target file names """
|
|
|
|
|
|
source = str(source[0])
|
2011-01-30 22:12:55 +01:00
|
|
|
|
targetdir = env.path.buildIcon
|
2011-01-29 23:09:02 +01:00
|
|
|
|
targetfiles = renderer.getTargetNames(source) # parse SVG
|
2011-02-07 11:35:44 +01:00
|
|
|
|
|
|
|
|
|
|
# additionally create an installation task for each Icon to be generated
|
|
|
|
|
|
installLocation = env.path.installIcon
|
|
|
|
|
|
generateTargets = []
|
|
|
|
|
|
for icon in targetfiles:
|
|
|
|
|
|
icon = targetdir+icon
|
|
|
|
|
|
subdir = getDirname(str(icon))
|
|
|
|
|
|
env.Install (installLocation+subdir, icon)
|
2025-06-07 23:59:57 +02:00
|
|
|
|
generateTargets.append(icon)
|
2011-02-07 11:35:44 +01:00
|
|
|
|
|
|
|
|
|
|
return (generateTargets, source)
|
2011-01-30 22:12:55 +01:00
|
|
|
|
|
Build: chase down and solve spurious SVG icon rebuilds
Many years ago, I integrated the IconSvgRenderer (written by Joel Holdsworth)
directly into the SCons build by means of a custom builder with an ''emitter function''.
The build as such works fine and automatically determines which icons can be
generated from a given SVG source. However, those SVG icons tend to be rebuilt
very frequently, even while none of the SVG sources has changed.
Basically this was more of an annoyance, since we have now about 15 icons
and the rendering is really fast ... it is just ugly, especially on
incremental builds (and it could become a problem once we have a
massive amount of graphics to process.
So I took the opportunity to take close look now, since I am doing
uninspiring clean-up work since several weeks now. But that problem
turned out to be quite insidious and hard to spot.
First, AI set me off into a completely wrong angle, since it is ''not''
caused by a missing string representation of the custom Action class.
However, from there I went to watching the target checks with the debugger,
and this eventually got me to realise, that SCons mistakenly detects
a change in the Executor / the Action class.
The root cause is, that we invoke Python code from an external Module,
IconSvgRenderer.py, and this is »materialised« by SCons automatically
into a string representation, which includes memory addresses of functions
in that module. And that yields a signature, that is, quite obviously,
not stable, even while you end up sometimes with loading the code to
the same memory location.
As a solution / workaround, we now subclass the standard implementation
from SCons and override the signature function; instead of fingerprinting
the binary code, we just compute a MD5 over the python source file,
which can be easily achieved with the help of the File-Node from SCons.
Essential resources:
https://scons.org/doc/4.8.0/HTML/scons-user.html#chap-builders-writing
...and the Reference / Manpage of SCons
https://scons.org/doc/4.8.0/HTML/scons-man.html#action_objects
SCons/Action.py
SCons/Node/__init__.py
2025-11-27 02:08:50 +01:00
|
|
|
|
|
|
|
|
|
|
buildIcon = env.Builder( action = IconRenderAction()
|
|
|
|
|
|
, single_source = True
|
|
|
|
|
|
, emitter = createIconTargets
|
|
|
|
|
|
)
|
|
|
|
|
|
env.Append(BUILDERS = {'IconRender' : buildIcon})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def register_LumieraResourceBuilders(env):
|
|
|
|
|
|
""" Registers further Custom Methods for installing various Resources.
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2011-01-30 22:12:55 +01:00
|
|
|
|
def IconResource(env, source):
|
Global-Layer-Renaming: fix handling of GuiResources in the build
the new structure causes them now to be installed into $TARGET/stage
which is simply not what I want. I still consider $TARGET/gui the better choice,
since an administrator or packager is not aware of our layer namings.
The existing solution was half baked anyway, it did not really replicate the source tree.
On the other hand, I want to retain the location of the CSS files within the GUI tree,
since I consider it a good practice, to keep "code-like" resources with the actual code,
and not far away in some arcane "data" directory.
No I've noticed, that the env.GuiResource() function is only used once, for this very task.
So, for the time being, we can keep it simple and deditaced to that task, i.e
we pick up all CSS files we find and install it into a single target directory.
NOTE: this issue has brought to my attention two further, completely unrelated issues
* Ticket #1192 (Lumiera hangs on failed GUI start)
* The ProcDispatcher does an idle wait, due to an error in timed-wait implementation
2018-11-16 18:18:33 +01:00
|
|
|
|
""" copy icon pixmap to corresponding icon dir. """
|
2012-01-10 05:52:00 +01:00
|
|
|
|
subdir = getDirname(str(source))
|
|
|
|
|
|
toBuild = env.path.buildIcon+subdir
|
|
|
|
|
|
toInstall = env.path.installIcon+subdir
|
|
|
|
|
|
env.Install (toInstall, source)
|
|
|
|
|
|
return env.Install(toBuild, source)
|
2011-01-29 23:09:02 +01:00
|
|
|
|
|
2011-01-30 22:12:55 +01:00
|
|
|
|
def GuiResource(env, source):
|
2025-11-20 22:55:38 +01:00
|
|
|
|
""" pick up given source resource and install
|
Global-Layer-Renaming: fix handling of GuiResources in the build
the new structure causes them now to be installed into $TARGET/stage
which is simply not what I want. I still consider $TARGET/gui the better choice,
since an administrator or packager is not aware of our layer namings.
The existing solution was half baked anyway, it did not really replicate the source tree.
On the other hand, I want to retain the location of the CSS files within the GUI tree,
since I consider it a good practice, to keep "code-like" resources with the actual code,
and not far away in some arcane "data" directory.
No I've noticed, that the env.GuiResource() function is only used once, for this very task.
So, for the time being, we can keep it simple and deditaced to that task, i.e
we pick up all CSS files we find and install it into a single target directory.
NOTE: this issue has brought to my attention two further, completely unrelated issues
* Ticket #1192 (Lumiera hangs on failed GUI start)
* The ProcDispatcher does an idle wait, due to an error in timed-wait implementation
2018-11-16 18:18:33 +01:00
|
|
|
|
them (flat) into the configured target
|
|
|
|
|
|
"""
|
|
|
|
|
|
toBuild = env.path.buildUIRes
|
|
|
|
|
|
toInstall = env.path.installUIRes
|
2012-01-10 05:52:00 +01:00
|
|
|
|
env.Install (toInstall, source)
|
|
|
|
|
|
return env.Install(toBuild, source)
|
2011-01-30 22:12:55 +01:00
|
|
|
|
|
2012-01-10 08:06:09 +01:00
|
|
|
|
def ConfigData(env, prefix, source, targetDir=None):
|
2012-01-10 05:52:00 +01:00
|
|
|
|
""" install (copy) configuration- and metadata.
|
2025-11-20 22:55:38 +01:00
|
|
|
|
@param targetDir: when None, then use he install location configured (in Setup.py),
|
Build: provide a placeholder page for the (planned) User-Manual
Debian-Docbase allows to register some HTML documentation;
My old package definition added placeholder config, which renders
the documentation configuration invalid (as pointed out by Lintian).
However, I still think it is a good idea to have the anchor point
already defined, and thus I came up with the idea of in fact
providing some usable placeholder content...
As it turns out, we also have a placeholder page at the Lumiera website,
where the User Manual is assumed to be located later — so why not extend
this one and then provide the HTML-rendering for the DEB package?
To allow for this setup
* I have now extended the placeholder page for the Website
to include some generic description about Lumiera (from the 'about' page)
* Furthermore, I added the screenshot (from the »Outer Space« page)
* and I use this a an opportunity to document the various test / demo
facilities currently available in the GUI, since these are rather obscure.
While only intended for the developer, it seems still worthwhile
to describe the possible effects — it may well be that we retain
some of that test/demo functionality and in that case, we have
now already some starting point for a documentation
* Then, to include that page as stand-alone HTML, I used the 'Print Edit WE'-plugin
from Firefox, to encode the images as inline-base64 URLs (which are restored
by a tiny JavaScript embedded into that page)
* and last but not least, our SCons buildsystem needs the ability
to install such a documentation file, since it seems most adequate
to handle this requirement as part of the generic installation (and
not hidden in some Debian scripting)
2025-11-18 16:09:54 +01:00
|
|
|
|
otherwise an explicitly given absolute or relative path segment,
|
|
|
|
|
|
which might refer to the location of the executable through the $ORIGIN token
|
|
|
|
|
|
@param prefix: a prefix relative to the current path (location of SConscript),
|
|
|
|
|
|
i.e. typically a subdirectory where to find the source config file
|
2025-06-07 23:59:57 +02:00
|
|
|
|
"""
|
2012-01-10 08:06:09 +01:00
|
|
|
|
source = path.join(prefix,str(source))
|
|
|
|
|
|
subdir = getDirname(source, prefix) # removes source location path prefix
|
2012-01-10 05:52:00 +01:00
|
|
|
|
if targetDir:
|
|
|
|
|
|
if path.isabs(targetDir):
|
|
|
|
|
|
toBuild = toInstall = path.join(targetDir,subdir)
|
|
|
|
|
|
else:
|
|
|
|
|
|
if targetDir.startswith('$ORIGIN'):
|
|
|
|
|
|
targetDir = targetDir[len('$ORIGIN'):]
|
|
|
|
|
|
toBuild = path.join(env.path.buildExe, targetDir, subdir)
|
|
|
|
|
|
toInstall = path.join(env.path.installExe, targetDir, subdir)
|
|
|
|
|
|
else:
|
|
|
|
|
|
toBuild = path.join(env.path.buildConf, targetDir, subdir)
|
|
|
|
|
|
toInstall = path.join(env.path.installConf, targetDir, subdir)
|
|
|
|
|
|
else:
|
|
|
|
|
|
toBuild = path.join(env.path.buildConf,subdir)
|
|
|
|
|
|
toInstall = path.join(env.path.installConf,subdir)
|
|
|
|
|
|
env.Install (toInstall, source)
|
|
|
|
|
|
return env.Install(toBuild, source)
|
2011-01-29 23:09:02 +01:00
|
|
|
|
|
Build: provide a placeholder page for the (planned) User-Manual
Debian-Docbase allows to register some HTML documentation;
My old package definition added placeholder config, which renders
the documentation configuration invalid (as pointed out by Lintian).
However, I still think it is a good idea to have the anchor point
already defined, and thus I came up with the idea of in fact
providing some usable placeholder content...
As it turns out, we also have a placeholder page at the Lumiera website,
where the User Manual is assumed to be located later — so why not extend
this one and then provide the HTML-rendering for the DEB package?
To allow for this setup
* I have now extended the placeholder page for the Website
to include some generic description about Lumiera (from the 'about' page)
* Furthermore, I added the screenshot (from the »Outer Space« page)
* and I use this a an opportunity to document the various test / demo
facilities currently available in the GUI, since these are rather obscure.
While only intended for the developer, it seems still worthwhile
to describe the possible effects — it may well be that we retain
some of that test/demo functionality and in that case, we have
now already some starting point for a documentation
* Then, to include that page as stand-alone HTML, I used the 'Print Edit WE'-plugin
from Firefox, to encode the images as inline-base64 URLs (which are restored
by a tiny JavaScript embedded into that page)
* and last but not least, our SCons buildsystem needs the ability
to install such a documentation file, since it seems most adequate
to handle this requirement as part of the generic installation (and
not hidden in some Debian scripting)
2025-11-18 16:09:54 +01:00
|
|
|
|
def DocFile(env, prefix, source, target=None):
|
|
|
|
|
|
""" install (copy) files for documentation.
|
|
|
|
|
|
Always places the documentation below the standard location 'installDoc' configured in Setup.py
|
|
|
|
|
|
@param prefix: relative to current path (SConscript), will be stripped at destination
|
|
|
|
|
|
@param target: when given, the target will be named explicitly, or (when only a directory)
|
|
|
|
|
|
placed into a specific subdir, otherwise (when None) the source spec will be placed
|
|
|
|
|
|
into the corresponding subdir after stripping the prefix
|
|
|
|
|
|
"""
|
|
|
|
|
|
source = path.join(prefix,str(source))
|
|
|
|
|
|
subdir = getDirname(source, prefix) # removes source location path prefix
|
|
|
|
|
|
if not target:
|
|
|
|
|
|
target = subdir+'/'
|
|
|
|
|
|
elif target.endswith('/'):
|
|
|
|
|
|
target = target+subdir+'/'
|
|
|
|
|
|
toInstall = path.join(env.path.installDoc, target)
|
|
|
|
|
|
if toInstall.endswith('/'):
|
|
|
|
|
|
return env.Install(toInstall, source)
|
|
|
|
|
|
else:
|
|
|
|
|
|
return env.InstallAs(toInstall, source) # this renames at target
|
|
|
|
|
|
|
2011-01-30 18:56:51 +01:00
|
|
|
|
|
2011-01-30 22:12:55 +01:00
|
|
|
|
env.AddMethod(IconResource)
|
|
|
|
|
|
env.AddMethod(GuiResource)
|
|
|
|
|
|
env.AddMethod(ConfigData)
|
Build: provide a placeholder page for the (planned) User-Manual
Debian-Docbase allows to register some HTML documentation;
My old package definition added placeholder config, which renders
the documentation configuration invalid (as pointed out by Lintian).
However, I still think it is a good idea to have the anchor point
already defined, and thus I came up with the idea of in fact
providing some usable placeholder content...
As it turns out, we also have a placeholder page at the Lumiera website,
where the User Manual is assumed to be located later — so why not extend
this one and then provide the HTML-rendering for the DEB package?
To allow for this setup
* I have now extended the placeholder page for the Website
to include some generic description about Lumiera (from the 'about' page)
* Furthermore, I added the screenshot (from the »Outer Space« page)
* and I use this a an opportunity to document the various test / demo
facilities currently available in the GUI, since these are rather obscure.
While only intended for the developer, it seems still worthwhile
to describe the possible effects — it may well be that we retain
some of that test/demo functionality and in that case, we have
now already some starting point for a documentation
* Then, to include that page as stand-alone HTML, I used the 'Print Edit WE'-plugin
from Firefox, to encode the images as inline-base64 URLs (which are restored
by a tiny JavaScript embedded into that page)
* and last but not least, our SCons buildsystem needs the ability
to install such a documentation file, since it seems most adequate
to handle this requirement as part of the generic installation (and
not hidden in some Debian scripting)
2025-11-18 16:09:54 +01:00
|
|
|
|
env.AddMethod(DocFile)
|
2011-01-29 23:09:02 +01:00
|
|
|
|
|
2011-01-30 16:47:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2011-12-03 05:46:36 +01:00
|
|
|
|
wrapped object by Python magic. But some calls are intercepted in order to inject
|
|
|
|
|
|
suitable default configuration based on the project setup.
|
2011-01-30 16:47:03 +01:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, originalBuilder):
|
|
|
|
|
|
SCons.Util.Proxy.__init__ (self, originalBuilder)
|
|
|
|
|
|
|
2022-09-05 23:02:29 +02:00
|
|
|
|
def __bool__(self): return True
|
2011-02-06 15:12:13 +01:00
|
|
|
|
|
2011-01-30 16:47:03 +01:00
|
|
|
|
def __call__(self, env, target=None, source=None, **kw):
|
|
|
|
|
|
""" when the builder gets invoked from the SConscript...
|
|
|
|
|
|
create a clone environment for specific configuration
|
2011-01-30 19:43:51 +01:00
|
|
|
|
and then pass on the call to the wrapped original builder.
|
|
|
|
|
|
Automatically define installation targets for build results.
|
2025-06-07 23:59:57 +02:00
|
|
|
|
@note only returning the build targets, not the install targets
|
2011-01-30 16:47:03 +01:00
|
|
|
|
"""
|
2011-01-30 22:12:55 +01:00
|
|
|
|
customisedEnv = self.getCustomEnvironment(env, target=target, **kw) # defined in subclasses
|
2011-01-30 18:56:51 +01:00
|
|
|
|
buildTarget = self.buildLocation(customisedEnv, target)
|
2011-01-30 22:12:55 +01:00
|
|
|
|
buildTarget = self.invokeOriginalBuilder(customisedEnv, buildTarget, source, **kw)
|
2025-06-07 23:59:57 +02:00
|
|
|
|
self.installTarget(customisedEnv, buildTarget, **kw)
|
|
|
|
|
|
return buildTarget
|
2011-01-30 18:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 []
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-30 16:47:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
2011-01-30 19:20:02 +01:00
|
|
|
|
custEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/modules,--enable-new-dtags" )
|
2013-11-02 23:54:45 +01:00
|
|
|
|
if 'addLibs' in kw:
|
|
|
|
|
|
custEnv.Append(LIBS = kw['addLibs'])
|
2011-01-30 16:47:03 +01:00
|
|
|
|
return custEnv
|
2011-01-30 18:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildExe
|
|
|
|
|
|
def getInstallDestination(self, lumiEnv): return lumiEnv.path.installExe
|
2025-06-07 23:59:57 +02:00
|
|
|
|
|
2011-01-30 18:56:51 +01:00
|
|
|
|
|
2011-01-30 16:47:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LumieraModuleBuilder(WrappedStandardExeBuilder):
|
|
|
|
|
|
|
|
|
|
|
|
def getCustomEnvironment(self, lumiEnv, target, **kw):
|
|
|
|
|
|
""" augments the built-in SharedLibrary() builder to add some tweaks missing in SCons 1.0,
|
fix for resolution of transitive dependencies between "Lumiera modules"
This is a somewhat intricate problem. As long as we linked with --no-as-needed,
these problems could not manifest themselves, since all dependencies are spotted
correctly by SCons and thus added as direct children of the executable.
But when we switch to --as-needed linking, the linker will omit some of
the dependencies given from the build system, when the code to be linked
doesn't call directly into these dependencies. But of course dynamic modules
may depend on each other, and indeed, the Lumiera libs do so. Thus
the linker may omit the dependency to liblumierasupport, and just add
a dependency to, say liblumierabackend. But the backend in turn
depends on the support library.
Now the problem is, that when resolving several steps deep into such
a dependency chain, our special relative path resolution scheme fails.
The fix is to give each lumiera module itself another relative path
resolution spec, which overrides at that point the root spec given
for the executable. Thus, we define
- for the executable: "search at $ORIGIN/modules"
- for the modules: "search at $ORIGIN/../modules"
This accounts for the fact, that a module, which is the Origin
for a transitive resolution step, already sits in a subdirectory
below the executable; thus step one level up and devle down into
the hard wired modules directory. Alternatively, we could also
use just "search at $ORIGIN" (i.e. in the same directory).
But assuming that in future we'll roll several core plugins,
which also count as "Lumiera modules", the scheme defined here
is more flexible, since it allows to place those core plugins
into sibling directories.
2014-10-01 00:21:47 +02:00
|
|
|
|
like setting a SONAME proper instead of just passing the relative pathname to the linker.
|
|
|
|
|
|
Besides, we override the library search path to allow for transitive dependencies between
|
2025-06-07 23:59:57 +02:00
|
|
|
|
Lumiera modules; modules are assumed to reside in a subdirectory below the executable.
|
2011-01-30 16:47:03 +01:00
|
|
|
|
"""
|
|
|
|
|
|
custEnv = lumiEnv.Clone()
|
2025-11-24 22:03:11 +01:00
|
|
|
|
custEnv.Append( LINKFLAGS = "-Wl,-soname="+self.defineSoname(target,**kw))
|
fix for resolution of transitive dependencies between "Lumiera modules"
This is a somewhat intricate problem. As long as we linked with --no-as-needed,
these problems could not manifest themselves, since all dependencies are spotted
correctly by SCons and thus added as direct children of the executable.
But when we switch to --as-needed linking, the linker will omit some of
the dependencies given from the build system, when the code to be linked
doesn't call directly into these dependencies. But of course dynamic modules
may depend on each other, and indeed, the Lumiera libs do so. Thus
the linker may omit the dependency to liblumierasupport, and just add
a dependency to, say liblumierabackend. But the backend in turn
depends on the support library.
Now the problem is, that when resolving several steps deep into such
a dependency chain, our special relative path resolution scheme fails.
The fix is to give each lumiera module itself another relative path
resolution spec, which overrides at that point the root spec given
for the executable. Thus, we define
- for the executable: "search at $ORIGIN/modules"
- for the modules: "search at $ORIGIN/../modules"
This accounts for the fact, that a module, which is the Origin
for a transitive resolution step, already sits in a subdirectory
below the executable; thus step one level up and devle down into
the hard wired modules directory. Alternatively, we could also
use just "search at $ORIGIN" (i.e. in the same directory).
But assuming that in future we'll roll several core plugins,
which also count as "Lumiera modules", the scheme defined here
is more flexible, since it allows to place those core plugins
into sibling directories.
2014-10-01 00:21:47 +02:00
|
|
|
|
custEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/../modules,--enable-new-dtags" )
|
2013-11-02 23:54:45 +01:00
|
|
|
|
if 'addLibs' in kw:
|
|
|
|
|
|
custEnv.Append(LIBS = kw['addLibs'])
|
2011-01-30 16:47:03 +01:00
|
|
|
|
return custEnv
|
|
|
|
|
|
|
2011-01-30 18:56:51 +01:00
|
|
|
|
def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildLib
|
|
|
|
|
|
def getInstallDestination(self, lumiEnv): return lumiEnv.path.installLib
|
|
|
|
|
|
|
2011-01-30 16:47:03 +01:00
|
|
|
|
|
2025-11-24 22:03:11 +01:00
|
|
|
|
def installTarget(self, env, buildTarget, **kw):
|
|
|
|
|
|
""" ensure a shared library is not marked executable.
|
|
|
|
|
|
The default toolchain on Linux often installs shared libraries as executable, which seems
|
|
|
|
|
|
to be necessary on some arcane Unix platforms. However, Debian Policy prohibits that.
|
|
|
|
|
|
See https://unix.stackexchange.com/questions/400187/why-should-or-should-not-shared-libraries-be-executable-e-g-red-hat-vs-debian
|
|
|
|
|
|
"""
|
|
|
|
|
|
toInstall = super().installTarget(env, buildTarget, **kw)
|
|
|
|
|
|
if toInstall:
|
|
|
|
|
|
def _Chmod(target, source, env):
|
|
|
|
|
|
""" Workaround since env.Chmod is present only in SCons 4.10 """
|
|
|
|
|
|
import os
|
|
|
|
|
|
for t in target:
|
|
|
|
|
|
os.chmod(str(t), 0o644)
|
|
|
|
|
|
return None
|
2025-11-25 02:14:21 +01:00
|
|
|
|
# removeExecBit = env.Chmod(toInstall, 0o644) # ◁◁◁ could use this for SCons > 4.10
|
2025-11-24 22:03:11 +01:00
|
|
|
|
msg = '....... clear exec perm %s' % [str(t) for t in toInstall]
|
|
|
|
|
|
removeExecBit = env.Action(_Chmod, msg)
|
|
|
|
|
|
env.AddPostAction(toInstall, removeExecBit)
|
|
|
|
|
|
return toInstall
|
|
|
|
|
|
|
2011-01-30 16:47:03 +01:00
|
|
|
|
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:
|
2011-12-03 05:46:36 +01:00
|
|
|
|
soname = self.subst(kw['soname']) # explicitly defined by user
|
2011-01-30 16:47:03 +01:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-30 18:56:51 +01:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-30 22:12:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-30 16:47:03 +01:00
|
|
|
|
def register_LumieraCustomBuilders (lumiEnv):
|
2011-12-03 05:46:36 +01:00
|
|
|
|
""" install the customised builder versions tightly integrated with our build system.
|
2011-01-30 16:47:03 +01:00
|
|
|
|
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
|
|
|
|
|
|
"""
|
2011-01-30 18:56:51 +01:00
|
|
|
|
programBuilder = LumieraExeBuilder (lumiEnv['BUILDERS']['Program'])
|
|
|
|
|
|
libraryBuilder = LumieraModuleBuilder (lumiEnv['BUILDERS']['SharedLibrary'])
|
|
|
|
|
|
smoduleBuilder = LumieraModuleBuilder (lumiEnv['BUILDERS']['LoadableModule'])
|
|
|
|
|
|
lpluginBuilder = LumieraPluginBuilder (lumiEnv['BUILDERS']['LoadableModule'])
|
2011-01-30 16:47:03 +01:00
|
|
|
|
|
2011-01-30 18:56:51 +01:00
|
|
|
|
lumiEnv['BUILDERS']['Program'] = programBuilder
|
|
|
|
|
|
lumiEnv['BUILDERS']['SharedLibrary'] = libraryBuilder
|
|
|
|
|
|
lumiEnv['BUILDERS']['LoadableModule'] = smoduleBuilder
|
2011-01-30 17:00:15 +01:00
|
|
|
|
lumiEnv['BUILDERS']['LumieraPlugin'] = lpluginBuilder
|
2011-01-30 22:12:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def SymLink(env, 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)
|
|
|
|
|
|
|
|
|
|
|
|
if linktext: srcSpec=linktext
|
|
|
|
|
|
else: srcSpec='$SOURCE'
|
|
|
|
|
|
action = Action(makeLink, "Install link: $TARGET -> "+srcSpec)
|
|
|
|
|
|
env.Command (target,source, action)
|
|
|
|
|
|
|
2011-12-03 05:46:36 +01:00
|
|
|
|
# adding SymLink directly as method on the environment object
|
2011-01-30 22:12:55 +01:00
|
|
|
|
# Probably that should better be a real builder, but I couldn't figure out
|
|
|
|
|
|
# how to get the linktext through literally, which is necessary for relative links.
|
|
|
|
|
|
# Judging from the sourcecode of SCons.Builder.BuilderBase, there seems to be no way
|
|
|
|
|
|
# to set the executor_kw, which are passed through to the action object.
|
|
|
|
|
|
lumiEnv.AddMethod(SymLink)
|