diff options
author | Pawel Tomulik <ptomulik@meil.pw.edu.pl> | 2015-09-02 23:02:08 (GMT) |
---|---|---|
committer | Pawel Tomulik <ptomulik@meil.pw.edu.pl> | 2015-09-02 23:02:08 (GMT) |
commit | 596b7aca20e286ecb45dade015ab9e89ac6aa791 (patch) | |
tree | 71e59b1562c5d867550c13291491de386ea3a1c7 /src | |
parent | a6a65ee80272d61fa2e09e33eeedfa0e08aed333 (diff) | |
download | SCons-596b7aca20e286ecb45dade015ab9e89ac6aa791.zip SCons-596b7aca20e286ecb45dade015ab9e89ac6aa791.tar.gz SCons-596b7aca20e286ecb45dade015ab9e89ac6aa791.tar.bz2 |
new versioned libraries - gnulink and cyglink for now
Diffstat (limited to 'src')
-rw-r--r-- | src/engine/SCons/Defaults.py | 7 | ||||
-rw-r--r-- | src/engine/SCons/Platform/cygwin.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Tool/__init__.py | 428 | ||||
-rw-r--r-- | src/engine/SCons/Tool/__init__.xml | 37 | ||||
-rw-r--r-- | src/engine/SCons/Tool/cyglink.py | 148 | ||||
-rw-r--r-- | src/engine/SCons/Tool/cyglink.xml | 49 | ||||
-rw-r--r-- | src/engine/SCons/Tool/gnulink.py | 117 | ||||
-rw-r--r-- | src/engine/SCons/Tool/gnulink.xml | 4 | ||||
-rw-r--r-- | src/engine/SCons/Tool/install.py | 130 | ||||
-rw-r--r-- | src/engine/SCons/Tool/install.xml | 11 | ||||
-rw-r--r-- | src/engine/SCons/Tool/link.py | 148 | ||||
-rw-r--r-- | src/engine/SCons/Tool/link.xml | 154 | ||||
-rw-r--r-- | src/engine/SCons/Tool/linkloc.py | 1 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mingw.py | 1 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslink.py | 1 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mwld.py | 1 | ||||
-rw-r--r-- | src/engine/SCons/Tool/qt.py | 1 |
17 files changed, 888 insertions, 354 deletions
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 6500443..db48969 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -482,6 +482,10 @@ class Variable_Method_Caller(object): frame = frame.f_back return None +def __libversionflags_string(versionvar): + return '${("%s" in locals() and %s and "_%sFLAGS" in locals()) ' \ + 'and _%sFLAGS or None}' % (versionvar, versionvar, versionvar, versionvar) + ConstructionEnvironment = { 'BUILDERS' : {}, 'SCANNERS' : [], @@ -499,6 +503,9 @@ ConstructionEnvironment = { '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', + + '__SHLIBVERSIONFLAGS' : __libversionflags_string('SHLIBVERSION'), + '__LDMODULEVERSIONFLAGS' : __libversionflags_string('LDMODULEVERSION'), 'TEMPFILE' : NullCmdGenerator, 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py index a012682..34c79ff 100644 --- a/src/engine/SCons/Platform/cygwin.py +++ b/src/engine/SCons/Platform/cygwin.py @@ -42,8 +42,8 @@ def generate(env): env['PROGSUFFIX'] = '.exe' env['SHLIBPREFIX'] = '' env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX', '$IMPLIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX', '$IMPLIBSUFFIX' ] env['TEMPFILE'] = TempFileMunge env['TEMPFILEPREFIX'] = '@' env['MAXLINELENGTH'] = 2048 diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index efd2e33..4a27c0d 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -236,150 +236,304 @@ def createStaticLibBuilder(env): return static_lib -def VersionShLibLinkNames(version, libname, env): - """Generate names of symlinks to the versioned shared library""" - Verbose = False - platform = env.subst('$PLATFORM') - shlib_suffix = env.subst('$SHLIBSUFFIX') - shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS')) - - linknames = [] - if version.count(".") != 2: - # We need a version string of the form x.y.z to proceed - # Several changes need to be made to support versions like x.y - raise ValueError - - if platform == 'darwin': - # For libfoo.x.y.z.dylib, linknames libfoo.so - suffix_re = re.escape('.' + version + shlib_suffix) - linkname = re.sub(suffix_re, shlib_suffix, libname) - if Verbose: - print "VersionShLibLinkNames: linkname = ",linkname - linknames.append(linkname) - elif platform == 'posix' or platform == 'sunos': - if sys.platform.startswith('openbsd'): - # OpenBSD uses x.y shared library versioning numbering convention - # and doesn't use symlinks to backwards-compatible libraries - return [] - # For libfoo.so.x.y.z, linknames libfoo.so libfoo.so.x.y libfoo.so.x - suffix_re = re.escape(shlib_suffix + '.' + version) - # First linkname has no version number - linkname = re.sub(suffix_re, shlib_suffix, libname) - if Verbose: - print "VersionShLibLinkNames: linkname = ",linkname - linknames.append(linkname) - versionparts = version.split('.') - major_name = linkname + "." + versionparts[0] - minor_name = major_name + "." + versionparts[1] - #Only add link for major_name - #for linkname in [major_name, minor_name]: - for linkname in [major_name, ]: - if Verbose: - print "VersionShLibLinkNames: linkname ",linkname, ", target ",libname - linknames.append(linkname) - # note: no Windows case here (win32 or cygwin); - # MSVC doesn't support this type of versioned shared libs. - # (could probably do something for MinGW though) - return linknames - -def VersionedSharedLibrary(target = None, source= None, env=None): - """Build a shared library. If the environment has SHLIBVERSION -defined make a versioned shared library and create the appropriate -symlinks for the platform we are on""" +def _call_env_cb(env, callback, args, result = None): + """Returns the result of env[callback](*args) call if env[callback] is + callable. If env[callback] does not exist or is not callable, return the + value provided as the *result* argument. This function is mainly used for + generating library info such as versioned suffixes, symlink maps, sonames + etc. by delegating the core job to callbacks configured by current linker + tool""" + Verbose = False + + if Verbose: + print '_call_env_cb: args=%r' % args + print '_call_env_cb: callback=%r' % callback + try: - version = env.subst('$SHLIBVERSION') + cbfun = env[callback] except KeyError: + pass + else: + if Verbose: + print '_call_env_cb: env[%r] found' % callback + print '_call_env_cb: env[%r]=%r' % (callback, cbfun) + if(callable(cbfun)): + if Verbose: + print '_call_env_cb: env[%r] is callable' % callback + result = cbfun(env, *args) + return result + +class _LibInfoGeneratorBase(object): + """Generator base class for library-related info such as suffixes for + versioned libraries, symlink maps, sonames etc. It handles commonities + of SharedLibrary and LoadableModule + """ + def __init__(self, libtype, infoname): + self.set_libtype(libtype) + self.set_infoname(infoname) + + def set_libtype(self, libtype): + if libtype not in ['ShLib', 'LdMod', 'ImpLib']: + raise ValueError('unsupported libtype %r' % libtype) + self.libtype = libtype + + def get_libtype(self): + return self.libtype + + def set_infoname(self, infoname): + self.infoname = infoname + + def get_infoname(self): + return self.infoname + + def get_lib_prefix(self, env): + prefix = None + libtype = self.get_libtype() + if libtype == 'ShLib': + prefix = env.subst('$SHLIBPREFIX') + elif libtype == 'LdMod': + prefix = env.subst('$LDMODULEPREFIX') + elif libtype == 'ImpLib': + prefix = env.subst('$IMPLIBPREFIX') + return prefix + + def get_lib_suffix(self, env): + suffix = None + libtype = self.get_libtype() + if libtype == 'ShLib': + suffix = env.subst('$SHLIBSUFFIX') + elif libtype == 'LdMod': + suffix = env.subst('$LDMODULESUFFIX') + elif libtype == 'ImpLib': + suffix = env.subst('$IMPLIBSUFFIX') + return suffix + + def get_lib_version(self, env, **kw): version = None + libtype = self.get_libtype() + if libtype == 'ShLib': + version = env.subst('$SHLIBVERSION') + elif libtype == 'LdMod': + version = env.subst('$LDMODULEVERSION') + elif libtype == 'ImpLib': + version = env.subst('$IMPLIBVERSION') + if not version: + try: lt = kw['implib_libtype'] + except KeyError: pass + else: + if lt == 'ShLib': + version = env.subst('$SHLIBVERSION') + elif lt == 'LdMod': + version = env.subst('$LDMODULEVERSION') + return version + + def get_versioned_lib_info_generator(self, **kw): + try: libtype = kw['generator_libtype'] + except KeyError: libtype = self.get_libtype() + infoname = self.get_infoname() + return 'GenerateVersioned%s%s' % (libtype, infoname) + + def generate_versioned_lib_info(self, env, args, result = None, **kw): + callback = self.get_versioned_lib_info_generator(**kw) + return _call_env_cb(env, callback, args, result) + +class _LibSuffixGenerator(_LibInfoGeneratorBase): + """Library suffix generator, used as target_suffix in SharedLibrary and + LoadableModule builders""" + def __init__(self, libtype): + super(_LibSuffixGenerator, self).__init__(libtype, 'Suffix') + + def __call__(self, env, sources = None, **kw): + Verbose = False + + suffix = self.get_lib_suffix(env) + if Verbose: + print "_LibSuffixGenerator: input suffix=%r" % suffix - # libname includes the version number if one was given - libname = getattr(target[0].attributes, 'shlibname', target[0].name) - platform = env.subst('$PLATFORM') - shlib_suffix = env.subst('$SHLIBSUFFIX') - shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS')) - if Verbose: - print "VersionShLib: libname = ",libname - print "VersionShLib: platform = ",platform - print "VersionShLib: shlib_suffix = ",shlib_suffix - print "VersionShLib: target = ",str(target[0]) - - if version: - # set the shared library link flags - if platform == 'posix': - shlink_flags += [ '-Wl,-Bsymbolic' ] - # OpenBSD doesn't usually use SONAME for libraries - if not sys.platform.startswith('openbsd'): - # continue setup of shlink flags for all other POSIX systems - suffix_re = re.escape(shlib_suffix + '.' + version) - (major, age, revision) = version.split(".") - # soname will have only the major version number in it - soname = re.sub(suffix_re, shlib_suffix, libname) + '.' + major - shlink_flags += [ '-Wl,-soname=%s' % soname ] - if Verbose: - print " soname ",soname,", shlink_flags ",shlink_flags - elif platform == 'sunos': - suffix_re = re.escape(shlib_suffix + '.' + version) - (major, age, revision) = version.split(".") - soname = re.sub(suffix_re, shlib_suffix, libname) + '.' + major - shlink_flags += [ '-h', soname ] - elif platform == 'cygwin': - shlink_flags += [ '-Wl,-Bsymbolic', - '-Wl,--out-implib,${TARGET.base}.a' ] - elif platform == 'darwin': - shlink_flags += [ '-current_version', '%s' % version, - '-compatibility_version', '%s' % version, - '-undefined', 'dynamic_lookup' ] + version = self.get_lib_version(env, **kw) if Verbose: - print "VersionShLib: shlink_flags = ",shlink_flags - envlink = env.Clone() - envlink['SHLINKFLAGS'] = shlink_flags - else: - envlink = env + print "_LibSuffixGenerator: version=%r" % version - result = SCons.Defaults.ShLinkAction(target, source, envlink) + if version: + suffix = self.generate_versioned_lib_info(env, [suffix, version], suffix, **kw) - if version: - # here we need the full pathname so the links end up in the right directory - libname = getattr(target[0].attributes, 'shlibpath', target[0].get_internal_path()) if Verbose: - print "VerShLib: target lib is = ", libname - print "VerShLib: name is = ", target[0].name - print "VerShLib: dir is = ", target[0].dir.path - linknames = VersionShLibLinkNames(version, libname, env) + print "_LibSuffixGenerator: return suffix=%r" % suffix + return suffix + +ShLibSuffixGenerator = _LibSuffixGenerator('ShLib') +LdModSuffixGenerator = _LibSuffixGenerator('LdMod') +ImpLibSuffixGenerator = _LibSuffixGenerator('ImpLib') + +class _LibSymlinkGenerator(_LibInfoGeneratorBase): + """Library symlink map generator. It generates a dict of symlinks that + should be created by SharedLibrary or LoadableModule builders""" + def __init__(self, libtype): + super(_LibSymlinkGenerator, self).__init__(libtype, 'Symlinks') + + def get_noversionsymlinks(self, env, **kw): + disable = None + libtype = self.get_libtype() + if libtype == 'ShLib': + disable = env.subst('$SHLIBNOVERSIONSYMLINKS') + elif libtype == 'LdMod': + disable = env.subst('$LDMODULENOVERSIONSYMLINKS') + elif libtype == 'ImpLib': + try: env['IMPLIBNOVERSIONSYMLINKS'] + except KeyError: + try: lt = kw['implib_libtype'] + except KeyError: pass + else: + if lt == 'ShLib': + disable = env.subst('$SHLIBNOVERSIONSYMLINKS') + elif lt == 'LdMod': + disable = env.subst('$LDMODULENOVERSIONSYMLINKS') + else: + disable = env.subst('$IMPLIBNOVERSIONSYMLINKS') + return disable + + def __call__(self, env, libnode, **kw): + Verbose = False + if Verbose: - print "VerShLib: linknames ",linknames - # Here we just need the file name w/o path as the target of the link - lib_ver = getattr(target[0].attributes, 'shlibname', target[0].name) - # make symlink of adjacent names in linknames - for count in range(len(linknames)): - linkname = linknames[count] - if count > 0: - try: - os.remove(lastlinkname) - except: - pass - os.symlink(os.path.basename(linkname),lastlinkname) - if Verbose: - print "VerShLib: made sym link of %s -> %s" % (lastlinkname,linkname) - lastlinkname = linkname - # finish chain of sym links with link to the actual library - if len(linknames)>0: - try: - os.remove(lastlinkname) - except: - pass - os.symlink(lib_ver,lastlinkname) + print "_LibSymLinkGenerator: str(libnode)=%r" % str(libnode) + + symlinks = None + + version = self.get_lib_version(env, **kw) + disable = self.get_noversionsymlinks(env, **kw) + if Verbose: + print '_LibSymlinkGenerator: version=%r' % version + print '_LibSymlinkGenerator: disable=%r' % disable + + if version and not disable: + suffix = self.get_lib_suffix(env) + symlinks = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) + + if Verbose: + print '_LibSymlinkGenerator: return symlinks=%r' % symlinks + return symlinks + +ShLibSymlinkGenerator = _LibSymlinkGenerator('ShLib') +LdModSymlinkGenerator = _LibSymlinkGenerator('LdMod') +ImpLibSymlinkGenerator = _LibSymlinkGenerator('ImpLib') + +class _LibNameGenerator(_LibInfoGeneratorBase): + """Library name generator. Returns library name (e.g. libfoo.so) for + a given node (e.g. /foo/bar/libfoo.so.0.1.2)""" + def __init__(self, libtype): + super(_LibNameGenerator, self).__init__(libtype, 'Name') + + def __call__(self, env, libnode, **kw): + """Returns library name with version suffixes stripped""" + Verbose = False + + if Verbose: + print "_LibNameGenerator: str(libnode)=%r" % str(libnode) + + version = self.get_lib_version(env, **kw) + if Verbose: + print '_LibNameGenerator: version=%r' % version + + name = None + if version: + suffix = self.get_lib_suffix(env) + name = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) + + if not name: + name = os.path.basename(str(libnode)) + + if Verbose: + print '_LibNameGenerator: return name=%r' % name + + return name + +ShLibNameGenerator = _LibNameGenerator('ShLib') +LdModNameGenerator = _LibNameGenerator('LdMod') +ImpLibNameGenerator = _LibNameGenerator('ImpLib') + +class _LibSonameGenerator(_LibInfoGeneratorBase): + """Library soname generator. Returns library soname (e.g. libfoo.so.0) for + a given node (e.g. /foo/bar/libfoo.so.0.1.2)""" + def __init__(self, libtype): + super(_LibSonameGenerator, self).__init__(libtype, 'Soname') + + def __call__(self, env, libnode, **kw): + """Returns a SONAME based on a shared library's node path""" + Verbose = False + + if Verbose: + print "_LibSonameGenerator: str(libnode)=%r" % str(libnode) + + soname = env.subst('$SONAME') + if not soname: + version = self.get_lib_version(env,**kw) if Verbose: - print "VerShLib: made sym link of %s -> %s" % (linkname, lib_ver) - return result + print "_LibSonameGenerator: version=%r" % version + if version: + suffix = self.get_lib_suffix(env) + soname = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) + + if not soname: + # fallback to library name (as returned by appropriate _LibNameGenerator) + soname = _LibNameGenerator(self.get_libtype())(env, libnode) + if Verbose: + print "_LibSonameGenerator: FALLBACK: soname=%r" % soname + + if Verbose: + print "_LibSonameGenerator: return soname=%r" % soname -# Fix http://scons.tigris.org/issues/show_bug.cgi?id=2903 : -# Ensure we still depend on SCons.Defaults.ShLinkAction command line which is $SHLINKCOM. -# This was tricky because we don't want changing LIBPATH to cause a rebuild, but -# changing other link args should. LIBPATH has $( ... $) around it but until this -# fix, when the varlist was added to the build sig those ignored parts weren't getting -# ignored. -ShLibAction = SCons.Action.Action(VersionedSharedLibrary, None, varlist=['SHLINKCOM']) + return soname + +ShLibSonameGenerator = _LibSonameGenerator('ShLib') +LdModSonameGenerator = _LibSonameGenerator('LdMod') + +def EmitLibSymlinks(env, symlinks, libnode): + """Used by emitters to handle (shared/versioned) library symlinks""" + Verbose = False + for linkname, linktgt in symlinks.iteritems(): + env.SideEffect(linkname, linktgt) + if(Verbose): + print "EmitLibSymlinks: SideEffect(", linkname, ", ", linktgt, ")" + clean = list(set(filter(lambda x : x != linktgt, symlinks.keys() + [str(libnode)]))) + env.Clean(linktgt, clean) + if(Verbose): + print "EmitLibSymlinks: Clean(%r,%r)" % (linktgt, clean) + +def CreateLibSymlinks(env, symlinks): + """Physically creates symlinks. The symlinks argument must be a dict in + form { linkname : linktarget } + """ + + Verbose = False + for linkname, linktgt in symlinks.iteritems(): + linkname = str(env.arg2nodes(linkname)[0]) + linkdir = os.path.dirname(linkname) + if linkdir: + # NOTE: os.path.relpath appears in python 2.6 + linktgt = os.path.relpath(linktgt, linkdir) + else: + linktgt = os.path.basename(linktgt) + if(Verbose): + print "CreateLibSymlinks: preparing to add symlink ", linkname, " -> ", linktgt + try: + os.remove(linkname) + except: + pass + os.symlink(linktgt, linkname) + if(Verbose): + print "CreateLibSymlinks: add symlink ", linkname, " -> ", linktgt + return 0 + +def LibSymlinksActionFunction(target, source, env): + for tgt in target: + symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None) + if symlinks: + CreateLibSymlinks(env, symlinks) + return 0 + +LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, None) def createSharedLibBuilder(env): """This is a utility function that creates the SharedLibrary @@ -393,11 +547,12 @@ def createSharedLibBuilder(env): except KeyError: import SCons.Defaults action_list = [ SCons.Defaults.SharedCheck, - ShLibAction ] + SCons.Defaults.ShLinkAction, + LibSymlinksAction ] shared_lib = SCons.Builder.Builder(action = action_list, emitter = "$SHLIBEMITTER", prefix = '$SHLIBPREFIX', - suffix = '$SHLIBSUFFIX', + suffix = ShLibSuffixGenerator, target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', src_builder = 'SharedObject') @@ -417,11 +572,12 @@ def createLoadableModuleBuilder(env): except KeyError: import SCons.Defaults action_list = [ SCons.Defaults.SharedCheck, - SCons.Defaults.LdModuleLinkAction ] + SCons.Defaults.LdModuleLinkAction, + LibSymlinksAction ] ld_module = SCons.Builder.Builder(action = action_list, emitter = "$LDMODULEEMITTER", prefix = '$LDMODULEPREFIX', - suffix = '$LDMODULESUFFIX', + suffix = LdModSuffixGenerator, target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', src_builder = 'SharedObject') diff --git a/src/engine/SCons/Tool/__init__.xml b/src/engine/SCons/Tool/__init__.xml index ee56fc3..7102fa4 100644 --- a/src/engine/SCons/Tool/__init__.xml +++ b/src/engine/SCons/Tool/__init__.xml @@ -464,6 +464,17 @@ as C++ files. </summary> </cvar> +<cvar name="IMPLIBVERSION"> +<summary> +<para> +Used to override &cv-link-SHLIBVERSION;/&cv-link-LDMODULEVERSION; when +generating versioned import library for a shared library/loadable module. If +undefined, the &cv-link-SHLIBVERSION;/&cv-link-LDMODULEVERSION; is used do +determine the version of versioned import library. +</para> +</summary> +</cvar> + <cvar name="LIBEMITTER"> <summary> <para> @@ -472,6 +483,21 @@ TODO </summary> </cvar> +<cvar name="LDMODULEVERSION"> +<summary> +<para> +When this construction variable is defined, a versioned loadable module +is created by &b-link-LoadableModule; builder. This activates the +&cv-link-_LDMODULEVERSIONFLAGS; and thus modifies the &cv-link-LDMODULECOM; as +required, adds the version number to the library name, and creates the symlinks +that are needed. &cv-link-LDMODULEVERSION; needs to be of the form X.Y.Z, where +X and Y are numbers, and Z is a number but can also contain letters to +designate alpha, beta, or release candidate patch levels. By default +&cv-link-LDMODULEVERSION; is set to $SHLIBVERSION. +</para> +</summary> +</cvar> + <cvar name="SHLIBEMITTER"> <summary> <para> @@ -492,11 +518,12 @@ TODO <summary> <para> When this construction variable is defined, a versioned shared library -is created. This modifies the &cv-link-SHLINKFLAGS; as required, adds -the version number to the library name, and creates the symlinks that -are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z, -where X and Y are numbers, and Z is a number but can also contain -letters to designate alpha, beta, or release candidate patch levels. +is created by &b-link-SharedLibrary; builder. This activates the +&cv-link-_SHLIBVERSIONFLAGS; and thus modifies the &cv-link-SHLINKCOM; as +required, adds the version number to the library name, and creates the symlinks +that are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z, where X +and Y are numbers, and Z is a number but can also contain letters to designate +alpha, beta, or release candidate patch levels. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index 87716cf..4033978 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -7,19 +7,26 @@ It will usually be imported through the generic SCons.Tool.Tool() selection method. """ +import re +import os import SCons.Action import SCons.Util +import SCons.Tool import gnulink -def shlib_generator(target, source, env, for_signature): - cmd = SCons.Util.CLVar(['$SHLINK']) +def _lib_generator(target, source, env, for_signature, **kw): + try: cmd = kw['cmd'] + except KeyError: cmd = SCons.Util.CLVar(['$SHLINK']) + + try: vp = kw['varprefix'] + except KeyError: vp = 'SHLIB' - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp) if dll: cmd.extend(['-o', dll]) - cmd.extend(['$SHLINKFLAGS', '$__RPATH']) + cmd.extend(['$SHLINKFLAGS', '$__%sVERSIONFLAGS' % vp, '$__RPATH']) implib = env.FindIxes(target, 'IMPLIBPREFIX', 'IMPLIBSUFFIX') if implib: @@ -35,15 +42,34 @@ def shlib_generator(target, source, env, for_signature): return [cmd] -def shlib_emitter(target, source, env): - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + +def shlib_generator(target, source, env, for_signature): + return _lib_generator(target, source, env, for_signature, + varprefix='SHLIB', + cmd = SCons.Util.CLVar(['$SHLINK'])) + +def ldmod_generator(target, source, env, for_signature): + return _lib_generator(target, source, env, for_signature, + varprefix='LDMODULE', + cmd = SCons.Util.CLVar(['$LDMODULE'])) + +def _lib_emitter(target, source, env, **kw): + Verbose = False + + try: vp = kw['varprefix'] + except KeyError: vp = 'SHLIB' + + try: libtype = kw['libtype'] + except KeyError: libtype = 'ShLib' + + dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp) no_import_lib = env.get('no_import_lib', 0) if not dll or len(target) > 1: - raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) + raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$%sSUFFIX" % vp)) # Remove any "lib" after the prefix - pre = env.subst('$SHLIBPREFIX') + pre = env.subst('$%sPREFIX' % vp) if dll.name[len(pre):len(pre)+3] == 'lib': dll.name = pre + dll.name[len(pre)+3:] @@ -55,17 +81,90 @@ def shlib_emitter(target, source, env): if not no_import_lib: # Create list of target libraries as strings target_strings = env.ReplaceIxes(orig_target[0], - 'SHLIBPREFIX', 'SHLIBSUFFIX', + '%sPREFIX' % vp, '%sSUFFIX' % vp, 'IMPLIBPREFIX', 'IMPLIBSUFFIX') implib_target = env.fs.File(target_strings) + if Verbose: + print "_lib_emitter: implib_target=%r" % str(implib_target) implib_target.attributes.shared = 1 target.append(implib_target) + symlinks = SCons.Tool.ImpLibSymlinkGenerator(env, implib_target, + implib_libtype=libtype, + generator_libtype=libtype+'ImpLib') + if Verbose: + print "_lib_emitter: implib symlinks=%r" % symlinks + if symlinks: + SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target) + implib_target.attributes.shliblinks = symlinks + return (target, source) + +def shlib_emitter(target, source, env): + return _lib_emitter(target, source, env, varprefix='SHLIB', libtype='ShLib') + +def ldmod_emitter(target, source, env): + return _lib_emitter(target, source, env, varprefix='LDMODULE', libtype='LdMod') +def _versioned_lib_suffix(env, suffix, version): + """Generate versioned shared library suffix from a unversioned one. + If suffix='.dll', and version='0.1.2', then it returns '-0-1-2.dll'""" + Verbose = False + if Verbose: + print "_versioned_lib_suffix: suffix= ", suffix + print "_versioned_lib_suffix: version= ", version + cygversion = re.sub('\.', '-', version) + if not suffix.startswith('-' + cygversion): + suffix = '-' + cygversion + suffix + if Verbose: + print "_versioned_lib_suffix: return suffix= ", suffix + return suffix + +def _versioned_implib_name(env, libnode, version, suffix, **kw): + import link + generator = SCons.Tool.ImpLibSuffixGenerator + libtype = kw['libtype'] + return link._versioned_lib_name(env, libnode, version, suffix, generator, implib_libtype=libtype) + +def _versioned_implib_symlinks(env, libnode, version, suffix, **kw): + """Generate link names that should be created for a versioned shared lirbrary. + Returns a dictionary in the form { linkname : linktarget } + """ + Verbose = False + + if Verbose: + print "_versioned_implib_symlinks: str(libnode)=%r" % str(libnode) + print "_versioned_implib_symlinks: version=%r" % version + + try: libtype = kw['libtype'] + except KeyError: libtype = 'ShLib' + + symlinks = {} + + linkdir = os.path.dirname(str(libnode)) + if Verbose: + print "_versioned_implib_symlinks: linkdir=%r" % linkdir + + name = SCons.Tool.ImpLibNameGenerator(env, libnode, + implib_libtype=libtype, + generator_libtype=libtype+'ImpLib') + if Verbose: + print "_versioned_implib_symlinks: name=%r" % name + + major = version.split('.')[0] + + link0 = os.path.join(str(linkdir), name) + + symlinks[link0] = str(libnode) + + if Verbose: + print "_versioned_implib_symlinks: return symlinks=%r" % symlinks + + return symlinks shlib_action = SCons.Action.Action(shlib_generator, generator=1) +ldmod_action = SCons.Action.Action(ldmod_generator, generator=1) def generate(env): """Add Builders and construction variables for cyglink to an Environment.""" @@ -74,8 +173,9 @@ def generate(env): env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,-no-undefined') env['SHLINKCOM'] = shlib_action - env['LDMODULECOM'] = shlib_action + env['LDMODULECOM'] = ldmod_action env.Append(SHLIBEMITTER = [shlib_emitter]) + env.Append(LDMODULEEMITTER = [ldmod_emitter]) env['SHLIBPREFIX'] = 'cyg' env['SHLIBSUFFIX'] = '.dll' @@ -83,6 +183,34 @@ def generate(env): env['IMPLIBPREFIX'] = 'lib' env['IMPLIBSUFFIX'] = '.dll.a' + # Variables used by versioned shared libraries + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' + + # SHLIBVERSIONFLAGS and LDMODULEVERSIONFLAGS are same as in gnulink... + + env['GenerateVersionedShLibSuffix'] = _versioned_lib_suffix + env['GenerateVersionedLdModSuffix'] = _versioned_lib_suffix + env['GenerateVersionedImpLibSuffix'] = _versioned_lib_suffix + env['GenerateVersionedShLibImpLibName'] = lambda *args: _versioned_implib_name(*args, libtype='ShLib') + env['GenerateVersionedLdModImpLibName'] = lambda *args: _versioned_implib_name(*args, libtype='LdMod') + env['GenerateVersionedShLibImpLibSymlinks'] = lambda *args: _versioned_implib_symlinks(*args, libtype='ShLib') + env['GenerateVersionedLdModImpLibSymlinks'] = lambda *args: _versioned_implib_symlinks(*args, libtype='LdMod') + + def trydel(env, key): + try: del env[key] + except KeyError: pass + + # these variables were set by gnulink but are not used in cyglink + trydel(env,'_SHLINKSONAME') + trydel(env,'_LDMODULESONAME') + trydel(env,'ShLibSonameGenerator') + trydel(env,'LdModSonameGenerator') + trydel(env,'GenerateVersionedShLibSymlinks') + trydel(env,'GenerateVersionedLdModSymlinks') + trydel(env,'GenerateVersionedShLibSoname') + trydel(env,'GenerateVersionedLdModSoname') + def exists(env): return gnulink.exists(env) diff --git a/src/engine/SCons/Tool/cyglink.xml b/src/engine/SCons/Tool/cyglink.xml new file mode 100644 index 0000000..42208f1 --- /dev/null +++ b/src/engine/SCons/Tool/cyglink.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +__COPYRIGHT__ + +This file is processed by the bin/SConsDoc.py module. +See its __doc__ string for a discussion of the format. +--> + +<!DOCTYPE sconsdoc [ +<!ENTITY % scons SYSTEM '../../../../doc/scons.mod'> +%scons; +<!ENTITY % builders-mod SYSTEM '../../../../doc/generated/builders.mod'> +%builders-mod; +<!ENTITY % functions-mod SYSTEM '../../../../doc/generated/functions.mod'> +%functions-mod; +<!ENTITY % tools-mod SYSTEM '../../../../doc/generated/tools.mod'> +%tools-mod; +<!ENTITY % variables-mod SYSTEM '../../../../doc/generated/variables.mod'> +%variables-mod; +]> + +<sconsdoc xmlns="http://www.scons.org/dbxsd/v1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> + +<tool name="cyglink"> +<summary> +<para> +Set construction variables for cygwin linker/loader. +</para> +</summary> +<sets> +<item>IMPLIBPREFIX</item> +<item>IMPLIBSUFFIX</item> +<item>LDMODULEVERSIONFLAGS</item> +<item>LINKFLAGS</item> +<item>RPATHPREFIX</item> +<item>RPATHSUFFIX</item> +<item>SHLIBPREFIX</item> +<item>SHLIBSUFFIX</item> +<item>SHLIBVERSIONFLAGS</item> +<item>SHLINKCOM</item> +<item>SHLINKFLAGS</item> +<item>_LDMODULEVERSIONFLAGS</item> +<item>_SHLIBVERSIONFLAGS</item> +</sets> +</tool> + +</sconsdoc> diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 3dc8f51..ea0ca5a 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -34,9 +34,97 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Util +import SCons.Tool +import os +import sys +import re import link +def _versioned_lib_suffix(env, suffix, version): + """For suffix='.so' and version='0.1.2' it returns '.so.0.1.2'""" + Verbose = False + if Verbose: + print "_versioned_lib_suffix: suffix=%r" % suffix + print "_versioned_lib_suffix: version=%r" % version + if not suffix.endswith(version): + suffix = suffix + '.' + version + if Verbose: + print "_versioned_lib_suffix: return suffix=%r" % suffix + return suffix + +def _versioned_lib_soname(env, libnode, version, suffix, name_generator): + """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so.X'""" + Verbose = False + if Verbose: + print "_versioned_lib_soname: version=%r" % version + name = name_generator(env, libnode) + if Verbose: + print "_versioned_lib_soname: name=%r" % name + major = version.split('.')[0] + soname = name + '.' + major + if Verbose: + print "_versioned_lib_soname: soname=%r" % soname + return soname + +def _versioned_shlib_soname(env, libnode, version, suffix): + generator = SCons.Tool.ShLibNameGenerator + return _versioned_lib_soname(env, libnode, version, suffix, generator) + +def _versioned_ldmod_soname(env, libnode, version, suffix): + generator = SCons.Tool.LdModNameGenerator + return _versioned_lib_soname(env, libnode, version, suffix, generator) + +def _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, soname_generator): + """Generate link names that should be created for a versioned shared lirbrary. + Returns a dictionary in the form { linkname : linktarget } + """ + Verbose = False + + if Verbose: + print "_versioned_lib_symlinks: str(libnode)=%r" % str(libnode) + print "_versioned_lib_symlinks: version=%r" % version + + symlinks = {} + + if sys.platform.startswith('openbsd'): + # OpenBSD uses x.y shared library versioning numbering convention + # and doesn't use symlinks to backwards-compatible libraries + if Verbose: + print "_versioned_lib_symlinks: return symlinks=%r" % symlinks + return symlinks + + linkdir = os.path.dirname(str(libnode)) + if Verbose: + print "_versioned_lib_symlinks: linkdir=%r" % linkdir + + name = name_generator(env, libnode) + if Verbose: + print "_versioned_lib_symlinks: name=%r" % name + + soname = soname_generator(env, libnode) + + link0 = os.path.join(str(linkdir), soname) + link1 = os.path.join(str(linkdir), name) + + symlinks[link0] = str(libnode) + symlinks[link1] = link0 + + if Verbose: + print "_versioned_lib_symlinks: return symlinks=%r" % symlinks + + return symlinks + +def _versioned_shlib_symlinks(env, libnode, version, suffix): + name_generator = SCons.Tool.ShLibNameGenerator + soname_generator = SCons.Tool.ShLibSonameGenerator + return _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, soname_generator) + +def _versioned_ldmod_symlinks(env, libnode, version, suffix): + name_generator = SCons.Tool.LdModNameGenerator + soname_generator = SCons.Tool.LdModSonameGenerator + return _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, soname_generator) + def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" link.generate(env) @@ -49,6 +137,35 @@ def generate(env): env['RPATHPREFIX'] = '-Wl,-rpath=' env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + + # The $_SHLIBVERSIONFLAGS define extra commandline flags used when + # building VERSIONED shared libraries. It's always set, but used only + # when VERSIONED library is built (see __SHLIBVERSIONFLAGS). + if sys.platform.startswith('openbsd'): + # OpenBSD doesn't usually use SONAME for libraries + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' + else: + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLINKSONAME' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME' + env['SHLIBVERSIONFLAGS'] = SCons.Util.CLVar('-Wl,-Bsymbolic') + env['LDMODULEVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' + + # libfoo.so.X.Y.Z -> libfoo.so.X + env['_SHLINKSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' + env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}' + + env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator + env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator + + env['GenerateVersionedShLibSuffix'] = _versioned_lib_suffix + env['GenerateVersionedLdModSuffix'] = _versioned_lib_suffix + env['GenerateVersionedShLibSymlinks'] = _versioned_shlib_symlinks + env['GenerateVersionedLdModSymlinks'] = _versioned_ldmod_symlinks + env['GenerateVersionedShLibName'] = link._versioned_shlib_name + env['GenerateVersionedLdModName'] = link._versioned_ldmod_name + env['GenerateVersionedShLibSoname'] = _versioned_shlib_soname + env['GenerateVersionedLdModSoname'] = _versioned_shlib_soname def exists(env): # TODO: sync with link.smart_link() to choose a linker diff --git a/src/engine/SCons/Tool/gnulink.xml b/src/engine/SCons/Tool/gnulink.xml index 2a36de2..63ce0f4 100644 --- a/src/engine/SCons/Tool/gnulink.xml +++ b/src/engine/SCons/Tool/gnulink.xml @@ -33,6 +33,10 @@ Set construction variables for GNU linker/loader. <item>SHLINKFLAGS</item> <item>RPATHPREFIX</item> <item>RPATHSUFFIX</item> +<item>_LDMODULESONAME</item> +<item>_SHLINKSONAME</item> +<item>LDMODULEVERSIONFLAGS</item> +<item>SHLIBVERSIONFLAGS</item> </sets> </tool> diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 9f2e937..c5c2adb 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -38,6 +38,7 @@ import shutil import stat import SCons.Action +import SCons.Tool from SCons.Util import make_path_relative # @@ -141,98 +142,34 @@ def copyFuncVersionedLib(dest, source, env): shutil.copy2(source, dest) st = os.stat(source) os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - versionedLibLinks(dest, source, env) + installShlibLinks(dest, source, env) return 0 -def versionedLibVersion(dest, source, env): - """Check if dest is a version shared library name. Return version, libname, & install_dir if it is.""" - Verbose = False - platform = env.subst('$PLATFORM') - if not (platform == 'posix' or platform == 'darwin' or platform == 'sunos'): - return (None, None, None) - - if (hasattr(source[0], 'attributes') and - hasattr(source[0].attributes, 'shlibname')): - libname = source[0].attributes.shlibname - else: - libname = os.path.basename(str(dest)) +def listShlibLinksToInstall(dest, source, env): + install_links = {} install_dir = os.path.dirname(str(dest)) - shlib_suffix = env.subst('$SHLIBSUFFIX') - # See if the source name is a versioned shared library, get the version number - result = False - - version_re = re.compile("[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") - version_File = None - if platform == 'posix' or platform == 'sunos': - # handle unix names - versioned_re = re.compile(re.escape(shlib_suffix + '.') + "[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") - result = versioned_re.findall(libname) - if result: - version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1] - elif platform == 'darwin': - # handle OSX names - versioned_re = re.compile("\\.[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+" + re.escape(shlib_suffix) ) - result = versioned_re.findall(libname) - if result: - version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1] - - if Verbose: - print "install: version_File ", version_File - # result is False if we did not find a versioned shared library name, so return and empty list - if not result: - return (None, libname, install_dir) - - version = None - # get version number from the environment - try: - version = env.subst('$SHLIBVERSION') - except KeyError: - version = None - - if version != version_File: - #raise SCons.Errors.UserError("SHLIBVERSION '%s' does not match the version # '%s' in the filename" % (version, version_File) ) - print "SHLIBVERSION '%s' does not match the version # '%s' in the filename, proceeding based on file name" % (version, version_File) - version = version_File - return (version, libname, install_dir) - -def versionedLibLinks(dest, source, env): + source = env.arg2nodes(source) + for src in source: + links = getattr(getattr(src,'attributes',None), 'shliblinks', None) + if SCons.Util.is_Dict(links): + for linkname, linktgt in links.iteritems(): + linkname_base = os.path.basename(str(linkname)) + linktgt_base = os.path.basename(str(linktgt)) + install_linkname = os.path.join(install_dir, linkname_base) + install_linktgt = os.path.join(install_dir, linktgt_base) + install_links[install_linkname] = install_linktgt + return install_links + +def installShlibLinks(dest, source, env): """If we are installing a versioned shared library create the required links.""" Verbose = False - linknames = [] - version, libname, install_dir = versionedLibVersion(dest, source, env) - - if version != None: - # libname includes the version number if one was given - linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env) - if Verbose: - print "versionedLibLinks: linknames ",linknames - # Here we just need the file name w/o path as the target of the link - lib_ver = libname - # make symlink of adjacent names in linknames - for count in range(len(linknames)): - linkname = linknames[count] - fulllinkname = os.path.join(install_dir, linkname) - if Verbose: - print "full link name ",fulllinkname - if count > 0: - try: - os.remove(lastlinkname) - except: - pass - os.symlink(os.path.basename(fulllinkname),lastlinkname) - if Verbose: - print "versionedLibLinks: made sym link of %s -> %s" % (lastlinkname,os.path.basename(fulllinkname)) - lastlinkname = fulllinkname - # finish chain of sym links with link to the actual library - if len(linknames)>0: - try: - os.remove(lastlinkname) - except: - pass - os.symlink(lib_ver,lastlinkname) - if Verbose: - print "versionedLibLinks: made sym link of %s -> %s" % (lib_ver,lastlinkname) + + symlinks = listShlibLinksToInstall(dest, source, env) + if Verbose: + print 'installShlibLinks: symlinks=%r' % symlinks + if symlinks: + SCons.Tool.CreateLibSymlinks(env, symlinks) return def installFunc(target, source, env): @@ -306,22 +243,11 @@ def add_versioned_targets_to_INSTALLED_FILES(target, source, env): Verbose = False _INSTALLED_FILES.extend(target) if Verbose: - print "ver lib emitter ",repr(target) - - # see if we have a versioned shared library, if so generate side effects - version, libname, install_dir = versionedLibVersion(target[0], source, env) - if version != None: - # generate list of link names - linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env) - for linkname in linknames: - if Verbose: - print "make side effect of %s" % os.path.join(install_dir, linkname) - fulllinkname = os.path.join(install_dir, linkname) - env.SideEffect(fulllinkname,target[0]) - env.Clean(target[0],fulllinkname) - _INSTALLED_FILES.append(fulllinkname) - if Verbose: - print "installed list ", _INSTALLED_FILES + print "add_versioned_targets_to_INSTALLED_FILES: target=%r" % map(str, target) + + symlinks = listShlibLinksToInstall(target[0], source, env) + if symlinks: + SCons.Tool.EmitLibSymlinks(env, symlinks, target[0]) _UNIQUE_INSTALLED_FILES = None return (target, source) diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml index 0aa9384..6ae3e30 100644 --- a/src/engine/SCons/Tool/install.xml +++ b/src/engine/SCons/Tool/install.xml @@ -82,20 +82,13 @@ env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], <builder name="InstallVersionedLib"> <summary> <para> -Installs a versioned shared library. The &cv-link-SHLIBVERSION; -construction variable should be defined in the environment -to confirm the version number in the library name. -If &cv-link-SHLIBVERSION; is not defined a warning will be issued -and the name of the library will be parsed to derive the version. -The symlinks appropriate to the architecture will be generated. +Installs a versioned shared library. The symlinks appropriate to the +architecture will be generated based on symlinks of the source library. </para> <example_commands> env.InstallVersionedLib(target = '/usr/local/bin/foo', source = 'libxyz.1.5.2.so') -env.InstallVersionedLib(target = '/usr/local/bin/foo', - source = 'libxyz.1.5.2.so', - SHLIBVERSION='1.5.2') </example_commands> </summary> </builder> diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index a084bc4..33e50d9 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -33,9 +33,10 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import sys import re +import os -import SCons.Defaults import SCons.Tool import SCons.Util import SCons.Warnings @@ -72,97 +73,66 @@ def smart_link(source, target, env, for_signature): return '$CXX' return '$CC' -def shlib_emitter(target, source, env): +def _lib_emitter(target, source, env, **kw): Verbose = False - platform = env.subst('$PLATFORM') + if Verbose: + print "_lib_emitter: str(target[0])=%r" % str(target[0]) for tgt in target: tgt.attributes.shared = 1 + try: - # target[0] comes in as libtest.so. Add the version extensions - version = env.subst('$SHLIBVERSION') - if version: - version_names = shlib_emitter_names(target, source, env) - # mark the target with the shared libraries name, including - # the version number - target[0].attributes.shlibname = version_names[0] - shlib = env.File(version_names[0], directory=target[0].get_dir()) - target[0].attributes.shlibpath = shlib.get_internal_path() - for name in version_names[1:]: - env.SideEffect(name, shlib) - env.Clean(shlib, name) - if Verbose: - print "shlib_emitter: add side effect - ",name - env.Clean(shlib, target[0]) - return ([shlib], source) + symlink_generator = kw['symlink_generator'] except KeyError: - version = None + pass + else: + if Verbose: + print "_lib_emitter: symlink_generator=%r" % symlink_generator + symlinks = symlink_generator(env, target[0]) + if Verbose: + print "_lib_emitter: symlinks=%r" % symlinks + + if symlinks: + SCons.Tool.EmitLibSymlinks(env, symlinks, target[0]) + target[0].attributes.shliblinks = symlinks return (target, source) -def shlib_emitter_names(target, source, env): - """Return list of file names that are side effects for a versioned library build. The first name in the list is the new name for the target""" +def shlib_emitter(target, source, env): + return _lib_emitter(target, source, env, symlink_generator = SCons.Tool.ShLibSymlinkGenerator) + +def ldmod_emitter(target, source, env): + return _lib_emitter(target, source, env, symlink_generator = SCons.Tool.LdModSymlinkGenerator) + +# This is generic enough to be included here... +def _versioned_lib_name(env, libnode, version, suffix, suffix_generator, **kw): + """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so'""" Verbose = False - platform = env.subst('$PLATFORM') - version_names = [] - try: - # target[0] comes in as libtest.so. Add the version extensions - version = env.subst('$SHLIBVERSION') - if version.count(".") != 2: - # We need a version of the form x.y.z to proceed - raise ValueError - if version: - if platform == 'posix' or platform == 'sunos': - versionparts = version.split('.') - if hasattr(target[0].attributes, 'shlibname'): - name = target[0].attributes.shlibname - else: - name = target[0].name - # generate library name with the version number - version_name = name + '.' + version - if Verbose: - print "shlib_emitter_names: target is ", version_name - print "shlib_emitter_names: side effect: ", name - # add version_name to list of names to be a Side effect - version_names.append(version_name) - if Verbose: - print "shlib_emitter_names: versionparts ",versionparts - for ver in versionparts[0:-1]: - name = name + '.' + ver - if Verbose: - print "shlib_emitter_names: side effect: ", name - # add name to list of names to be a Side effect - version_names.append(name) - elif platform == 'darwin': - shlib_suffix = env.subst('$SHLIBSUFFIX') - if hasattr(target[0].attributes, 'shlibname'): - name = target[0].attributes.shlibname - else: - name = target[0].name - # generate library name with the version number - suffix_re = re.escape(shlib_suffix) - version_name = re.sub(suffix_re, '.' + version + shlib_suffix, name) - if Verbose: - print "shlib_emitter_names: target is ", version_name - print "shlib_emitter_names: side effect: ", name - # add version_name to list of names to be a Side effect - version_names.append(version_name) - elif platform == 'cygwin': - shlib_suffix = env.subst('$SHLIBSUFFIX') - if hasattr(target[0].attributes, 'shlibname'): - name = target[0].attributes.shlibname - else: - name = target[0].name - # generate library name with the version number - suffix_re = re.escape(shlib_suffix) - version_name = re.sub(suffix_re, '-' + re.sub('\.', '-', version) + shlib_suffix, name) - if Verbose: - print "shlib_emitter_names: target is ", version_name - print "shlib_emitter_names: side effect: ", name - # add version_name to list of names to be a Side effect - version_names.append(version_name) - except KeyError: - version = None - return version_names + if Verbose: + print "_versioned_lib_name: version=%r" % version + + versioned_name = os.path.basename(str(libnode)) + if Verbose: + print "_versioned_lib_name: versioned_name=%r" % versioned_name + + if Verbose: + print "_versioned_lib_name: suffix=%r" % suffix + + versioned_suffix = suffix_generator(env, **kw) + + versioned_suffix_re = re.escape(versioned_suffix) + '$' + name = re.sub(versioned_suffix_re, suffix, versioned_name) + if Verbose: + print "_versioned_lib_name: name=%r" % name + return name + +def _versioned_shlib_name(env, libnode, version, suffix, **kw): + generator = SCons.Tool.ShLibSuffixGenerator + return _versioned_lib_name(env, libnode, version, suffix, generator, **kw) + +def _versioned_ldmod_name(env, libnode, version, suffix, **kw): + generator = SCons.Tool.LdModSuffixGenerator + return _versioned_lib_name(env, libnode, version, suffix, generator, **kw) + def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" @@ -171,7 +141,7 @@ def generate(env): env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' # don't set up the emitter, cause AppendUnique will generate a list # starting with None :-( env.Append(SHLIBEMITTER = [shlib_emitter]) @@ -196,15 +166,13 @@ def generate(env): # setting them the same means that LoadableModule works everywhere. SCons.Tool.createLoadableModuleBuilder(env) env['LDMODULE'] = '$SHLINK' - # don't set up the emitter, cause AppendUnique will generate a list - # starting with None :-( - env.Append(LDMODULEEMITTER='$SHLIBEMITTER') + env.Append(LDMODULEEMITTER = [ldmod_emitter]) env['LDMODULEPREFIX'] = '$SHLIBPREFIX' env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' env['LDMODULEFLAGS'] = '$SHLINKFLAGS' - env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - - + env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__LDMODULEVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LDMODULEVERSION'] = '$SHLIBVERSION' + env['LDMODULENOVERSIONSYMLINKS'] = '$SHLIBNOVERSIONSYMLINKS' def exists(env): # This module isn't really a Tool on its own, it's common logic for diff --git a/src/engine/SCons/Tool/link.xml b/src/engine/SCons/Tool/link.xml index d58b9e2..52349da 100644 --- a/src/engine/SCons/Tool/link.xml +++ b/src/engine/SCons/Tool/link.xml @@ -41,11 +41,16 @@ Sets construction variables for generic POSIX linkers. <item>LIBLINKPREFIX</item> <item>LIBLINKSUFFIX</item> <item>SHLIBSUFFIX</item> +<item>__SHLIBVERSIONFLAGS</item> <item>LDMODULE</item> <item>LDMODULEPREFIX</item> <item>LDMODULESUFFIX</item> <item>LDMODULEFLAGS</item> <item>LDMODULECOM</item> +<item>LDMODULEVERSION</item> +<item>LDMODULENOVERSIONSYMLINKS</item> +<item>LDMODULEVERSIONFLAGS</item> +<item>__LDMODULEVERSIONFLAGS</item> </sets> <uses> <item>SHLINKCOMSTR</item> @@ -54,6 +59,105 @@ Sets construction variables for generic POSIX linkers. </uses> </tool> +<cvar name="__LDMODULEVERSIONFLAGS"> +<summary> +<para> +This construction variable automatically introduces &cv-link-_LDMODULEVERSIONFLAGS; +if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string. +</para> +</summary> +</cvar> + +<cvar name="__SHLIBVERSIONFLAGS"> +<summary> +<para> +This construction variable automatically introduces &cv-link-_SHLIBVERSIONFLAGS; +if &cv-link-SHLIBVERSION; is set. Othervise it evaluates to an empty string. +</para> +</summary> +</cvar> + +<cvar name="_LDMODULESONAME"> +<summary> +<para> +A macro that automatically generates loadable module's SONAME based on $TARGET, +$LDMODULEVERSION and $LDMODULESUFFIX. Used by &b-link-LoadableModule; builder +when the linker tool supports SONAME (e.g. &t-link-gnulink;). +</para> +</summary> +</cvar> + +<cvar name="_LDMODULEVERSIONFLAGS"> +<summary> +<para> +This macro automatically introduces extra flags to &cv-link-LDMODULECOM; when +building versioned &b-link-LoadableModule; (that is when +&cv-link-LDMODULEVERSION; is set). <literal>_LDMODULEVERSIONFLAGS</literal> +usually adds &cv-link-SHLIBVERSIONFLAGS; and some extra dynamically generated +options (such as <literal>-Wl,-soname=$_LDMODULESONAME</literal>). It is unused +by plain (unversioned) loadable modules. +</para> +</summary> +</cvar> + +<cvar name="_SHLIBVERSIONFLAGS"> +<summary> +<para> +This macro automatically introduces extra flags to &cv-link-SHLINKCOM; when +building versioned &b-link-SharedLibrary; (that is when &cv-link-SHLIBVERSION; +is set). <literal>_SHLIBVERSIONFLAGS</literal> usually adds &cv-link-SHLIBVERSIONFLAGS; +and some extra dynamically generated options (such as +<literal>-Wl,-soname=$_SHLINKSONAME</literal>. It is unused by "plain" +(unversioned) shared libraries. +</para> +</summary> +</cvar> + +<cvar name="_SHLINKSONAME"> +<summary> +<para> +A macro that automatically generates shared library's SONAME based on $TARGET, +$SHLIBVERSION and $SHLIBSUFFIX. Used by &b-link-SharedLibrary; builder when +the linker tool supports SONAME (e.g. &t-link-gnulink;). +</para> +</summary> +</cvar> + +<cvar name="IMPLIBPREFIX"> +<summary> +<para> +The prefix used for import library names. For example, cygwin uses import +libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries +(<literal>cygfoo.dll</literal>). The &t-link-cyglink; linker sets +&cv-link-IMPLIBPREFIX; to <literal>'lib'</literal> and &cv-link-SHLIBPREFIX; +to <literal>'cyg'</literal>. +</para> +</summary> +</cvar> + +<cvar name="IMPLIBSUFFIX"> +<summary> +<para> +The suffix used for import library names. For example, cygwin uses import +libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries +(<literal>cygfoo.dll</literal>). The &t-link-cyglink; linker sets +&cv-link-IMPLIBSUFFIX; to <literal>'.dll.a'</literal> and &cv-link-SHLIBSUFFIX; +to <literal>'.dll'</literal>. +</para> +</summary> +</cvar> + +<cvar name="IMPLIBNOVERSIONSYMLINKS"> +<summary> +<para> +Used to override &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; when +creating versioned import library for a shared library/loadable module. If not defined, +then &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; is used to determine +whether to disable symlink generation or not. +</para> +</summary> +</cvar> + <cvar name="LDMODULE"> <summary> <para> @@ -92,6 +196,15 @@ General user options passed to the linker for building loadable modules. </summary> </cvar> +<cvar name="LDMODULENOVERSIONSYMLINKS"> +<summary> +<para> +Instructs the &b-link-LoadableModule; builder to not automatically create symlinks +for versioned modules. Defaults to <literal>$SHLIBNOVERSIONSYMLINKS</literal> +</para> +</summary> +</cvar> + <cvar name="LDMODULEPREFIX"> <summary> <para> @@ -114,6 +227,16 @@ the same as $SHLIBSUFFIX. </summary> </cvar> +<cvar name="LDMODULEVERSIONFLAGS"> +<summary> +<para> +Extra flags added to &cv-link-LDMODULECOM; when building versioned +&b-link-LoadableModule;. These flags are only used when &cv-link-LDMODULEVERSION; is +set. +</para> +</summary> +</cvar> + <cvar name="LINK"> <summary> <para> @@ -169,6 +292,25 @@ for the variable that expands to library search path options. </summary> </cvar> +<cvar name="SHLIBNOVERSIONSYMLINKS"> +<summary> +<para> +Instructs the &b-link-SharedLibrary; builder to not create symlinks for versioned +shared libraries. +</para> +</summary> +</cvar> + +<cvar name="SHLIBVERSIONFLAGS"> +<summary> +<para> +Extra flags added to &cv-link-SHLINKCOM; when building versioned +&b-link-SharedLibrary;. These flags are only used when &cv-link-SHLIBVERSION; is +set. +</para> +</summary> +</cvar> + <cvar name="SHLINK"> <summary> <para> @@ -223,6 +365,18 @@ for the variable that expands to library search path options. </summary> </cvar> +<cvar name="SONAME"> +<summary> +<para> +Variable used to hard-code SONAME for versioned shared library/loadable module. +<example_commands> +env.SharedLibrary('test', 'test.c', SHLIBVERSION='0.1.2', SONAME='libtest.so.2') +</example_commands> +The variable is used, for example, by &t-link-gnulink; linker tool. +</para> +</summary> +</cvar> + <cvar name="STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"> <summary> <para> diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py index 9c58561..bd643f7 100644 --- a/src/engine/SCons/Tool/linkloc.py +++ b/src/engine/SCons/Tool/linkloc.py @@ -86,6 +86,7 @@ def generate(env): env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}' env['SHLIBEMITTER']= None + env['LDMODULEEMITTER']= None env['LINK'] = "linkloc" env['LINKFLAGS'] = SCons.Util.CLVar('') env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}' diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 601ec3b..948ebe5 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -146,6 +146,7 @@ def generate(env): env['SHLINKCOM'] = shlib_action env['LDMODULECOM'] = shlib_action env.Append(SHLIBEMITTER = [shlib_emitter]) + env.Append(LDMODULEEMITTER = [shlib_emitter]) env['AS'] = 'as' env['WIN32DEFPREFIX'] = '' diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 6606e10..1390c20 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -263,6 +263,7 @@ def generate(env): env['_SHLINK_SOURCES'] = windowsShlinkSources env['SHLINKCOM'] = compositeShLinkAction env.Append(SHLIBEMITTER = [windowsLibEmitter]) + env.Append(LDMODULEEMITTER = [windowsLibEmitter]) env['LINK'] = 'link' env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') env['_PDB'] = pdbGenerator diff --git a/src/engine/SCons/Tool/mwld.py b/src/engine/SCons/Tool/mwld.py index e762d55..2067660 100644 --- a/src/engine/SCons/Tool/mwld.py +++ b/src/engine/SCons/Tool/mwld.py @@ -56,6 +56,7 @@ def generate(env): env['SHLINKFLAGS'] = '$LINKFLAGS' env['SHLINKCOM'] = shlib_action env['SHLIBEMITTER']= shlib_emitter + env['LDMODULEEMITTER']= shlib_emitter def exists(env): diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 716c7d5..7bc0ef6 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -320,6 +320,7 @@ def generate(env): # correctly later by our emitter. env.AppendUnique(PROGEMITTER =[AutomocStatic], SHLIBEMITTER=[AutomocShared], + LDMODULEEMITTER=[AutomocShared], LIBEMITTER =[AutomocStatic], # Of course, we need to link against the qt libraries CPPPATH=["$QT_CPPPATH"], |