From 596b7aca20e286ecb45dade015ab9e89ac6aa791 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Thu, 3 Sep 2015 01:02:08 +0200 Subject: new versioned libraries - gnulink and cyglink for now --- src/engine/SCons/Defaults.py | 7 + src/engine/SCons/Platform/cygwin.py | 4 +- src/engine/SCons/Tool/__init__.py | 428 ++++++++++++++++++++++++------------ src/engine/SCons/Tool/__init__.xml | 37 +++- src/engine/SCons/Tool/cyglink.py | 148 ++++++++++++- src/engine/SCons/Tool/cyglink.xml | 49 +++++ src/engine/SCons/Tool/gnulink.py | 117 ++++++++++ src/engine/SCons/Tool/gnulink.xml | 4 + src/engine/SCons/Tool/install.py | 130 +++-------- src/engine/SCons/Tool/install.xml | 11 +- src/engine/SCons/Tool/link.py | 148 +++++-------- src/engine/SCons/Tool/link.xml | 154 +++++++++++++ src/engine/SCons/Tool/linkloc.py | 1 + src/engine/SCons/Tool/mingw.py | 1 + src/engine/SCons/Tool/mslink.py | 1 + src/engine/SCons/Tool/mwld.py | 1 + src/engine/SCons/Tool/qt.py | 1 + test/LINK/VersionedLib-j2.py | 135 ++++++++++++ test/LINK/VersionedLib-subdir.py | 149 +++++++++++++ test/LINK/VersionedLib.py | 22 +- 20 files changed, 1187 insertions(+), 361 deletions(-) create mode 100644 src/engine/SCons/Tool/cyglink.xml create mode 100644 test/LINK/VersionedLib-j2.py create mode 100644 test/LINK/VersionedLib-subdir.py 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. + + + +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. + + + + @@ -472,6 +483,21 @@ TODO + + + +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. + + + + @@ -492,11 +518,12 @@ TODO 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. 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 @@ + + + + +%scons; + +%builders-mod; + +%functions-mod; + +%tools-mod; + +%variables-mod; +]> + + + + + + +Set construction variables for cygwin linker/loader. + + + +IMPLIBPREFIX +IMPLIBSUFFIX +LDMODULEVERSIONFLAGS +LINKFLAGS +RPATHPREFIX +RPATHSUFFIX +SHLIBPREFIX +SHLIBSUFFIX +SHLIBVERSIONFLAGS +SHLINKCOM +SHLINKFLAGS +_LDMODULEVERSIONFLAGS +_SHLIBVERSIONFLAGS + + + + 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. SHLINKFLAGS RPATHPREFIX RPATHSUFFIX +_LDMODULESONAME +_SHLINKSONAME +LDMODULEVERSIONFLAGS +SHLIBVERSIONFLAGS 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'], -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. 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') 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. LIBLINKPREFIX LIBLINKSUFFIX SHLIBSUFFIX +__SHLIBVERSIONFLAGS LDMODULE LDMODULEPREFIX LDMODULESUFFIX LDMODULEFLAGS LDMODULECOM +LDMODULEVERSION +LDMODULENOVERSIONSYMLINKS +LDMODULEVERSIONFLAGS +__LDMODULEVERSIONFLAGS SHLINKCOMSTR @@ -54,6 +59,105 @@ Sets construction variables for generic POSIX linkers. + + + +This construction variable automatically introduces &cv-link-_LDMODULEVERSIONFLAGS; +if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string. + + + + + + + +This construction variable automatically introduces &cv-link-_SHLIBVERSIONFLAGS; +if &cv-link-SHLIBVERSION; is set. Othervise it evaluates to an empty string. + + + + + + + +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;). + + + + + + + +This macro automatically introduces extra flags to &cv-link-LDMODULECOM; when +building versioned &b-link-LoadableModule; (that is when +&cv-link-LDMODULEVERSION; is set). _LDMODULEVERSIONFLAGS +usually adds &cv-link-SHLIBVERSIONFLAGS; and some extra dynamically generated +options (such as -Wl,-soname=$_LDMODULESONAME). It is unused +by plain (unversioned) loadable modules. + + + + + + + +This macro automatically introduces extra flags to &cv-link-SHLINKCOM; when +building versioned &b-link-SharedLibrary; (that is when &cv-link-SHLIBVERSION; +is set). _SHLIBVERSIONFLAGS usually adds &cv-link-SHLIBVERSIONFLAGS; +and some extra dynamically generated options (such as +-Wl,-soname=$_SHLINKSONAME. It is unused by "plain" +(unversioned) shared libraries. + + + + + + + +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;). + + + + + + + +The prefix used for import library names. For example, cygwin uses import +libraries (libfoo.dll.a) in pair with dynamic libraries +(cygfoo.dll). The &t-link-cyglink; linker sets +&cv-link-IMPLIBPREFIX; to 'lib' and &cv-link-SHLIBPREFIX; +to 'cyg'. + + + + + + + +The suffix used for import library names. For example, cygwin uses import +libraries (libfoo.dll.a) in pair with dynamic libraries +(cygfoo.dll). The &t-link-cyglink; linker sets +&cv-link-IMPLIBSUFFIX; to '.dll.a' and &cv-link-SHLIBSUFFIX; +to '.dll'. + + + + + + + +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. + + + + @@ -92,6 +196,15 @@ General user options passed to the linker for building loadable modules. + + + +Instructs the &b-link-LoadableModule; builder to not automatically create symlinks +for versioned modules. Defaults to $SHLIBNOVERSIONSYMLINKS + + + + @@ -114,6 +227,16 @@ the same as $SHLIBSUFFIX. + + + +Extra flags added to &cv-link-LDMODULECOM; when building versioned +&b-link-LoadableModule;. These flags are only used when &cv-link-LDMODULEVERSION; is +set. + + + + @@ -169,6 +292,25 @@ for the variable that expands to library search path options. + + + +Instructs the &b-link-SharedLibrary; builder to not create symlinks for versioned +shared libraries. + + + + + + + +Extra flags added to &cv-link-SHLINKCOM; when building versioned +&b-link-SharedLibrary;. These flags are only used when &cv-link-SHLIBVERSION; is +set. + + + + @@ -223,6 +365,18 @@ for the variable that expands to library search path options. + + + +Variable used to hard-code SONAME for versioned shared library/loadable module. + +env.SharedLibrary('test', 'test.c', SHLIBVERSION='0.1.2', SONAME='libtest.so.2') + +The variable is used, for example, by &t-link-gnulink; linker tool. + + + + 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"], diff --git a/test/LINK/VersionedLib-j2.py b/test/LINK/VersionedLib-j2.py new file mode 100644 index 0000000..076a4dd --- /dev/null +++ b/test/LINK/VersionedLib-j2.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Ensure that SharedLibrary builder works with SHLIBVERSION and -j2. +This is regression test for: +http://article.gmane.org/gmane.comp.programming.tools.scons.user/27049 +""" + +import TestSCons +import os +import sys + +import SCons.Platform + +test = TestSCons.TestSCons() + +test.write('foo.c', """ +#if _WIN32 +__declspec(dllexport) +#endif +int foo() { return 0; } +""") + + +test.write('main.c', """ +#if _WIN32 +__declspec(dllimport) +#endif +int foo(); +int main() { return foo(); } +""") + +test.write('SConstruct', """ +env = Environment() +env.AppendUnique(LIBPATH = ['.']) +env.Program('main.c', LIBS = ['foo']) +env.SharedLibrary('foo', 'foo.c', SHLIBVERSION = '0.1.2') +""") + +test.run(arguments = ['-j 2', '--tree=all']) + +platform = SCons.Platform.platform_default() + +if platform == 'cygwin': + # PATH is used to search for *.dll librarier (cygfoo-0-2-1.dll in our case) + path = os.environ.get('PATH','') + if path: path = path + os.pathsep + path = path + test.workpath('.') + os.environ['PATH'] = path + +if os.name == 'posix': + os.environ['LD_LIBRARY_PATH'] = test.workpath('.') +if sys.platform.find('irix') != -1: + os.environ['LD_LIBRARYN32_PATH'] = test.workpath('.') + +test.run(program = test.workpath('main')) + +test.run(arguments = ['-c']) + +platform = SCons.Platform.platform_default() + +if platform == 'posix': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.so', + 'libfoo.so.0', + 'libfoo.so.0.1.2', + 'foo.os', + ] +elif platform == 'darwin': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.dylib', + 'libfoo.0.1.2.dylib', + 'foo.os', + ] +elif platform == 'cygwin': + # All (?) the files we expect will get created in the current directory + files = [ + 'cygfoo-0-1-2.dll', + 'libfoo-0-1-2.dll.a', + 'libfoo.dll.a', + 'foo.os', + ] +elif platform == 'win32': + # All (?) the files we expect will get created in the current directory + files = [ + 'foo.dll', + 'foo.lib', + 'foo.obj', + ] +else: + # All (?) the files we expect will get created in the current directory + files= [ + 'libfoo.so', + 'foo.os'] + +for f in files: + test.must_not_exist([ f]) + +test.must_exist(['main.c']) +test.must_exist(['foo.c']) +test.must_exist(['SConstruct']) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py new file mode 100644 index 0000000..5fae101 --- /dev/null +++ b/test/LINK/VersionedLib-subdir.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Ensure that SharedLibrary builder with SHLIBVERSION='0.1.2' can build its target +in a subdirectory containing .so.0.1.2 in name. + +This is regression test for issue mentioned in: +http://thread.gmane.org/gmane.comp.programming.tools.scons.user/27081 +""" + +import TestSCons +import os +import sys + +import SCons.Platform + +test = TestSCons.TestSCons() + +test.write('foo.c', """ +#if _WIN32 +__declspec(dllexport) +#endif +int foo() { return 0; } +""") + +test.write('main.c', """ +#if _WIN32 +__declspec(dllimport) +#endif +int foo(); +int main() +{ + return foo(); +} +""") + +platform = SCons.Platform.platform_default() + +if platform == 'darwin': + subdir = 'blah.0.1.2.dylib.blah' +elif platform == 'cygwin': + subdir = 'blah-0-1-2.dll.a.blah' +else: + subdir = 'blah.so.0.1.2.blah' + +test.write('SConstruct', """ +env = Environment() +env.AppendUnique(LIBPATH = [ '%s' ]) +env.SharedLibrary('%s/foo', 'foo.c', SHLIBVERSION = '0.1.2') +env.Program('main.c', LIBS=['foo']) +""" % (subdir,subdir)) + +test.run(arguments = ['--tree=all']) + +if platform == 'cygwin': + # PATH is used to search for *.dll librarier (cygfoo-0-2-1.dll in our case) + path = os.environ.get('PATH','') + if path: path = path + os.pathsep + path = path + test.workpath(subdir) + os.environ['PATH'] = path + +if os.name == 'posix': + os.environ['LD_LIBRARY_PATH'] = subdir +if sys.platform.find('irix') != -1: + os.environ['LD_LIBRARYN32_PATH'] = subdir + +test.run(program = test.workpath('main')) + +if platform == 'posix': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.so', + 'libfoo.so.0', + 'libfoo.so.0.1.2', + ] + obj = 'foo.os' +elif platform == 'darwin': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.dylib', + 'libfoo.0.1.2.dylib', + ] + obj = 'foo.os' +elif platform == 'cygwin': + # All (?) the files we expect will get created in the current directory + files = [ + 'cygfoo-0-1-2.dll', + 'libfoo-0-1-2.dll.a', + 'libfoo.dll.a', + ] + obj = 'foo.os' +elif platform == 'win32': + # All (?) the files we expect will get created in the current directory + files = [ + 'foo.dll', + 'foo.lib', + ] + obj = 'foo.obj' +else: + # All (?) the files we expect will get created in the current directory + files= [ + 'libfoo.so', + ] + obj = 'foo.os' + +test.must_exist([ obj ]) +for f in files: + test.must_exist([ subdir, f ]) + +test.run(arguments = ['-c']) + +test.must_not_exist([ obj ]) +for f in files: + test.must_not_exist([ subdir, f ]) + +test.must_exist(['foo.c']) +test.must_exist(['SConstruct']) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py index a2345d6..c68dd55 100644 --- a/test/LINK/VersionedLib.py +++ b/test/LINK/VersionedLib.py @@ -39,8 +39,8 @@ import os env = Environment() objs = env.SharedObject('test.c') mylib = env.SharedLibrary('test', objs, SHLIBVERSION = '2.5.4') -env.Program(source=['testapp.c',mylib]) -env.Program(target=['testapp2'],source=['testapp.c','libtest.dylib']) +env.Program('testapp1.c', LIBS = mylib, LIBPATH='.') +env.Program('testapp2.c', LIBS = ['test'], LIBPATH='.') instnode = env.InstallVersionedLib("#/installtest",mylib) env.Default(instnode) """) @@ -55,22 +55,28 @@ return n+1 ; } """) -test.write('testapp.c', """\ +testapp_src = """\ +#if _WIN32 +__declspec(dllimport) +#endif +int testlib(int n); #include int main(int argc, char **argv) { int itest ; itest = testlib(2) ; -printf("results: testlib(2) = %d\n",itest) ; +printf("results: testlib(2) = %d\\n",itest) ; return 0 ; } -""") +""" +test.write('testapp1.c', testapp_src) +test.write('testapp2.c', testapp_src) platform = SCons.Platform.platform_default() -test.run() +test.run(arguments = ['--tree=all']) if platform == 'posix': # All (?) the files we expect will get created in the current directory @@ -103,12 +109,14 @@ elif platform == 'cygwin': files = [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', + 'libtest.dll.a', 'test.os', ] # All (?) the files we expect will get created in the 'installtest' directory instfiles = [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', + 'libtest.dll.a', ] elif platform == 'win32': # All (?) the files we expect will get created in the current directory @@ -148,7 +156,7 @@ return n+11 ; test.run() -test.run(arguments = '-c') +test.run(arguments = ['-c']) for f in files: test.must_not_exist([ f]) -- cgit v0.12 From 97a7990e29435b7fe752ce220aac9a6048393395 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Fri, 4 Sep 2015 03:34:59 +0200 Subject: reivised code, fixed cygwin/variant-dir issue, cleaned-up some areas --- src/engine/SCons/Tool/__init__.py | 67 +++++++++------ src/engine/SCons/Tool/cyglink.py | 32 ++++--- src/engine/SCons/Tool/gnulink.py | 19 ++--- src/engine/SCons/Tool/install.py | 23 ++--- test/LINK/VersionedLib-VariantDir.py | 157 +++++++++++++++++++++++++++++++++++ test/LINK/VersionedLib-subdir.py | 4 +- 6 files changed, 242 insertions(+), 60 deletions(-) create mode 100644 test/LINK/VersionedLib-VariantDir.py diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 4a27c0d..0e6e7d8 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -367,7 +367,7 @@ LdModSuffixGenerator = _LibSuffixGenerator('LdMod') ImpLibSuffixGenerator = _LibSuffixGenerator('ImpLib') class _LibSymlinkGenerator(_LibInfoGeneratorBase): - """Library symlink map generator. It generates a dict of symlinks that + """Library symlink map generator. It generates a list of symlinks that should be created by SharedLibrary or LoadableModule builders""" def __init__(self, libtype): super(_LibSymlinkGenerator, self).__init__(libtype, 'Symlinks') @@ -397,7 +397,7 @@ class _LibSymlinkGenerator(_LibInfoGeneratorBase): Verbose = False if Verbose: - print "_LibSymLinkGenerator: str(libnode)=%r" % str(libnode) + print "_LibSymLinkGenerator: libnode=%r" % libnode.get_path() symlinks = None @@ -412,7 +412,7 @@ class _LibSymlinkGenerator(_LibInfoGeneratorBase): symlinks = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) if Verbose: - print '_LibSymlinkGenerator: return symlinks=%r' % symlinks + print '_LibSymlinkGenerator: return symlinks=%r' % StringizeLibSymlinks(symlinks) return symlinks ShLibSymlinkGenerator = _LibSymlinkGenerator('ShLib') @@ -430,7 +430,7 @@ class _LibNameGenerator(_LibInfoGeneratorBase): Verbose = False if Verbose: - print "_LibNameGenerator: str(libnode)=%r" % str(libnode) + print "_LibNameGenerator: libnode=%r" % libnode.get_path() version = self.get_lib_version(env, **kw) if Verbose: @@ -442,7 +442,7 @@ class _LibNameGenerator(_LibInfoGeneratorBase): name = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) if not name: - name = os.path.basename(str(libnode)) + name = os.path.basename(libnode.get_path()) if Verbose: print '_LibNameGenerator: return name=%r' % name @@ -464,7 +464,7 @@ class _LibSonameGenerator(_LibInfoGeneratorBase): Verbose = False if Verbose: - print "_LibSonameGenerator: str(libnode)=%r" % str(libnode) + print "_LibSonameGenerator: libnode=%r" % libnode.get_path() soname = env.subst('$SONAME') if not soname: @@ -489,41 +489,56 @@ class _LibSonameGenerator(_LibInfoGeneratorBase): ShLibSonameGenerator = _LibSonameGenerator('ShLib') LdModSonameGenerator = _LibSonameGenerator('LdMod') +def StringizeLibSymlinks(symlinks): + """Converts list with pairs of nodes to list with pairs of node paths + (strings). Used mainly for debugging.""" + if SCons.Util.is_List(symlinks): + try: + return [ (k.get_path(), v.get_path()) for k,v in symlinks ] + except TypeError: + return symlinks + except ValueError: + return symlinks + else: + return symlinks + 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) + + # nodes involved in process... all symlinks + library + nodes = list(set([ x for x,y in symlinks ] + [libnode])) + + for link, linktgt in symlinks: + env.SideEffect(link, linktgt) if(Verbose): - print "EmitLibSymlinks: SideEffect(", linkname, ", ", linktgt, ")" - clean = list(set(filter(lambda x : x != linktgt, symlinks.keys() + [str(libnode)]))) - env.Clean(linktgt, clean) + print "EmitLibSymlinks: SideEffect(%r,%r)" % (link.get_path(), linktgt.get_path()) + clean_list = filter(lambda x : x != linktgt, nodes) + env.Clean(linktgt, clean_list) if(Verbose): - print "EmitLibSymlinks: Clean(%r,%r)" % (linktgt, clean) + print "EmitLibSymlinks: Clean(%r,%r)" % (linktgt.get_path(), map(lambda x : x.get_path(), clean_list)) def CreateLibSymlinks(env, symlinks): - """Physically creates symlinks. The symlinks argument must be a dict in - form { linkname : linktarget } + """Physically creates symlinks. The symlinks argument must be a list in + form [ (link, linktarget), ... ], where link and linktarget are SCons + nodes. """ 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) + for link, linktgt in symlinks: + linktgt = link.get_dir().rel_path(linktgt) + link = link.get_path() if(Verbose): - print "CreateLibSymlinks: preparing to add symlink ", linkname, " -> ", linktgt + print "CreateLibSymlinks: preparing to add symlink %r -> %r" % (link, linktgt) try: - os.remove(linkname) + os.remove(link) + if(Verbose): + print "CreateLibSymlinks: removed old file %r" % link except: pass - os.symlink(linktgt, linkname) + os.symlink(linktgt, link) if(Verbose): - print "CreateLibSymlinks: add symlink ", linkname, " -> ", linktgt + print "CreateLibSymlinks: add symlink %r -> %r" % (link, linktgt) return 0 def LibSymlinksActionFunction(target, source, env): diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index 4033978..59258b2 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -56,6 +56,9 @@ def ldmod_generator(target, source, env, for_signature): def _lib_emitter(target, source, env, **kw): Verbose = False + if Verbose: + print "_lib_emitter: target[0]=%r" % target[0].get_path() + try: vp = kw['varprefix'] except KeyError: vp = 'SHLIB' @@ -65,6 +68,9 @@ def _lib_emitter(target, source, env, **kw): dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp) no_import_lib = env.get('no_import_lib', 0) + if Verbose: + print "_lib_emitter: dll=%r" % dll.get_path() + 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("$%sSUFFIX" % vp)) @@ -73,20 +79,28 @@ def _lib_emitter(target, source, env, **kw): if dll.name[len(pre):len(pre)+3] == 'lib': dll.name = pre + dll.name[len(pre)+3:] + if Verbose: + print "_lib_emitter: dll.name=%r" % dll.name + orig_target = target target = [env.fs.File(dll)] target[0].attributes.shared = 1 + if Verbose: + print "_lib_emitter: after target=[env.fs.File(dll)]: target[0]=%r" % target[0].get_path() + # Append an import lib target if not no_import_lib: # Create list of target libraries as strings target_strings = env.ReplaceIxes(orig_target[0], '%sPREFIX' % vp, '%sSUFFIX' % vp, 'IMPLIBPREFIX', 'IMPLIBSUFFIX') + if Verbose: + print "_lib_emitter: target_strings=%r" % target_strings implib_target = env.fs.File(target_strings) if Verbose: - print "_lib_emitter: implib_target=%r" % str(implib_target) + print "_lib_emitter: implib_target=%r" % implib_target.get_path() implib_target.attributes.shared = 1 target.append(implib_target) @@ -94,7 +108,7 @@ def _lib_emitter(target, source, env, **kw): implib_libtype=libtype, generator_libtype=libtype+'ImpLib') if Verbose: - print "_lib_emitter: implib symlinks=%r" % symlinks + print "_lib_emitter: implib symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) if symlinks: SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target) implib_target.attributes.shliblinks = symlinks @@ -129,20 +143,19 @@ def _versioned_implib_name(env, libnode, version, suffix, **kw): 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 } + Returns a list in the form [ (link, linktarget), ... ] """ Verbose = False if Verbose: - print "_versioned_implib_symlinks: str(libnode)=%r" % str(libnode) + print "_versioned_implib_symlinks: libnode=%r" % libnode.get_path() print "_versioned_implib_symlinks: version=%r" % version try: libtype = kw['libtype'] except KeyError: libtype = 'ShLib' - symlinks = {} - linkdir = os.path.dirname(str(libnode)) + linkdir = os.path.dirname(libnode.get_path()) if Verbose: print "_versioned_implib_symlinks: linkdir=%r" % linkdir @@ -154,12 +167,11 @@ def _versioned_implib_symlinks(env, libnode, version, suffix, **kw): major = version.split('.')[0] - link0 = os.path.join(str(linkdir), name) - - symlinks[link0] = str(libnode) + link0 = env.fs.File(os.path.join(linkdir, name)) + symlinks = [(link0, libnode)] if Verbose: - print "_versioned_implib_symlinks: return symlinks=%r" % symlinks + print "_versioned_implib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) return symlinks diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index ea0ca5a..14007af 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -85,18 +85,16 @@ def _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, sonam 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 + print "_versioned_lib_symlinks: return symlinks=%r" % None + return None - linkdir = os.path.dirname(str(libnode)) + linkdir = libnode.get_dir() if Verbose: - print "_versioned_lib_symlinks: linkdir=%r" % linkdir + print "_versioned_lib_symlinks: linkdir=%r" % linkdir.get_path() name = name_generator(env, libnode) if Verbose: @@ -104,14 +102,13 @@ def _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, sonam soname = soname_generator(env, libnode) - link0 = os.path.join(str(linkdir), soname) - link1 = os.path.join(str(linkdir), name) + link0 = env.fs.File(soname, linkdir) + link1 = env.fs.File(name, linkdir) - symlinks[link0] = str(libnode) - symlinks[link1] = link0 + symlinks = [ (link0, libnode), (link1, link0) ] if Verbose: - print "_versioned_lib_symlinks: return symlinks=%r" % symlinks + print "_versioned_lib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) return symlinks diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index c5c2adb..9d5db9f 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -147,18 +147,19 @@ def copyFuncVersionedLib(dest, source, env): return 0 def listShlibLinksToInstall(dest, source, env): - install_links = {} - install_dir = os.path.dirname(str(dest)) + install_links = [] source = env.arg2nodes(source) + dest = env.fs.File(dest) + install_dir = dest.get_dir() 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 + symlinks = getattr(getattr(src,'attributes',None), 'shliblinks', None) + if symlinks: + for link, linktgt in symlinks: + link_base = os.path.basename(link.get_path()) + linktgt_base = os.path.basename(linktgt.get_path()) + install_link = env.fs.File(link_base, install_dir) + install_linktgt = env.fs.File(linktgt_base, install_dir) + install_links.append((install_link, install_linktgt)) return install_links def installShlibLinks(dest, source, env): @@ -167,7 +168,7 @@ def installShlibLinks(dest, source, env): symlinks = listShlibLinksToInstall(dest, source, env) if Verbose: - print 'installShlibLinks: symlinks=%r' % symlinks + print 'installShlibLinks: symlinks=%r' % SCons.Tool.StringizeLibSymlinks(symlinks) if symlinks: SCons.Tool.CreateLibSymlinks(env, symlinks) return diff --git a/test/LINK/VersionedLib-VariantDir.py b/test/LINK/VersionedLib-VariantDir.py new file mode 100644 index 0000000..a3ea660 --- /dev/null +++ b/test/LINK/VersionedLib-VariantDir.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Ensure that SharedLibrary builder with SHLIBVERSION set works with VariantDir. +""" + +import TestSCons +import os +import sys + +import SCons.Platform + +platform = SCons.Platform.platform_default() + +test = TestSCons.TestSCons() + +test.subdir(['src']) +test.subdir(['src','lib']) +test.subdir(['src','bin']) + +test.write(['src','lib','foo.c'], """ +#if _WIN32 +__declspec(dllexport) +#endif +int foo() { return 0; } +""") + +test.write(['src','bin','main.c'], """ +#if _WIN32 +__declspec(dllimport) +#endif +int foo(); +int main() +{ + return foo(); +} +""") + +test.write('SConstruct', """ +env = Environment() +variant = { 'variant_dir' : 'build', + 'src_dir' : 'src', + 'duplicate' : 0, + 'exports' : { 'env' : env } } +SConscript('src/lib/SConscript', **variant) +SConscript('src/bin/SConscript', **variant) +""") + +test.write(['src','lib','SConscript'], """ +Import('env') +env.SharedLibrary('foo', 'foo.c', SHLIBVERSION = '0.1.2') +""" ) + +test.write(['src','bin','SConscript'], """ +Import('env') +env.Program('main.c', LIBS=['foo'], LIBPATH=['../lib']) +""") + +test.run(arguments = ['--tree=all']) + +if platform == 'cygwin' or platform == 'win32': + # PATH is used to search for *.dll libraries on windows + path = os.environ.get('PATH','') + if path: path = path + os.pathsep + path = path + test.workpath('build/lib') + os.environ['PATH'] = path + +if os.name == 'posix': + os.environ['LD_LIBRARY_PATH'] = test.workpath('build/lib') +if sys.platform.find('irix') != -1: + os.environ['LD_LIBRARYN32_PATH'] = test.workpath('build/lib') + +test.run(program = test.workpath('build/bin/main')) + +if platform == 'posix': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.so', + 'libfoo.so.0', + 'libfoo.so.0.1.2', + ] + obj = 'foo.os' +elif platform == 'darwin': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.dylib', + 'libfoo.0.1.2.dylib', + ] + obj = 'foo.os' +elif platform == 'cygwin': + # All (?) the files we expect will get created in the current directory + files = [ + 'cygfoo-0-1-2.dll', + 'libfoo-0-1-2.dll.a', + 'libfoo.dll.a', + ] + obj = 'foo.os' +elif platform == 'win32': + # All (?) the files we expect will get created in the current directory + files = [ + 'foo.dll', + 'foo.lib', + ] + obj = 'foo.obj' +else: + # All (?) the files we expect will get created in the current directory + files= [ + 'libfoo.so', + ] + obj = 'foo.os' + +test.must_exist([ 'build', 'lib', obj ]) +for f in files: + test.must_exist([ 'build', 'lib', f ]) + +test.run(arguments = ['-c']) + +test.must_not_exist([ 'build', 'lib', obj ]) +for f in files: + test.must_not_exist([ 'build', 'lib', f ]) + +test.must_exist(['src', 'lib', 'foo.c']) +test.must_exist(['SConstruct']) +test.must_exist(['src', 'lib', 'SConscript']) +test.must_exist(['src', 'bin', 'SConscript']) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py index 5fae101..6facd41 100644 --- a/test/LINK/VersionedLib-subdir.py +++ b/test/LINK/VersionedLib-subdir.py @@ -76,8 +76,8 @@ env.Program('main.c', LIBS=['foo']) test.run(arguments = ['--tree=all']) -if platform == 'cygwin': - # PATH is used to search for *.dll librarier (cygfoo-0-2-1.dll in our case) +if platform == 'cygwin' or platform == 'win32': + # PATH is used to search for *.dll libraries on windows path = os.environ.get('PATH','') if path: path = path + os.pathsep path = path + test.workpath(subdir) -- cgit v0.12 From d124e8dd69f7c73e7ce82d55abf5f69e12379098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 4 Sep 2015 23:37:17 +0200 Subject: refactored the versioned lib code a little --- src/engine/SCons/Tool/__init__.py | 98 ++++++++++++++++++++++++++++++--------- src/engine/SCons/Tool/cyglink.py | 48 +++++++++---------- src/engine/SCons/Tool/gnulink.py | 56 +++++++++++----------- src/engine/SCons/Tool/link.py | 35 ++++++++------ 4 files changed, 148 insertions(+), 89 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 0e6e7d8..0b09a13 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -236,31 +236,31 @@ def createStaticLibBuilder(env): return static_lib -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""" +def _call_linker_cb(env, callback, args, result = None): + """Returns the result of env['LINKCALLBACKS'][callback](*args) + if env['LINKCALLBACKS'] is a dictionary and env['LINKCALLBACKS'][callback] + is callable. If these conditions are not meet, 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 - + print '_call_linker_cb: args=%r' % args + print '_call_linker_cb: callback=%r' % callback + try: - cbfun = env[callback] - except KeyError: + cbfun = env['LINKCALLBACKS'][callback] + except (KeyError, TypeError): pass else: if Verbose: - print '_call_env_cb: env[%r] found' % callback - print '_call_env_cb: env[%r]=%r' % (callback, cbfun) + print '_call_linker_cb: env[%r] found' % callback + print '_call_linker_cb: env[%r]=%r' % (callback, cbfun) if(callable(cbfun)): if Verbose: - print '_call_env_cb: env[%r] is callable' % callback + print '_call_linker_cb: env[%r] is callable' % callback result = cbfun(env, *args) return result @@ -298,6 +298,17 @@ class _LibInfoGeneratorBase(object): prefix = env.subst('$IMPLIBPREFIX') return prefix + 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() @@ -332,11 +343,39 @@ class _LibInfoGeneratorBase(object): try: libtype = kw['generator_libtype'] except KeyError: libtype = self.get_libtype() infoname = self.get_infoname() - return 'GenerateVersioned%s%s' % (libtype, infoname) + return 'Versioned%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) + return _call_linker_cb(env, callback, args, result) + +class _LibPrefixGenerator(_LibInfoGeneratorBase): + """Library prefix generator, used as target_prefix in SharedLibrary and + LoadableModule builders""" + def __init__(self, libtype): + super(_LibPrefixGenerator, self).__init__(libtype, 'Prefix') + + def __call__(self, env, sources = None, **kw): + Verbose = False + + prefix = self.get_lib_prefix(env) + if Verbose: + print "_LibPrefixGenerator: input prefix=%r" % prefix + + version = self.get_lib_version(env, **kw) + if Verbose: + print "_LibPrefixGenerator: version=%r" % version + + if version: + prefix = self.generate_versioned_lib_info(env, [prefix, version], prefix, **kw) + + if Verbose: + print "_LibPrefixGenerator: return prefix=%r" % prefix + return prefix + +ShLibPrefixGenerator = _LibPrefixGenerator('ShLib') +LdModPrefixGenerator = _LibPrefixGenerator('LdMod') +ImpLibPrefixGenerator = _LibPrefixGenerator('ImpLib') class _LibSuffixGenerator(_LibInfoGeneratorBase): """Library suffix generator, used as target_suffix in SharedLibrary and @@ -408,8 +447,9 @@ class _LibSymlinkGenerator(_LibInfoGeneratorBase): print '_LibSymlinkGenerator: disable=%r' % disable if version and not disable: + prefix = self.get_lib_prefix(env) suffix = self.get_lib_suffix(env) - symlinks = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) + symlinks = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw) if Verbose: print '_LibSymlinkGenerator: return symlinks=%r' % StringizeLibSymlinks(symlinks) @@ -420,8 +460,19 @@ 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)""" + """Generates "unmangled" library name from a library file node. + + Generally, it's thought to revert modifications done by prefix/suffix + generators (_LibPrefixGenerator/_LibSuffixGenerator) used by a library + builder. For example, on gnulink the suffix generator used by SharedLibrary + builder appends $SHLIBVERSION to $SHLIBSUFFIX producing node name which + ends with "$SHLIBSUFFIX.$SHLIBVERSION". Correspondingly, the implementation + of _LibNameGenerator replaces "$SHLIBSUFFIX.$SHLIBVERSION" with + "$SHLIBSUFFIX" in the node's basename. So that, if $SHLIBSUFFIX is ".so", + $SHLIBVERSION is "0.1.2" and the node path is "/foo/bar/libfoo.so.0.1.2", + the _LibNameGenerator shall return "libfoo.so". Other link tools may + implement it's own way of library name unmangling. + """ def __init__(self, libtype): super(_LibNameGenerator, self).__init__(libtype, 'Name') @@ -438,8 +489,9 @@ class _LibNameGenerator(_LibInfoGeneratorBase): name = None if version: + prefix = self.get_lib_prefix(env) suffix = self.get_lib_suffix(env) - name = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) + name = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw) if not name: name = os.path.basename(libnode.get_path()) @@ -566,7 +618,7 @@ def createSharedLibBuilder(env): LibSymlinksAction ] shared_lib = SCons.Builder.Builder(action = action_list, emitter = "$SHLIBEMITTER", - prefix = '$SHLIBPREFIX', + prefix = ShLibPrefixGenerator, suffix = ShLibSuffixGenerator, target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', @@ -591,7 +643,7 @@ def createLoadableModuleBuilder(env): LibSymlinksAction ] ld_module = SCons.Builder.Builder(action = action_list, emitter = "$LDMODULEEMITTER", - prefix = '$LDMODULEPREFIX', + prefix = ShLibPrefixGenerator, suffix = LdModSuffixGenerator, target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index 59258b2..e05e85f 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -15,6 +15,7 @@ import SCons.Util import SCons.Tool import gnulink +import link def _lib_generator(target, source, env, for_signature, **kw): try: cmd = kw['cmd'] @@ -135,13 +136,13 @@ def _versioned_lib_suffix(env, suffix, version): 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_name(env, libnode, version, prefix, suffix, **kw): + return link._versioned_lib_name(env, libnode, version, prefix, suffix, + SCons.Tool.ImpLibPrefixGenerator, + SCons.Tool.ImpLibSuffixGenerator, + implib_libtype=kw['libtype']) -def _versioned_implib_symlinks(env, libnode, version, suffix, **kw): +def _versioned_implib_symlinks(env, libnode, version, prefix, suffix, **kw): """Generate link names that should be created for a versioned shared lirbrary. Returns a list in the form [ (link, linktarget), ... ] """ @@ -201,27 +202,24 @@ def generate(env): # 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 + # LINKCALLBACKS are NOT inherited from gnulink + env['LINKCALLBACKS'] = { + 'VersionedShLibSuffix' : _versioned_lib_suffix, + 'VersionedLdModSuffix' : _versioned_lib_suffix, + 'VersionedImpLibSuffix' : _versioned_lib_suffix, + 'VersionedShLibName' : link._versioned_shlib_name, + 'VersionedLdModName' : link._versioned_ldmod_name, + 'VersionedShLibImpLibName' : lambda *args: _versioned_implib_name(*args, libtype='ShLib'), + 'VersionedLdModImpLibName' : lambda *args: _versioned_implib_name(*args, libtype='LdMod'), + 'VersionedShLibImpLibSymlinks' : lambda *args: _versioned_implib_symlinks(*args, libtype='ShLib'), + 'VersionedLdModImpLibSymlinks' : lambda *args: _versioned_implib_symlinks(*args, libtype='LdMod'), + } # 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') + try: del env['_SHLINKSONAME'] + except KeyError: pass + try: del env['_LDMODULESONAME'] + except KeyError: pass def exists(env): return gnulink.exists(env) diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 14007af..e5e8818 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -53,12 +53,12 @@ def _versioned_lib_suffix(env, suffix, version): print "_versioned_lib_suffix: return suffix=%r" % suffix return suffix -def _versioned_lib_soname(env, libnode, version, suffix, name_generator): +def _versioned_lib_soname(env, libnode, version, prefix, suffix, name_func): """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) + name = name_func(env, libnode, version, prefix, suffix) if Verbose: print "_versioned_lib_soname: name=%r" % name major = version.split('.')[0] @@ -67,22 +67,20 @@ def _versioned_lib_soname(env, libnode, version, suffix, name_generator): 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_shlib_soname(env, libnode, version, prefix, suffix): + return _versioned_lib_soname(env, libnode, version, prefix, suffix, link._versioned_shlib_name) -def _versioned_ldmod_soname(env, libnode, version, suffix): - generator = SCons.Tool.LdModNameGenerator - return _versioned_lib_soname(env, libnode, version, suffix, generator) +def _versioned_ldmod_soname(env, libnode, version, prefix, suffix): + return _versioned_lib_soname(env, libnode, version, prefix, suffix, link._versioned_ldmod_name) -def _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, soname_generator): +def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, soname_func): """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: libnode=%r" % libnode.get_path() print "_versioned_lib_symlinks: version=%r" % version if sys.platform.startswith('openbsd'): @@ -96,11 +94,11 @@ def _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, sonam if Verbose: print "_versioned_lib_symlinks: linkdir=%r" % linkdir.get_path() - name = name_generator(env, libnode) + name = name_func(env, libnode, version, prefix, suffix) if Verbose: print "_versioned_lib_symlinks: name=%r" % name - soname = soname_generator(env, libnode) + soname = soname_func(env, libnode, version, prefix, suffix) link0 = env.fs.File(soname, linkdir) link1 = env.fs.File(name, linkdir) @@ -112,15 +110,15 @@ def _versioned_lib_symlinks(env, libnode, version, suffix, name_generator, sonam 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_shlib_symlinks(env, libnode, version, prefix, suffix): + nf = link._versioned_shlib_name + sf = _versioned_shlib_soname + return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) -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 _versioned_ldmod_symlinks(env, libnode, version, prefix, suffix): + nf = link._versioned_ldmod_name + sf = _versioned_ldmod_soname + return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" @@ -155,14 +153,16 @@ def generate(env): 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 + env['LINKCALLBACKS'] = { + 'VersionedShLibSuffix' : _versioned_lib_suffix, + 'VersionedLdModSuffix' : _versioned_lib_suffix, + 'VersionedShLibSymlinks' : _versioned_shlib_symlinks, + 'VersionedLdModSymlinks' : _versioned_ldmod_symlinks, + 'VersionedShLibName' : link._versioned_shlib_name, + 'VersionedLdModName' : link._versioned_ldmod_name, + 'VersionedShLibSoname' : _versioned_shlib_soname, + 'VersionedLdModSoname' : _versioned_shlib_soname, + } def exists(env): # TODO: sync with link.smart_link() to choose a linker diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index 33e50d9..d52a9e5 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -76,7 +76,7 @@ def smart_link(source, target, env, for_signature): def _lib_emitter(target, source, env, **kw): Verbose = False if Verbose: - print "_lib_emitter: str(target[0])=%r" % str(target[0]) + print "_lib_emitter: target[0]=%r" % target[0].get_path() for tgt in target: tgt.attributes.shared = 1 @@ -103,35 +103,44 @@ 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): +def _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, suffix_generator, **kw): """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so'""" Verbose = False if Verbose: + print "_versioned_lib_name: libnode=%r" % libnode.get_path() print "_versioned_lib_name: version=%r" % version + print "_versioned_lib_name: prefix=%r" % prefix + print "_versioned_lib_name: suffix=%r" % suffix + print "_versioned_lib_name: suffix_generator=%r" % suffix_generator - versioned_name = os.path.basename(str(libnode)) + versioned_name = os.path.basename(libnode.get_path()) if Verbose: print "_versioned_lib_name: versioned_name=%r" % versioned_name - if Verbose: - print "_versioned_lib_name: suffix=%r" % suffix - + versioned_prefix = prefix_generator(env, **kw) versioned_suffix = suffix_generator(env, **kw) + if Verbose: + print "_versioned_lib_name: versioned_prefix=%r" % versioned_prefix + print "_versioned_lib_name: versioned_suffix=%r" % versioned_suffix + versioned_prefix_re = '^' + re.escape(versioned_prefix) versioned_suffix_re = re.escape(versioned_suffix) + '$' - name = re.sub(versioned_suffix_re, suffix, versioned_name) + name = re.sub(versioned_prefix_re, prefix, versioned_name) + name = re.sub(versioned_suffix_re, suffix, 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_shlib_name(env, libnode, version, prefix, suffix, **kw): + pg = SCons.Tool.ShLibPrefixGenerator + sg = SCons.Tool.ShLibSuffixGenerator + return _versioned_lib_name(env, libnode, version, prefix, suffix, pg, sg, **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 _versioned_ldmod_name(env, libnode, version, prefix, suffix, **kw): + pg = SCons.Tool.LdModPrefixGenerator + sg = SCons.Tool.LdModSuffixGenerator + return _versioned_lib_name(env, libnode, version, prefix, suffix, pg, sg, **kw) def generate(env): -- cgit v0.12 From 76b1f15cd9d50e70672908bed0a5c065232e4301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 4 Sep 2015 23:44:31 +0200 Subject: remove redundant get_lib_prefix() method --- src/engine/SCons/Tool/__init__.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 0b09a13..23b08c6 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -298,17 +298,6 @@ class _LibInfoGeneratorBase(object): prefix = env.subst('$IMPLIBPREFIX') return prefix - 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() -- cgit v0.12 From 13c193a7dabeecc0381a6ef770e72de216eb3f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Fri, 4 Sep 2015 23:55:20 +0200 Subject: minor fix in Tool/__init__.py --- src/engine/SCons/Tool/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 23b08c6..df917ed 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -632,7 +632,7 @@ def createLoadableModuleBuilder(env): LibSymlinksAction ] ld_module = SCons.Builder.Builder(action = action_list, emitter = "$LDMODULEEMITTER", - prefix = ShLibPrefixGenerator, + prefix = LdModPrefixGenerator, suffix = LdModSuffixGenerator, target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', -- cgit v0.12 From cf4df802f7c81e2eb07628850165ad1e2269334a Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sat, 5 Sep 2015 00:50:43 +0200 Subject: add action string function for LibSymlinksAction --- src/engine/SCons/Tool/__init__.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index df917ed..d1f41ec 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -466,7 +466,7 @@ class _LibNameGenerator(_LibInfoGeneratorBase): super(_LibNameGenerator, self).__init__(libtype, 'Name') def __call__(self, env, libnode, **kw): - """Returns library name with version suffixes stripped""" + """Returns "demangled" library name""" Verbose = False if Verbose: @@ -536,9 +536,7 @@ def StringizeLibSymlinks(symlinks): if SCons.Util.is_List(symlinks): try: return [ (k.get_path(), v.get_path()) for k,v in symlinks ] - except TypeError: - return symlinks - except ValueError: + except (TypeError, ValueError): return symlinks else: return symlinks @@ -589,7 +587,24 @@ def LibSymlinksActionFunction(target, source, env): CreateLibSymlinks(env, symlinks) return 0 -LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, None) +def LibSymlinksStrFun(target, source, env,*args): + cmd = None + for tgt in target: + symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None) + if symlinks: + if cmd is None: cmd = "" + if cmd: cmd += "\n" + cmd += "Create symlinks for: %r" % tgt.get_path() + try: + linkstr = ', '.join([ "%r->%r" %(k,v) for k,v in StringizeLibSymlinks(symlinks)]) + except (KeyError, ValueError): + pass + else: + cmd += ": %s" % linkstr + return cmd + + +LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun) def createSharedLibBuilder(env): """This is a utility function that creates the SharedLibrary -- cgit v0.12 From 2a1a18d6c6a311f28eca6b97372475a09732bad0 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Mon, 7 Sep 2015 09:34:25 +0200 Subject: minor correction to debug code in Tool/__init__.py --- src/engine/SCons/Tool/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index d1f41ec..87a4cf4 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -253,14 +253,16 @@ def _call_linker_cb(env, callback, args, result = None): try: cbfun = env['LINKCALLBACKS'][callback] except (KeyError, TypeError): + if Verbose: + print '_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' pass else: if Verbose: - print '_call_linker_cb: env[%r] found' % callback - print '_call_linker_cb: env[%r]=%r' % (callback, cbfun) + print '_call_linker_cb: env["LINKCALLBACKS"][%r] found' % callback + print '_call_linker_cb: env["LINKCALLBACKS"][%r]=%r' % (callback, cbfun) if(callable(cbfun)): if Verbose: - print '_call_linker_cb: env[%r] is callable' % callback + print '_call_linker_cb: env["LINKCALLBACKS"][%r] is callable' % callback result = cbfun(env, *args) return result -- cgit v0.12 From 4f3e3c23da4fe1b1cc22ac1c1030c94f26217ec5 Mon Sep 17 00:00:00 2001 From: ptomulik Date: Tue, 15 Sep 2015 16:06:48 +0200 Subject: revision: address remarks made by Gary Oberbrunner --- src/engine/SCons/Tool/__init__.py | 234 +++++++++++++++++++++++-------------- src/engine/SCons/Tool/__init__.xml | 2 +- 2 files changed, 145 insertions(+), 91 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 87a4cf4..357b765 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -239,7 +239,7 @@ def createStaticLibBuilder(env): def _call_linker_cb(env, callback, args, result = None): """Returns the result of env['LINKCALLBACKS'][callback](*args) if env['LINKCALLBACKS'] is a dictionary and env['LINKCALLBACKS'][callback] - is callable. If these conditions are not meet, return the value provided as + is callable. If these conditions are not met, 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""" @@ -254,7 +254,7 @@ def _call_linker_cb(env, callback, args, result = None): cbfun = env['LINKCALLBACKS'][callback] except (KeyError, TypeError): if Verbose: - print '_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' + print '_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' % callback pass else: if Verbose: @@ -266,22 +266,91 @@ def _call_linker_cb(env, callback, args, result = None): result = cbfun(env, *args) return result +def _call_env_subst(env, string, *args, **kw): + kw2 = {} + for k in ('raw', 'target', 'source', 'conv', 'executor'): + try: kw2[k] = kw[k] + except KeyError: pass + return env.subst(string, *args, **kw2) + +class _ShLibInfoSupport(object): + def get_libtype(self): + return 'ShLib' + def get_lib_prefix(self, env, *args, **kw): + return _call_env_subst(env,'$SHLIBPREFIX', *args, **kw) + def get_lib_suffix(self, env, *args, **kw): + return _call_env_subst(env,'$SHLIBSUFFIX', *args, **kw) + def get_lib_version(self, env, *args, **kw): + return _call_env_subst(env,'$SHLIBVERSION', *args, **kw) + def get_lib_noversionsymlinks(self, env, *args, **kw): + return _call_env_subst(env,'$SHLIBNOVERSIONSYMLINKS', *args, **kw) + +class _LdModInfoSupport(object): + def get_libtype(self): + return 'LdMod' + def get_lib_prefix(self, env, *args, **kw): + return _call_env_subst(env,'$LDMODULEPREFIX', *args, **kw) + def get_lib_suffix(self, env, *args, **kw): + return _call_env_subst(env,'$LDMODULESUFFIX', *args, **kw) + def get_lib_version(self, env, *args, **kw): + return _call_env_subst(env,'$LDMODULEVERSION', *args, **kw) + def get_lib_noversionsymlinks(self, env, *args, **kw): + return _call_env_subst(env,'$LDMODULENOVERSIONSYMLINKS', *args, **kw) + +class _ImpLibInfoSupport(object): + def get_libtype(self): + return 'ImpLib' + def get_lib_prefix(self, env, *args, **kw): + return _call_env_subst(env,'$IMPLIBPREFIX', *args, **kw) + def get_lib_suffix(self, env, *args, **kw): + return _call_env_subst(env,'$IMPLIBSUFFIX', *args, **kw) + def get_lib_version(self, env, *args, **kw): + version = _call_env_subst(env,'$IMPLIBVERSION', *args, **kw) + if not version: + try: lt = kw['implib_libtype'] + except KeyError: pass + else: + if lt == 'ShLib': + version = _call_env_subst(env,'$SHLIBVERSION', *args, **kw) + elif lt == 'LdMod': + version = _call_env_subst(env,'$LDMODULEVERSION', *args, **kw) + return version + def get_lib_noversionsymlinks(self, env, *args, **kw): + disable = None + try: env['IMPLIBNOVERSIONSYMLINKS'] + except KeyError: + try: lt = kw['implib_libtype'] + except KeyError: pass + else: + if lt == 'ShLib': + disable = _call_env_subst(env,'$SHLIBNOVERSIONSYMLINKS', *args, **kw) + elif lt == 'LdMod': + disable = _call_env_subst(env,'$LDMODULENOVERSIONSYMLINKS', *args, **kw) + else: + disable = _call_env_subst(env,'$IMPLIBNOVERSIONSYMLINKS', *args, **kw) + return disable + 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 """ + _support_classes = { 'ShLib' : _ShLibInfoSupport, + 'LdMod' : _LdModInfoSupport, + 'ImpLib' : _ImpLibInfoSupport } 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']: + try: + support_class = self._support_classes[libtype] + except KeyError: raise ValueError('unsupported libtype %r' % libtype) - self.libtype = libtype + self._support = support_class() def get_libtype(self): - return self.libtype + return self._support.get_libtype() def set_infoname(self, infoname): self.infoname = infoname @@ -289,47 +358,21 @@ class _LibInfoGeneratorBase(object): 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_prefix(self, env, *args, **kw): + return self._support.get_lib_prefix(env,*args,**kw) - 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_suffix(self, env, *args, **kw): + return self._support.get_lib_suffix(env,*args,**kw) - 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_lib_version(self, env, *args, **kw): + return self._support.get_lib_version(env,*args,**kw) + def get_lib_noversionsymlinks(self, env, *args, **kw): + return self._support.get_lib_noversionsymlinks(env,*args,**kw) + + # Returns name of generator linker callback that shall be used to generate + # our info for a versioned library. For example, if our libtype is 'ShLib' + # and infoname is 'Prefix', it would return 'VersionedShLibPrefix'. def get_versioned_lib_info_generator(self, **kw): try: libtype = kw['generator_libtype'] except KeyError: libtype = self.get_libtype() @@ -349,16 +392,22 @@ class _LibPrefixGenerator(_LibInfoGeneratorBase): def __call__(self, env, sources = None, **kw): Verbose = False - prefix = self.get_lib_prefix(env) + if sources and 'source' not in kw: + kw2 = kw.copy() + kw2['source'] = sources + else: + kw2 = kw + + prefix = self.get_lib_prefix(env,**kw2) if Verbose: print "_LibPrefixGenerator: input prefix=%r" % prefix - version = self.get_lib_version(env, **kw) + version = self.get_lib_version(env, **kw2) if Verbose: print "_LibPrefixGenerator: version=%r" % version if version: - prefix = self.generate_versioned_lib_info(env, [prefix, version], prefix, **kw) + prefix = self.generate_versioned_lib_info(env, [prefix, version], prefix, **kw2) if Verbose: print "_LibPrefixGenerator: return prefix=%r" % prefix @@ -377,16 +426,22 @@ class _LibSuffixGenerator(_LibInfoGeneratorBase): def __call__(self, env, sources = None, **kw): Verbose = False - suffix = self.get_lib_suffix(env) + if sources and 'source' not in kw: + kw2 = kw.copy() + kw2['source'] = sources + else: + kw2 = kw + + suffix = self.get_lib_suffix(env, **kw2) if Verbose: print "_LibSuffixGenerator: input suffix=%r" % suffix - version = self.get_lib_version(env, **kw) + version = self.get_lib_version(env, **kw2) if Verbose: print "_LibSuffixGenerator: version=%r" % version if version: - suffix = self.generate_versioned_lib_info(env, [suffix, version], suffix, **kw) + suffix = self.generate_versioned_lib_info(env, [suffix, version], suffix, **kw2) if Verbose: print "_LibSuffixGenerator: return suffix=%r" % suffix @@ -402,45 +457,30 @@ class _LibSymlinkGenerator(_LibInfoGeneratorBase): 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 libnode and 'target' not in kw: + kw2 = kw.copy() + kw2['target'] = libnode + else: + kw2 = kw + if Verbose: print "_LibSymLinkGenerator: libnode=%r" % libnode.get_path() symlinks = None - version = self.get_lib_version(env, **kw) - disable = self.get_noversionsymlinks(env, **kw) + version = self.get_lib_version(env, **kw2) + disable = self.get_lib_noversionsymlinks(env, **kw2) if Verbose: print '_LibSymlinkGenerator: version=%r' % version print '_LibSymlinkGenerator: disable=%r' % disable if version and not disable: - prefix = self.get_lib_prefix(env) - suffix = self.get_lib_suffix(env) - symlinks = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw) + prefix = self.get_lib_prefix(env,**kw2) + suffix = self.get_lib_suffix(env,**kw2) + symlinks = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2) if Verbose: print '_LibSymlinkGenerator: return symlinks=%r' % StringizeLibSymlinks(symlinks) @@ -471,18 +511,24 @@ class _LibNameGenerator(_LibInfoGeneratorBase): """Returns "demangled" library name""" Verbose = False + if libnode and 'target' not in kw: + kw2 = kw.copy() + kw2['target'] = libnode + else: + kw2 = kw + if Verbose: print "_LibNameGenerator: libnode=%r" % libnode.get_path() - version = self.get_lib_version(env, **kw) + version = self.get_lib_version(env, **kw2) if Verbose: print '_LibNameGenerator: version=%r' % version name = None if version: - prefix = self.get_lib_prefix(env) - suffix = self.get_lib_suffix(env) - name = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw) + prefix = self.get_lib_prefix(env,**kw2) + suffix = self.get_lib_suffix(env,**kw2) + name = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2) if not name: name = os.path.basename(libnode.get_path()) @@ -506,17 +552,23 @@ class _LibSonameGenerator(_LibInfoGeneratorBase): """Returns a SONAME based on a shared library's node path""" Verbose = False + if libnode and 'target' not in kw: + kw2 = kw.copy() + kw2['target'] = libnode + else: + kw2 = kw + if Verbose: print "_LibSonameGenerator: libnode=%r" % libnode.get_path() - soname = env.subst('$SONAME') + soname = _call_env_subst(env, '$SONAME', **kw2) if not soname: - version = self.get_lib_version(env,**kw) + version = self.get_lib_version(env,**kw2) if Verbose: print "_LibSonameGenerator: version=%r" % version if version: - suffix = self.get_lib_suffix(env) - soname = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw) + suffix = self.get_lib_suffix(env,**kw2) + soname = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw2) if not soname: # fallback to library name (as returned by appropriate _LibNameGenerator) @@ -571,13 +623,15 @@ def CreateLibSymlinks(env, symlinks): link = link.get_path() if(Verbose): print "CreateLibSymlinks: preparing to add symlink %r -> %r" % (link, linktgt) - try: - os.remove(link) + # Delete the (previously created) symlink if exists. Let only symlinks + # to be deleted to prevent accidental deletion of source files... + if env.fs.islink(link): + env.fs.unlink(link) if(Verbose): - print "CreateLibSymlinks: removed old file %r" % link - except: - pass - os.symlink(linktgt, link) + print "CreateLibSymlinks: removed old symlink %r" % link + # If a file or directory exists with the same name as link, an OSError + # will be thrown, which should be enough, I think. + env.fs.symlink(linktgt, link) if(Verbose): print "CreateLibSymlinks: add symlink %r -> %r" % (link, linktgt) return 0 @@ -589,7 +643,7 @@ def LibSymlinksActionFunction(target, source, env): CreateLibSymlinks(env, symlinks) return 0 -def LibSymlinksStrFun(target, source, env,*args): +def LibSymlinksStrFun(target, source, env, *args): cmd = None for tgt in target: symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None) diff --git a/src/engine/SCons/Tool/__init__.xml b/src/engine/SCons/Tool/__init__.xml index 7102fa4..7f19bfc 100644 --- a/src/engine/SCons/Tool/__init__.xml +++ b/src/engine/SCons/Tool/__init__.xml @@ -469,7 +469,7 @@ as C++ files. 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 +undefined, the &cv-link-SHLIBVERSION;/&cv-link-LDMODULEVERSION; is used to determine the version of versioned import library. -- cgit v0.12 From 3173faf4aa18308832c7c8a983e3ff556d2cb5ba Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sat, 19 Sep 2015 02:09:53 +0200 Subject: support free-form SHLIBVERSION/LDMODULEVERSION --- src/engine/SCons/Tool/gnulink.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index e5e8818..0c5087b 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -103,7 +103,11 @@ def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, so link0 = env.fs.File(soname, linkdir) link1 = env.fs.File(name, linkdir) - symlinks = [ (link0, libnode), (link1, link0) ] + # This allows anything in SHLIBVERSION (especially SHLIBVERSION=1). + if link0 == libnode: + symlinks = [ (link1, libnode) ] + else: + symlinks = [ (link0, libnode), (link1, link0) ] if Verbose: print "_versioned_lib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) -- cgit v0.12 From f6f919842bf5dc265422309b98546e182e3ee9ae Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sat, 19 Sep 2015 21:24:29 +0200 Subject: gnulink: create direct symlinks instead of daisy-chained ones --- src/engine/SCons/Tool/gnulink.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 0c5087b..aabc2f1 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -103,11 +103,13 @@ def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, so link0 = env.fs.File(soname, linkdir) link1 = env.fs.File(name, linkdir) - # This allows anything in SHLIBVERSION (especially SHLIBVERSION=1). + # We create direct symlinks, not daisy-chained. if link0 == libnode: + # This enables SHLIBVERSION without periods (e.g. SHLIBVERSION=1) symlinks = [ (link1, libnode) ] else: - symlinks = [ (link0, libnode), (link1, link0) ] + # This handles usual SHLIBVERSION, i.e. '1.2', '1.2.3', etc. + symlinks = [ (link0, libnode), (link1, libnode) ] if Verbose: print "_versioned_lib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) -- cgit v0.12 From 9a43408e4419ba54733222df65a38a9d1a925b6d Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 02:24:11 +0200 Subject: support for versioned libraries for sunlink --- src/engine/SCons/Tool/gnulink.py | 96 +++--------------------------------- src/engine/SCons/Tool/link.py | 85 +++++++++++++++++++++++++++++++ src/engine/SCons/Tool/sunlink.py | 21 ++++++++ test/LINK/VersionedLib-VariantDir.py | 8 +++ test/LINK/VersionedLib-j2.py | 8 +++ test/LINK/VersionedLib-subdir.py | 8 +++ test/LINK/VersionedLib.py | 14 ++++++ 7 files changed, 150 insertions(+), 90 deletions(-) diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index aabc2f1..92c38c4 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -41,90 +41,6 @@ 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, prefix, suffix, name_func): - """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_func(env, libnode, version, prefix, suffix) - 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, prefix, suffix): - return _versioned_lib_soname(env, libnode, version, prefix, suffix, link._versioned_shlib_name) - -def _versioned_ldmod_soname(env, libnode, version, prefix, suffix): - return _versioned_lib_soname(env, libnode, version, prefix, suffix, link._versioned_ldmod_name) - -def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, soname_func): - """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: libnode=%r" % libnode.get_path() - print "_versioned_lib_symlinks: version=%r" % version - - 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" % None - return None - - linkdir = libnode.get_dir() - if Verbose: - print "_versioned_lib_symlinks: linkdir=%r" % linkdir.get_path() - - name = name_func(env, libnode, version, prefix, suffix) - if Verbose: - print "_versioned_lib_symlinks: name=%r" % name - - soname = soname_func(env, libnode, version, prefix, suffix) - - link0 = env.fs.File(soname, linkdir) - link1 = env.fs.File(name, linkdir) - - # We create direct symlinks, not daisy-chained. - if link0 == libnode: - # This enables SHLIBVERSION without periods (e.g. SHLIBVERSION=1) - symlinks = [ (link1, libnode) ] - else: - # This handles usual SHLIBVERSION, i.e. '1.2', '1.2.3', etc. - symlinks = [ (link0, libnode), (link1, libnode) ] - - if Verbose: - print "_versioned_lib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) - - return symlinks - -def _versioned_shlib_symlinks(env, libnode, version, prefix, suffix): - nf = link._versioned_shlib_name - sf = _versioned_shlib_soname - return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) - -def _versioned_ldmod_symlinks(env, libnode, version, prefix, suffix): - nf = link._versioned_ldmod_name - sf = _versioned_ldmod_soname - return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" @@ -160,14 +76,14 @@ def generate(env): env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator env['LINKCALLBACKS'] = { - 'VersionedShLibSuffix' : _versioned_lib_suffix, - 'VersionedLdModSuffix' : _versioned_lib_suffix, - 'VersionedShLibSymlinks' : _versioned_shlib_symlinks, - 'VersionedLdModSymlinks' : _versioned_ldmod_symlinks, + 'VersionedShLibSuffix' : link._versioned_lib_suffix, + 'VersionedLdModSuffix' : link._versioned_lib_suffix, + 'VersionedShLibSymlinks' : link._versioned_shlib_symlinks, + 'VersionedLdModSymlinks' : link._versioned_ldmod_symlinks, 'VersionedShLibName' : link._versioned_shlib_name, 'VersionedLdModName' : link._versioned_ldmod_name, - 'VersionedShLibSoname' : _versioned_shlib_soname, - 'VersionedLdModSoname' : _versioned_shlib_soname, + 'VersionedShLibSoname' : link._versioned_shlib_soname, + 'VersionedLdModSoname' : link._versioned_shlib_soname, } def exists(env): diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index d52a9e5..0af7776 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -142,6 +142,91 @@ def _versioned_ldmod_name(env, libnode, version, prefix, suffix, **kw): sg = SCons.Tool.LdModSuffixGenerator return _versioned_lib_name(env, libnode, version, prefix, suffix, pg, sg, **kw) +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, prefix, suffix, name_func): + """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_func(env, libnode, version, prefix, suffix) + 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, prefix, suffix): + return _versioned_lib_soname(env, libnode, version, prefix, suffix, _versioned_shlib_name) + +def _versioned_ldmod_soname(env, libnode, version, prefix, suffix): + return _versioned_lib_soname(env, libnode, version, prefix, suffix, _versioned_ldmod_name) + +def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, soname_func): + """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: libnode=%r" % libnode.get_path() + print "_versioned_lib_symlinks: version=%r" % version + + 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" % None + return None + + linkdir = libnode.get_dir() + if Verbose: + print "_versioned_lib_symlinks: linkdir=%r" % linkdir.get_path() + + name = name_func(env, libnode, version, prefix, suffix) + if Verbose: + print "_versioned_lib_symlinks: name=%r" % name + + soname = soname_func(env, libnode, version, prefix, suffix) + + link0 = env.fs.File(soname, linkdir) + link1 = env.fs.File(name, linkdir) + + # We create direct symlinks, not daisy-chained. + if link0 == libnode: + # This enables SHLIBVERSION without periods (e.g. SHLIBVERSION=1) + symlinks = [ (link1, libnode) ] + else: + # This handles usual SHLIBVERSION, i.e. '1.2', '1.2.3', etc. + symlinks = [ (link0, libnode), (link1, libnode) ] + + if Verbose: + print "_versioned_lib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) + + return symlinks + +def _versioned_shlib_symlinks(env, libnode, version, prefix, suffix): + nf = _versioned_shlib_name + sf = _versioned_shlib_soname + return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) + +def _versioned_ldmod_symlinks(env, libnode, version, prefix, suffix): + nf = _versioned_ldmod_name + sf = _versioned_ldmod_soname + return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) + def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py index 5996a30..c9bb17d 100644 --- a/src/engine/SCons/Tool/sunlink.py +++ b/src/engine/SCons/Tool/sunlink.py @@ -66,6 +66,27 @@ def generate(env): env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + # Support for versioned libraries + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLINKSONAME' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME' + + env['_SHLINKSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' + env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}' + + env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator + env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator + + env['LINKCALLBACKS'] = { + 'VersionedShLibSuffix' : link._versioned_lib_suffix, + 'VersionedLdModSuffix' : link._versioned_lib_suffix, + 'VersionedShLibSymlinks' : link._versioned_shlib_symlinks, + 'VersionedLdModSymlinks' : link._versioned_ldmod_symlinks, + 'VersionedShLibName' : link._versioned_shlib_name, + 'VersionedLdModName' : link._versioned_ldmod_name, + 'VersionedShLibSoname' : link._versioned_shlib_soname, + 'VersionedLdModSoname' : link._versioned_shlib_soname, + } + def exists(env): return ccLinker diff --git a/test/LINK/VersionedLib-VariantDir.py b/test/LINK/VersionedLib-VariantDir.py index a3ea660..7406a33 100644 --- a/test/LINK/VersionedLib-VariantDir.py +++ b/test/LINK/VersionedLib-VariantDir.py @@ -126,6 +126,14 @@ elif platform == 'win32': 'foo.lib', ] obj = 'foo.obj' +elif platform == 'sunos': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.so', + 'libfoo.so.0', + 'libfoo.so.0.1.2', + ] + obj = 'so_foo.os' else: # All (?) the files we expect will get created in the current directory files= [ diff --git a/test/LINK/VersionedLib-j2.py b/test/LINK/VersionedLib-j2.py index 076a4dd..6f37e54 100644 --- a/test/LINK/VersionedLib-j2.py +++ b/test/LINK/VersionedLib-j2.py @@ -113,6 +113,14 @@ elif platform == 'win32': 'foo.lib', 'foo.obj', ] +elif platform == 'sunos': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.so', + 'libfoo.so.0', + 'libfoo.so.0.1.2', + 'so_foo.os', + ] else: # All (?) the files we expect will get created in the current directory files= [ diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py index 6facd41..15369ef 100644 --- a/test/LINK/VersionedLib-subdir.py +++ b/test/LINK/VersionedLib-subdir.py @@ -120,6 +120,14 @@ elif platform == 'win32': 'foo.lib', ] obj = 'foo.obj' +elif platform == 'sunos': + # All (?) the files we expect will get created in the current directory + files = [ + 'libfoo.so', + 'libfoo.so.0', + 'libfoo.so.0.1.2', + ] + obj = 'so_foo.os' else: # All (?) the files we expect will get created in the current directory files= [ diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py index c68dd55..a75e4a5 100644 --- a/test/LINK/VersionedLib.py +++ b/test/LINK/VersionedLib.py @@ -130,6 +130,20 @@ elif platform == 'win32': 'test.dll', 'test.lib', ] +elif platform == 'sunos': + # All (?) the files we expect will get created in the current directory + files = [ + 'libtest.so', + 'libtest.so.2', + 'libtest.so.2.5.4', + 'so_test.os', + ] + # All (?) the files we expect will get created in the 'installtest' directory + instfiles = [ + 'libtest.so', + 'libtest.so.2', + 'libtest.so.2.5.4', + ] else: # All (?) the files we expect will get created in the current directory files= [ -- cgit v0.12 From cb508c9a37c2a3cd5fcf55cc1efd4c47a0abdd41 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 04:21:27 +0200 Subject: fix broken soname generator --- src/engine/SCons/Defaults.py | 17 ++++++++++++----- src/engine/SCons/Tool/__init__.py | 3 ++- src/engine/SCons/Tool/gnulink.py | 2 +- src/engine/SCons/Tool/sunlink.py | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index db48969..c8170c3 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -482,9 +482,14 @@ 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) +# if env[version_var] id defined, returns env[flags_var], otherwise returns None +def __libversionflags(env, version_var, flags_var): + try: + if env[version_var]: + return env[flags_var] + except KeyError: + pass + return None ConstructionEnvironment = { 'BUILDERS' : {}, @@ -504,8 +509,10 @@ ConstructionEnvironment = { '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', - '__SHLIBVERSIONFLAGS' : __libversionflags_string('SHLIBVERSION'), - '__LDMODULEVERSIONFLAGS' : __libversionflags_string('LDMODULEVERSION'), + '__libversionflags' : __libversionflags, + '__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', + '__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', + 'TEMPFILE' : NullCmdGenerator, 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 357b765..96b9d98 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -567,8 +567,9 @@ class _LibSonameGenerator(_LibInfoGeneratorBase): if Verbose: print "_LibSonameGenerator: version=%r" % version if version: + prefix = self.get_lib_prefix(env,**kw2) suffix = self.get_lib_suffix(env,**kw2) - soname = self.generate_versioned_lib_info(env, [libnode, version, suffix], **kw2) + soname = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2) if not soname: # fallback to library name (as returned by appropriate _LibNameGenerator) diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 92c38c4..2e0ed03 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -83,7 +83,7 @@ def generate(env): 'VersionedShLibName' : link._versioned_shlib_name, 'VersionedLdModName' : link._versioned_ldmod_name, 'VersionedShLibSoname' : link._versioned_shlib_soname, - 'VersionedLdModSoname' : link._versioned_shlib_soname, + 'VersionedLdModSoname' : link._versioned_ldmod_soname, } def exists(env): diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py index c9bb17d..751bf92 100644 --- a/src/engine/SCons/Tool/sunlink.py +++ b/src/engine/SCons/Tool/sunlink.py @@ -84,7 +84,7 @@ def generate(env): 'VersionedShLibName' : link._versioned_shlib_name, 'VersionedLdModName' : link._versioned_ldmod_name, 'VersionedShLibSoname' : link._versioned_shlib_soname, - 'VersionedLdModSoname' : link._versioned_shlib_soname, + 'VersionedLdModSoname' : link._versioned_ldmod_soname, } def exists(env): -- cgit v0.12 From 31a9cc12812eeb3a83dd26010ec6947a79368f96 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 05:36:54 +0200 Subject: refactor code -> avoid code duplication --- src/engine/SCons/Tool/gnulink.py | 35 ++++++------------------------- src/engine/SCons/Tool/link.py | 45 ++++++++++++++++++++++++++++++++++++++++ src/engine/SCons/Tool/sunar.py | 2 +- src/engine/SCons/Tool/sunlink.py | 21 ++----------------- 4 files changed, 54 insertions(+), 49 deletions(-) diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 2e0ed03..6b0d5b3 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -55,36 +55,13 @@ def generate(env): 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)}' + # OpenBSD doesn't usually use SONAME for libraries + use_soname = not sys.platform.startswith('openbsd') + link._setup_versioned_lib_variables(env, tool = 'gnulink', use_soname = use_soname) + env['LINKCALLBACKS'] = link._versioned_lib_callbacks() - env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator - env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator - - env['LINKCALLBACKS'] = { - 'VersionedShLibSuffix' : link._versioned_lib_suffix, - 'VersionedLdModSuffix' : link._versioned_lib_suffix, - 'VersionedShLibSymlinks' : link._versioned_shlib_symlinks, - 'VersionedLdModSymlinks' : link._versioned_ldmod_symlinks, - 'VersionedShLibName' : link._versioned_shlib_name, - 'VersionedLdModName' : link._versioned_ldmod_name, - 'VersionedShLibSoname' : link._versioned_shlib_soname, - 'VersionedLdModSoname' : link._versioned_ldmod_soname, - } + # For backward-compatiblity with older SCons versions + env['SHLIBVERSIONFLAGS'] = SCons.Util.CLVar('-Wl,-Bsymbolic') def exists(env): # TODO: sync with link.smart_link() to choose a linker diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index 0af7776..6bd36e4 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -227,6 +227,51 @@ def _versioned_ldmod_symlinks(env, libnode, version, prefix, suffix): sf = _versioned_ldmod_soname return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) +def _versioned_lib_callbacks(): + return { + 'VersionedShLibSuffix' : _versioned_lib_suffix, + 'VersionedLdModSuffix' : _versioned_lib_suffix, + 'VersionedShLibSymlinks' : _versioned_shlib_symlinks, + 'VersionedLdModSymlinks' : _versioned_ldmod_symlinks, + 'VersionedShLibName' : _versioned_shlib_name, + 'VersionedLdModName' : _versioned_ldmod_name, + 'VersionedShLibSoname' : _versioned_shlib_soname, + 'VersionedLdModSoname' : _versioned_ldmod_soname, + }.copy() + +# Setup all variables required by the versioning machinery +def _setup_versioned_lib_variables(env, **kw): + + tool = None + try: tool = kw['tool'] + except KeyError: pass + + use_soname = False + try: use_soname = kw['use_soname'] + except KeyError: pass + + # 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 in SCons/Defaults.py). + if use_soname: + # If the linker uses SONAME, then we need this little automata + if tool == 'sunlink': + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLINKSONAME' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME' + else: + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLINKSONAME' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME' + env['_SHLINKSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' + env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}' + env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator + env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator + else: + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' + + # LDOMDULVERSIONFLAGS should always default to $SHLIBVERSIONFLAGS + env['LDMODULEVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' + def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" diff --git a/src/engine/SCons/Tool/sunar.py b/src/engine/SCons/Tool/sunar.py index 779414f..3ee4344 100644 --- a/src/engine/SCons/Tool/sunar.py +++ b/src/engine/SCons/Tool/sunar.py @@ -53,7 +53,7 @@ def generate(env): env['SHLINK'] = '$LINK' env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') - env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.a' diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py index 751bf92..680af03 100644 --- a/src/engine/SCons/Tool/sunlink.py +++ b/src/engine/SCons/Tool/sunlink.py @@ -67,25 +67,8 @@ def generate(env): env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' # Support for versioned libraries - env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLINKSONAME' - env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME' - - env['_SHLINKSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' - env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}' - - env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator - env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator - - env['LINKCALLBACKS'] = { - 'VersionedShLibSuffix' : link._versioned_lib_suffix, - 'VersionedLdModSuffix' : link._versioned_lib_suffix, - 'VersionedShLibSymlinks' : link._versioned_shlib_symlinks, - 'VersionedLdModSymlinks' : link._versioned_ldmod_symlinks, - 'VersionedShLibName' : link._versioned_shlib_name, - 'VersionedLdModName' : link._versioned_ldmod_name, - 'VersionedShLibSoname' : link._versioned_shlib_soname, - 'VersionedLdModSoname' : link._versioned_ldmod_soname, - } + link._setup_versioned_lib_variables(env, tool = 'sunlink', use_soname = True) + env['LINKCALLBACKS'] = link._versioned_lib_callbacks() def exists(env): return ccLinker -- cgit v0.12 From 9e5928980c0855f91de016467ef752d3aa338f1a Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 13:42:22 +0200 Subject: do not set SHxxxx variables in sunar.py --- src/engine/SCons/Tool/sunar.py | 3 --- src/engine/SCons/Tool/sunar.xml | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/engine/SCons/Tool/sunar.py b/src/engine/SCons/Tool/sunar.py index 3ee4344..eb58457 100644 --- a/src/engine/SCons/Tool/sunar.py +++ b/src/engine/SCons/Tool/sunar.py @@ -51,9 +51,6 @@ def generate(env): env['ARFLAGS'] = SCons.Util.CLVar('r') env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') - env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.a' diff --git a/src/engine/SCons/Tool/sunar.xml b/src/engine/SCons/Tool/sunar.xml index 65f0c9e..f875217 100644 --- a/src/engine/SCons/Tool/sunar.xml +++ b/src/engine/SCons/Tool/sunar.xml @@ -33,15 +33,11 @@ Sets construction variables for the Sun library archiver. AR ARFLAGS ARCOM -SHLINK -SHLINKFLAGS -SHLINKCOM LIBPREFIX LIBSUFFIX ARCOMSTR -SHLINKCOMSTR -- cgit v0.12 From 123fea257474ede95fa6bef8fb403cc83eff5cdc Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 17:01:35 +0200 Subject: add tests to test/LINK/VersionedLib.py --- test/LINK/VersionedLib.py | 332 ++++++++++++++++++++++++++++++---------------- 1 file changed, 215 insertions(+), 117 deletions(-) diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py index a75e4a5..2e38dee 100644 --- a/test/LINK/VersionedLib.py +++ b/test/LINK/VersionedLib.py @@ -32,30 +32,167 @@ import SCons.Platform _exe = TestSCons._exe -test = TestSCons.TestSCons() +platform = SCons.Platform.platform_default() -test.write('SConstruct', """\ -import os -env = Environment() -objs = env.SharedObject('test.c') -mylib = env.SharedLibrary('test', objs, SHLIBVERSION = '2.5.4') -env.Program('testapp1.c', LIBS = mylib, LIBPATH='.') -env.Program('testapp2.c', LIBS = ['test'], LIBPATH='.') -instnode = env.InstallVersionedLib("#/installtest",mylib) -env.Default(instnode) -""") +if platform == 'posix': + test_plan = [ + { + 'libversion' : '2', + 'files' : [ 'libtest.so', 'libtest.so.2', 'test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2') ], + }, + { + 'libversion' : '2.5', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5', 'test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.5'), ('libtest.so.2', 'libtest.so.2.5') ], + }, + { + 'libversion' : '2.5.4', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4', 'test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4'), ('libtest.so.2', 'libtest.so.2.5.4') ], + }, + { + 'libversion' : '2.5.4.7.8', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8', 'test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4.7.8'), ('libtest.so.2', 'libtest.so.2.5.4.7.8') ], + }, + { + 'libversion' : 'aabf114f', + 'files' : [ 'libtest.so', 'libtest.so.aabf114f', 'test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.aabf114f' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.aabf114f') ], + }, + { + 'libversion' : '2.dfffa11', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11', 'test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.dfffa11'), ('libtest.so.2', 'libtest.so.2.dfffa11') ], + }, + ] +elif platform == 'darwin': + # All (?) the files we expect will get created in the current directory + test_plan = [ + { + 'libversion' : '2.5.4', + 'files' : [ 'libtest.dylib', 'libtest.2.5.4.dylib', 'test.os' ], + 'instfiles' : [ 'libtest.dylib', 'libtest.2.5.4.dylib' ], + 'symlinks' : [], + }, + ] +elif platform == 'cygwin': + test_plan = [ + { + 'libversion' : '2.5.4', + 'files' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a', 'test.os' ], + 'instfiles' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a' ], + 'symlinks' : [], + }, + { + 'libversion' : '2', + 'files' : [ 'cygtest-2.dll', 'libtest-2.dll.a', 'libtest.dll.a', 'test.os' ], + 'instfiles' : [ 'cygtest-2.dll', 'libtest-2.dll.a', 'libtest.dll.a' ], + 'symlinks' : [ ('libtest.dll.a', 'libtest-2.dll.a') ], + }, + { + 'libversion' : '2.5', + 'files' : [ 'cygtest-2-5.dll', 'libtest-2-5.dll.a', 'libtest.dll.a', 'test.os' ], + 'instfiles' : [ 'cygtest-2-5.dll', 'libtest-2-5.dll.a', 'libtest.dll.a' ], + 'symlinks' : [ ('libtest.dll.a', 'libtest-2-5.dll.a') ], + }, + { + 'libversion' : '2.5.4', + 'files' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a', 'test.os' ], + 'instfiles' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a' ], + 'symlinks' : [ ('libtest.dll.a', 'libtest-2-5-4.dll.a') ], + }, + { + 'libversion' : '2.5.4.7.8', + 'files' : [ 'cygtest-2-5-4-7-8.dll', 'libtest-2-5-4-7-8.dll.a', 'libtest.dll.a', 'test.os' ], + 'instfiles' : [ 'cygtest-2-5-4-7-8.dll', 'libtest-2-5-4-7-8.dll.a', 'libtest.dll.a' ], + 'symlinks' : [ ('libtest.dll.a', 'libtest-2-5-4-7-8.dll.a') ], + }, + { + 'libversion' : 'aabf114f', + 'files' : [ 'cygtest-aabf114f.dll', 'libtest-aabf114f.dll.a', 'libtest.dll.a', 'test.os' ], + 'instfiles' : [ 'cygtest-aabf114f.dll', 'libtest-aabf114f.dll.a', 'libtest.dll.a' ], + 'symlinks' : [ ('libtest.dll.a', 'libtest-aabf114f.dll.a') ], + }, + { + 'libversion' : '2.dfffa11', + 'files' : [ 'cygtest-2-dfffa11.dll', 'libtest-2-dfffa11.dll.a', 'libtest.dll.a', 'test.os' ], + 'instfiles' : [ 'cygtest-2-dfffa11.dll', 'libtest-2-dfffa11.dll.a', 'libtest.dll.a' ], + 'symlinks' : [ ('libtest.dll.a', 'libtest-2-dfffa11.dll.a') ], + }, + ] +elif platform == 'win32': + test_plan = [ + { + 'libversion' : '2.5.4', + 'files' : [ 'test.dll', 'test.lib', 'test.obj' ], + 'instfiles' : [ 'test.dll', 'test.lib' ], + 'symlinks' : [], + }, + ] +elif platform == 'sunos': + test_plan = [ + { + 'libversion' : '2', + 'files' : [ 'libtest.so', 'libtest.so.2', 'so_test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2') ], + }, + { + 'libversion' : '2.5', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5', 'so_test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.5'), ('libtest.so.2', 'libtest.so.2.5') ], + }, + { + 'libversion' : '2.5.4', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4', 'so_test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4'), ('libtest.so.2', 'libtest.so.2.5.4') ], + }, + { + 'libversion' : '2.5.4.7.8', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8', 'so_test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4.7.8'), ('libtest.so.2', 'libtest.so.2.5.4.7.8') ], + }, + { + 'libversion' : 'aabf114f', + 'files' : [ 'libtest.so', 'libtest.so.aabf114f', 'so_test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.aabf114f' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.aabf114f') ], + }, + { + 'libversion' : '2.dfffa11', + 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11', 'so_test.os' ], + 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11' ], + 'symlinks' : [ ('libtest.so', 'libtest.so.2.dfffa11'), ('libtest.so.2', 'libtest.so.2.dfffa11') ], + }, + ] +else: + test_plan = [ + { + 'libversion' : '2.5.4', + 'files' : [ 'libtest.so', 'test.os' ], + 'instfiles' : [ ], + }, + ] -test.write('test.c', """\ +test_c_src = """\ #if _WIN32 __declspec(dllexport) #endif -int testlib(int n) -{ -return n+1 ; -} -""") +int testlib(int n) { return n+1 ; } +""" -testapp_src = """\ +testapp_c_src = """\ #if _WIN32 __declspec(dllimport) #endif @@ -70,112 +207,73 @@ printf("results: testlib(2) = %d\\n",itest) ; return 0 ; } """ -test.write('testapp1.c', testapp_src) -test.write('testapp2.c', testapp_src) -platform = SCons.Platform.platform_default() +for t in test_plan: + test = TestSCons.TestSCons() -test.run(arguments = ['--tree=all']) + libversion = t['libversion'] + files = t['files'] + symlinks = t['symlinks'] + instfiles = t['instfiles'] -if platform == 'posix': - # All (?) the files we expect will get created in the current directory - files = [ - 'libtest.so', - 'libtest.so.2', - 'libtest.so.2.5.4', - 'test.os', - ] - # All (?) the files we expect will get created in the 'installtest' directory - instfiles = [ - 'libtest.so', - 'libtest.so.2', - 'libtest.so.2.5.4', - ] -elif platform == 'darwin': - # All (?) the files we expect will get created in the current directory - files = [ - 'libtest.dylib', - 'libtest.2.5.4.dylib', - 'test.os', - ] - # All (?) the files we expect will get created in the 'installtest' directory - instfiles = [ - 'libtest.dylib', - 'libtest.2.5.4.dylib', - ] -elif platform == 'cygwin': - # All (?) the files we expect will get created in the current directory - files = [ - 'cygtest-2-5-4.dll', - 'libtest-2-5-4.dll.a', - 'libtest.dll.a', - 'test.os', - ] - # All (?) the files we expect will get created in the 'installtest' directory - instfiles = [ - 'cygtest-2-5-4.dll', - 'libtest-2-5-4.dll.a', - 'libtest.dll.a', - ] -elif platform == 'win32': - # All (?) the files we expect will get created in the current directory - files = [ - 'test.dll', - 'test.lib', - 'test.obj', - ] - # All (?) the files we expect will get created in the 'installtest' directory - instfiles = [ - 'test.dll', - 'test.lib', - ] -elif platform == 'sunos': - # All (?) the files we expect will get created in the current directory - files = [ - 'libtest.so', - 'libtest.so.2', - 'libtest.so.2.5.4', - 'so_test.os', - ] - # All (?) the files we expect will get created in the 'installtest' directory - instfiles = [ - 'libtest.so', - 'libtest.so.2', - 'libtest.so.2.5.4', - ] -else: - # All (?) the files we expect will get created in the current directory - files= [ - 'libtest.so', - 'test.os'] - # All (?) the files we expect will get created in the 'installtest' directory - instfiles = [] - -for f in files: - test.must_exist([ f]) -for f in instfiles: - test.must_exist(['installtest', f]) - -# modify test.c and make sure it can recompile when links already exist -test.write('test.c', """\ -#if _WIN32 -__declspec(dllexport) -#endif -int testlib(int n) -{ -return n+11 ; -} -""") + test.write('SConstruct', """\ +import os +env = Environment() +objs = env.SharedObject('test.c') +mylib = env.SharedLibrary('test', objs, SHLIBVERSION = '%s') +env.Program('testapp1.c', LIBS = mylib, LIBPATH='.') +env.Program('testapp2.c', LIBS = ['test'], LIBPATH='.') +instnode = env.InstallVersionedLib("#/installtest",mylib) +env.Default(instnode) +""" % libversion) + + test.write('test.c', test_c_src) + test.write('testapp1.c', testapp_c_src) + test.write('testapp2.c', testapp_c_src) + + test.run(arguments = ['--tree=all']) + + for f in files: + test.must_exist([ f]) + for f in instfiles: + test.must_exist(['installtest', f]) + + wrong_symlinks = [] + for (linkname,expected) in symlinks: + try: + endpoint = os.readlink(linkname) + except OSError, err: + print "%s (expected symlink %r -> %r)" % (err, linkname, expected) + wrong_symlinks.append(linkname) + else: + if endpoint != expected: + print "Wrong symlink: %r -> %r (expected symlink: %r -> %r)" % (linkname, endpoint, linkname, expected) + wrong_symlinks.append(linkname) + + if wrong_symlinks: + test.fail_test(wrong_symlinks) + + # modify test.c and make sure it can recompile when links already exist + test.write('test.c', """\ + #if _WIN32 + __declspec(dllexport) + #endif + int testlib(int n) + { + return n+11 ; + } + """) + + test.run() -test.run() + test.run(arguments = ['-c']) -test.run(arguments = ['-c']) + for f in files: + test.must_not_exist([ f]) -for f in files: - test.must_not_exist([ f]) -for f in instfiles: - test.must_not_exist(['installtest', f]) + for f in instfiles: + test.must_not_exist(['installtest', f]) test.pass_test() -- cgit v0.12 From 26f6e3f56d9741cc91cbe034a0e43d91e0fac6a3 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 17:09:34 +0200 Subject: fix test/LINK/VersionedLib.py to convince sun studio compiler --- test/LINK/VersionedLib.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py index 2e38dee..0d74a35 100644 --- a/test/LINK/VersionedLib.py +++ b/test/LINK/VersionedLib.py @@ -192,6 +192,13 @@ __declspec(dllexport) int testlib(int n) { return n+1 ; } """ +test_c_src2 = """\ +#if _WIN32 +__declspec(dllexport) +#endif +int testlib(int n) { return n+11 ; } +""" + testapp_c_src = """\ #if _WIN32 __declspec(dllimport) @@ -255,15 +262,7 @@ env.Default(instnode) test.fail_test(wrong_symlinks) # modify test.c and make sure it can recompile when links already exist - test.write('test.c', """\ - #if _WIN32 - __declspec(dllexport) - #endif - int testlib(int n) - { - return n+11 ; - } - """) + test.write('test.c', test_c_src2) test.run() -- cgit v0.12 From ffcb8963212db0e83b79c9cbb584ed6ff3da837a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tomulik?= Date: Sun, 20 Sep 2015 19:55:34 +0200 Subject: cyglink: fix for shlib symlink not being cleaned correctly --- src/engine/SCons/Tool/__init__.py | 8 ++++++-- src/engine/SCons/Tool/cyglink.py | 2 +- test/LINK/VersionedLib.py | 6 ------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 96b9d98..7374687 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -596,19 +596,23 @@ def StringizeLibSymlinks(symlinks): else: return symlinks -def EmitLibSymlinks(env, symlinks, libnode): +def EmitLibSymlinks(env, symlinks, libnode, **kw): """Used by emitters to handle (shared/versioned) library symlinks""" Verbose = False # nodes involved in process... all symlinks + library nodes = list(set([ x for x,y in symlinks ] + [libnode])) + + clean_targets = kw.get('clean_targets', []) + if not SCons.Util.is_List(clean_targets): + clean_targets = [ clean_targets ] for link, linktgt in symlinks: env.SideEffect(link, linktgt) if(Verbose): print "EmitLibSymlinks: SideEffect(%r,%r)" % (link.get_path(), linktgt.get_path()) clean_list = filter(lambda x : x != linktgt, nodes) - env.Clean(linktgt, clean_list) + env.Clean(list(set([linktgt] + clean_targets)), clean_list) if(Verbose): print "EmitLibSymlinks: Clean(%r,%r)" % (linktgt.get_path(), map(lambda x : x.get_path(), clean_list)) diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index e05e85f..deeb410 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -111,7 +111,7 @@ def _lib_emitter(target, source, env, **kw): if Verbose: print "_lib_emitter: implib symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) if symlinks: - SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target) + SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target, clean_targets = target[0]) implib_target.attributes.shliblinks = symlinks return (target, source) diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py index 0d74a35..360fa24 100644 --- a/test/LINK/VersionedLib.py +++ b/test/LINK/VersionedLib.py @@ -86,12 +86,6 @@ elif platform == 'darwin': elif platform == 'cygwin': test_plan = [ { - 'libversion' : '2.5.4', - 'files' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a', 'test.os' ], - 'instfiles' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a' ], - 'symlinks' : [], - }, - { 'libversion' : '2', 'files' : [ 'cygtest-2.dll', 'libtest-2.dll.a', 'libtest.dll.a', 'test.os' ], 'instfiles' : [ 'cygtest-2.dll', 'libtest-2.dll.a', 'libtest.dll.a' ], -- cgit v0.12 From 11711340ea36b162fcef4462f4f01d1d223700cf Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 21:37:39 +0200 Subject: s/SHLINKSONAME/SHLIBSONAME/ --- src/engine/SCons/Defaults.py | 1 + src/engine/SCons/Tool/cyglink.py | 2 +- src/engine/SCons/Tool/gdc.py | 2 +- src/engine/SCons/Tool/gnulink.xml | 2 +- src/engine/SCons/Tool/link.py | 6 +++--- src/engine/SCons/Tool/link.xml | 4 ++-- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index c8170c3..744da5f 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -512,6 +512,7 @@ ConstructionEnvironment = { '__libversionflags' : __libversionflags, '__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', '__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', + '__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', 'TEMPFILE' : NullCmdGenerator, 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index deeb410..5230910 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -216,7 +216,7 @@ def generate(env): } # these variables were set by gnulink but are not used in cyglink - try: del env['_SHLINKSONAME'] + try: del env['_SHLIBSONAME'] except KeyError: pass try: del env['_LDMODULESONAME'] except KeyError: pass diff --git a/src/engine/SCons/Tool/gdc.py b/src/engine/SCons/Tool/gdc.py index 799c3ab..347efef 100644 --- a/src/engine/SCons/Tool/gdc.py +++ b/src/engine/SCons/Tool/gdc.py @@ -97,7 +97,7 @@ def generate(env): env['DSHLINK'] = '$DC' env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared') - env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr' env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLINKLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '') diff --git a/src/engine/SCons/Tool/gnulink.xml b/src/engine/SCons/Tool/gnulink.xml index 63ce0f4..0e055c7 100644 --- a/src/engine/SCons/Tool/gnulink.xml +++ b/src/engine/SCons/Tool/gnulink.xml @@ -34,7 +34,7 @@ Set construction variables for GNU linker/loader. RPATHPREFIX RPATHSUFFIX _LDMODULESONAME -_SHLINKSONAME +_SHLIBSONAME LDMODULEVERSIONFLAGS SHLIBVERSIONFLAGS diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index 6bd36e4..a4a2a4c 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -256,12 +256,12 @@ def _setup_versioned_lib_variables(env, **kw): if use_soname: # If the linker uses SONAME, then we need this little automata if tool == 'sunlink': - env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLINKSONAME' + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLIBSONAME' env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME' else: - env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLINKSONAME' + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME' env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME' - env['_SHLINKSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' + env['_SHLIBSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}' env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator diff --git a/src/engine/SCons/Tool/link.xml b/src/engine/SCons/Tool/link.xml index 52349da..2f913fe 100644 --- a/src/engine/SCons/Tool/link.xml +++ b/src/engine/SCons/Tool/link.xml @@ -107,13 +107,13 @@ This macro automatically introduces extra flags to &cv-link-SHLINKCOM; when building versioned &b-link-SharedLibrary; (that is when &cv-link-SHLIBVERSION; is set). _SHLIBVERSIONFLAGS usually adds &cv-link-SHLIBVERSIONFLAGS; and some extra dynamically generated options (such as --Wl,-soname=$_SHLINKSONAME. It is unused by "plain" +-Wl,-soname=$_SHLIBSONAME. It is unused by "plain" (unversioned) shared libraries. - + A macro that automatically generates shared library's SONAME based on $TARGET, -- cgit v0.12 From 0662dc87640b0b2b42e51b87aa611e666c8a99eb Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Sun, 20 Sep 2015 22:22:10 +0200 Subject: gdc: support soname in D versioned libraries --- src/engine/SCons/Defaults.py | 1 - src/engine/SCons/Tool/gdc.py | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 744da5f..c8170c3 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -512,7 +512,6 @@ ConstructionEnvironment = { '__libversionflags' : __libversionflags, '__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', '__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', - '__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', 'TEMPFILE' : NullCmdGenerator, 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), diff --git a/src/engine/SCons/Tool/gdc.py b/src/engine/SCons/Tool/gdc.py index 347efef..255e6f1 100644 --- a/src/engine/SCons/Tool/gdc.py +++ b/src/engine/SCons/Tool/gdc.py @@ -97,7 +97,15 @@ def generate(env): env['DSHLINK'] = '$DC' env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared') - env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + # NOTE: using $__SHLIBVERSIONFLAGS here is just a quick hack. The macro + # $__SHLIBVERSIONFLAGS involves _SHLIBVERSIONFLAGS, which are set by + # current linker tool. If the current linker tool is not same as that used + # by gdc, and SHLIBVERSION is defined, an invalid flags may be generated + # for the gdc linker. It looks like the D tools should define its own + # set of variables (__DSHLIBVERSIONFLAGS, _DSHLIBVERSIONFLAGS, + # DSHLIBVERSIONFLAGS, DSHLIBVERSION, etc...) and duplicate the versioning + # machinery. + env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr' env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLINKLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '') -- cgit v0.12 From 48fcd009ca14235134b8bcc45e9fef7dde472a70 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Mon, 21 Sep 2015 00:59:51 +0200 Subject: add simple test for $__SHLIBVERSIONFLAGS --- test/LINK/SHLIBVERSIONFLAGS.py | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 test/LINK/SHLIBVERSIONFLAGS.py diff --git a/test/LINK/SHLIBVERSIONFLAGS.py b/test/LINK/SHLIBVERSIONFLAGS.py new file mode 100644 index 0000000..d8fd2e6 --- /dev/null +++ b/test/LINK/SHLIBVERSIONFLAGS.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import re + +import TestSCons +import SCons.Platform +import SCons.Defaults + +linkers = [ 'gnulink', 'cyglink', 'sunlink' ] + +foo_c_src = "void foo() {}\n" + +env = SCons.Defaults.DefaultEnvironment() +platform = SCons.Platform.platform_default() +tool_list = SCons.Platform.DefaultToolList(platform, env) + +test = TestSCons.TestSCons() +test.write('foo.c', foo_c_src) +test.write('SConstruct', "SharedLibrary('foo','foo.c',SHLIBVERSION='1.2.3')\n") + +if 'gnulink' in tool_list: + test.run(stdout = r".+ -Wl,-Bsymbolic -Wl,-soname=libfoo.so.1( .+)+", match = TestSCons.match_re_dotall) + test.run(arguments = ['-c']) +elif 'sunlink' in tool_list: + test.run(stdout = r".+ -h libfoo.so.1( .+)+", match = TestSCons.match_re_dotall) + test.run(arguments = ['-c']) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12 From c9aab89dd20ec11842c0183cf3c68fa5b8870155 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Mon, 21 Sep 2015 01:55:43 +0200 Subject: add SONAME support to D tools --- src/engine/SCons/Defaults.py | 1 + src/engine/SCons/Tool/dmd.py | 13 ++++++++++++- src/engine/SCons/Tool/gdc.py | 21 ++++++++++++--------- src/engine/SCons/Tool/ldc.py | 14 +++++++++++++- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index c8170c3..744da5f 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -512,6 +512,7 @@ ConstructionEnvironment = { '__libversionflags' : __libversionflags, '__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', '__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', + '__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', 'TEMPFILE' : NullCmdGenerator, 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), diff --git a/src/engine/SCons/Tool/dmd.py b/src/engine/SCons/Tool/dmd.py index a7d46c6..3722936 100644 --- a/src/engine/SCons/Tool/dmd.py +++ b/src/engine/SCons/Tool/dmd.py @@ -114,7 +114,7 @@ def generate(env): env['DSHLINK'] = '$DC' env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared -defaultlib=libphobos2.so') - env['SHDLINKCOM'] = '$DLINK -of$TARGET $DSHLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' + env['SHDLINKCOM'] = '$DLINK -of$TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l' env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else '' @@ -139,6 +139,17 @@ def generate(env): env['DRPATHSUFFIX'] = '' env['_DRPATH'] = '${_concat(DRPATHPREFIX, RPATH, DRPATHSUFFIX, __env__)}' + # Support for versioned libraries + env['_DSHLIBVERSIONFLAGS'] = '$DSHLIBVERSIONFLAGS -L-soname=$_DSHLIBSONAME' + env['_DSHLIBSONAME'] = '${DShLibSonameGenerator(__env__,TARGET)}' + # NOTE: this is a quick hack, the soname will only work if there is + # c/c++ linker loaded which provides callback for the ShLibSonameGenerator + env['DShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator + # NOTE: this is only for further reference, currently $DSHLIBVERSION does + # not work, the user must use $SHLIBVERSION + env['DSHLIBVERSION'] = '$SHLIBVERSION' + env['DSHLIBVERSIONFLAGS'] = [] + SCons.Tool.createStaticLibBuilder(env) diff --git a/src/engine/SCons/Tool/gdc.py b/src/engine/SCons/Tool/gdc.py index 255e6f1..32199b3 100644 --- a/src/engine/SCons/Tool/gdc.py +++ b/src/engine/SCons/Tool/gdc.py @@ -97,15 +97,7 @@ def generate(env): env['DSHLINK'] = '$DC' env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared') - # NOTE: using $__SHLIBVERSIONFLAGS here is just a quick hack. The macro - # $__SHLIBVERSIONFLAGS involves _SHLIBVERSIONFLAGS, which are set by - # current linker tool. If the current linker tool is not same as that used - # by gdc, and SHLIBVERSION is defined, an invalid flags may be generated - # for the gdc linker. It looks like the D tools should define its own - # set of variables (__DSHLIBVERSIONFLAGS, _DSHLIBVERSIONFLAGS, - # DSHLIBVERSIONFLAGS, DSHLIBVERSION, etc...) and duplicate the versioning - # machinery. - env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr' env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLINKLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '') @@ -123,6 +115,17 @@ def generate(env): env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + # Support for versioned libraries + env['_DSHLIBVERSIONFLAGS'] = '$DSHLIBVERSIONFLAGS -Wl,-soname=$_DSHLIBSONAME' + env['_DSHLIBSONAME'] = '${DShLibSonameGenerator(__env__,TARGET)}' + # NOTE: this is a quick hack, the soname will only work if there is + # c/c++ linker loaded which provides callback for the ShLibSonameGenerator + env['DShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator + # NOTE: this is only for further reference, currently $DSHLIBVERSION does + # not work, the user must use $SHLIBVERSION + env['DSHLIBVERSION'] = '$SHLIBVERSION' + env['DSHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' + SCons.Tool.createStaticLibBuilder(env) diff --git a/src/engine/SCons/Tool/ldc.py b/src/engine/SCons/Tool/ldc.py index 8f9b117..a3c1275 100644 --- a/src/engine/SCons/Tool/ldc.py +++ b/src/engine/SCons/Tool/ldc.py @@ -105,7 +105,7 @@ def generate(env): # Hack for Fedora the packages of which use the wrong name :-( if os.path.exists('/usr/lib64/libphobos-ldc.so') or os.path.exists('/usr/lib32/libphobos-ldc.so') or os.path.exists('/usr/lib/libphobos-ldc.so') : env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared -defaultlib=phobos-ldc') - env['SHDLINKCOM'] = '$DLINK -of=$TARGET $DSHLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' + env['SHDLINKCOM'] = '$DLINK -of=$TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l' env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else '' @@ -131,6 +131,18 @@ def generate(env): env['DRPATHSUFFIX'] = '' env['_DRPATH'] = '${_concat(DRPATHPREFIX, RPATH, DRPATHSUFFIX, __env__)}' + # Support for versioned libraries + env['_DSHLIBVERSIONFLAGS'] = '$DSHLIBVERSIONFLAGS -L-soname=$_DSHLIBSONAME' + env['_DSHLIBSONAME'] = '${DShLibSonameGenerator(__env__,TARGET)}' + # NOTE: this is a quick hack, the soname will only work if there is + # c/c++ linker loaded which provides callback for the ShLibSonameGenerator + env['DShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator + # NOTE: this is only for further reference, currently $DSHLIBVERSION does + # not work, the user must use $SHLIBVERSION + env['DSHLIBVERSION'] = '$SHLIBVERSION' + env['DSHLIBVERSIONFLAGS'] = [] + print "AKUKU: %r" % env.subst('$_DSHLIBVERSIONFLAGS') + SCons.Tool.createStaticLibBuilder(env) -- cgit v0.12 From 810d9787e113ec41451bb4722a764f551cb35178 Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Mon, 21 Sep 2015 02:03:24 +0200 Subject: remove debug message from ldc.py --- src/engine/SCons/Tool/ldc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/SCons/Tool/ldc.py b/src/engine/SCons/Tool/ldc.py index a3c1275..ade95db 100644 --- a/src/engine/SCons/Tool/ldc.py +++ b/src/engine/SCons/Tool/ldc.py @@ -141,7 +141,6 @@ def generate(env): # not work, the user must use $SHLIBVERSION env['DSHLIBVERSION'] = '$SHLIBVERSION' env['DSHLIBVERSIONFLAGS'] = [] - print "AKUKU: %r" % env.subst('$_DSHLIBVERSIONFLAGS') SCons.Tool.createStaticLibBuilder(env) -- cgit v0.12 From 30664716b8dd63de48ab40865c48db8580f4956b Mon Sep 17 00:00:00 2001 From: Pawel Tomulik Date: Mon, 21 Sep 2015 02:28:58 +0200 Subject: correct logic in VersionedLib*.py tests --- test/LINK/VersionedLib-VariantDir.py | 13 ++++++++----- test/LINK/VersionedLib-j2.py | 13 ++++++++----- test/LINK/VersionedLib-subdir.py | 17 ++++++++++------- test/LINK/VersionedLib.py | 15 ++++++++------- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/test/LINK/VersionedLib-VariantDir.py b/test/LINK/VersionedLib-VariantDir.py index 7406a33..0a631b0 100644 --- a/test/LINK/VersionedLib-VariantDir.py +++ b/test/LINK/VersionedLib-VariantDir.py @@ -33,8 +33,11 @@ import os import sys import SCons.Platform +import SCons.Defaults +env = SCons.Defaults.DefaultEnvironment() platform = SCons.Platform.platform_default() +tool_list = SCons.Platform.DefaultToolList(platform, env) test = TestSCons.TestSCons() @@ -96,7 +99,7 @@ if sys.platform.find('irix') != -1: test.run(program = test.workpath('build/bin/main')) -if platform == 'posix': +if 'gnulink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.so', @@ -104,14 +107,14 @@ if platform == 'posix': 'libfoo.so.0.1.2', ] obj = 'foo.os' -elif platform == 'darwin': +elif 'applelink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.dylib', 'libfoo.0.1.2.dylib', ] obj = 'foo.os' -elif platform == 'cygwin': +elif 'cyglink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'cygfoo-0-1-2.dll', @@ -119,14 +122,14 @@ elif platform == 'cygwin': 'libfoo.dll.a', ] obj = 'foo.os' -elif platform == 'win32': +elif 'mslink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'foo.dll', 'foo.lib', ] obj = 'foo.obj' -elif platform == 'sunos': +elif 'sunlink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.so', diff --git a/test/LINK/VersionedLib-j2.py b/test/LINK/VersionedLib-j2.py index 6f37e54..249b54f 100644 --- a/test/LINK/VersionedLib-j2.py +++ b/test/LINK/VersionedLib-j2.py @@ -35,6 +35,7 @@ import os import sys import SCons.Platform +import SCons.Defaults test = TestSCons.TestSCons() @@ -63,7 +64,9 @@ env.SharedLibrary('foo', 'foo.c', SHLIBVERSION = '0.1.2') test.run(arguments = ['-j 2', '--tree=all']) +env = SCons.Defaults.DefaultEnvironment() platform = SCons.Platform.platform_default() +tool_list = SCons.Platform.DefaultToolList(platform, env) if platform == 'cygwin': # PATH is used to search for *.dll librarier (cygfoo-0-2-1.dll in our case) @@ -83,7 +86,7 @@ test.run(arguments = ['-c']) platform = SCons.Platform.platform_default() -if platform == 'posix': +if 'gnulink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.so', @@ -91,14 +94,14 @@ if platform == 'posix': 'libfoo.so.0.1.2', 'foo.os', ] -elif platform == 'darwin': +elif 'applelink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.dylib', 'libfoo.0.1.2.dylib', 'foo.os', ] -elif platform == 'cygwin': +elif 'cyglink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'cygfoo-0-1-2.dll', @@ -106,14 +109,14 @@ elif platform == 'cygwin': 'libfoo.dll.a', 'foo.os', ] -elif platform == 'win32': +elif 'mslink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'foo.dll', 'foo.lib', 'foo.obj', ] -elif platform == 'sunos': +elif 'sunlink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.so', diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py index 15369ef..a2e141b 100644 --- a/test/LINK/VersionedLib-subdir.py +++ b/test/LINK/VersionedLib-subdir.py @@ -37,6 +37,7 @@ import os import sys import SCons.Platform +import SCons.Defaults test = TestSCons.TestSCons() @@ -58,11 +59,13 @@ int main() } """) +env = SCons.Defaults.DefaultEnvironment() platform = SCons.Platform.platform_default() +tool_list = SCons.Platform.DefaultToolList(platform, env) -if platform == 'darwin': +if 'applelink' in tool_list: subdir = 'blah.0.1.2.dylib.blah' -elif platform == 'cygwin': +elif 'cyglink' in tool_list: subdir = 'blah-0-1-2.dll.a.blah' else: subdir = 'blah.so.0.1.2.blah' @@ -90,7 +93,7 @@ if sys.platform.find('irix') != -1: test.run(program = test.workpath('main')) -if platform == 'posix': +if 'gnulink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.so', @@ -98,14 +101,14 @@ if platform == 'posix': 'libfoo.so.0.1.2', ] obj = 'foo.os' -elif platform == 'darwin': +elif 'applelink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.dylib', 'libfoo.0.1.2.dylib', ] obj = 'foo.os' -elif platform == 'cygwin': +elif 'cyglink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'cygfoo-0-1-2.dll', @@ -113,14 +116,14 @@ elif platform == 'cygwin': 'libfoo.dll.a', ] obj = 'foo.os' -elif platform == 'win32': +elif 'mslink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'foo.dll', 'foo.lib', ] obj = 'foo.obj' -elif platform == 'sunos': +elif 'sunlink' in tool_list: # All (?) the files we expect will get created in the current directory files = [ 'libfoo.so', diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py index 360fa24..599c8aa 100644 --- a/test/LINK/VersionedLib.py +++ b/test/LINK/VersionedLib.py @@ -29,12 +29,13 @@ import sys import TestSCons import SCons.Platform +import SCons.Defaults -_exe = TestSCons._exe - +env = SCons.Defaults.DefaultEnvironment() platform = SCons.Platform.platform_default() +tool_list = SCons.Platform.DefaultToolList(platform, env) -if platform == 'posix': +if 'gnulink' in tool_list: test_plan = [ { 'libversion' : '2', @@ -73,7 +74,7 @@ if platform == 'posix': 'symlinks' : [ ('libtest.so', 'libtest.so.2.dfffa11'), ('libtest.so.2', 'libtest.so.2.dfffa11') ], }, ] -elif platform == 'darwin': +elif 'applelink' in tool_list: # All (?) the files we expect will get created in the current directory test_plan = [ { @@ -83,7 +84,7 @@ elif platform == 'darwin': 'symlinks' : [], }, ] -elif platform == 'cygwin': +elif 'cyglink' in tool_list: test_plan = [ { 'libversion' : '2', @@ -122,7 +123,7 @@ elif platform == 'cygwin': 'symlinks' : [ ('libtest.dll.a', 'libtest-2-dfffa11.dll.a') ], }, ] -elif platform == 'win32': +elif 'mslink' in tool_list: test_plan = [ { 'libversion' : '2.5.4', @@ -131,7 +132,7 @@ elif platform == 'win32': 'symlinks' : [], }, ] -elif platform == 'sunos': +elif 'sunlink' in tool_list: test_plan = [ { 'libversion' : '2', -- cgit v0.12 From e902d86ed981c406ce5a8d120c46f580d8ef0717 Mon Sep 17 00:00:00 2001 From: ptomulik Date: Tue, 22 Sep 2015 12:57:48 +0200 Subject: add blurb to src/CHANGES.txt --- src/CHANGES.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 977d00f..860df99 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -6,6 +6,33 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER + From Paweł Tomulik: + - Reimplemented versioning for shared libraries, with the following effects + - Fixed tigris issues #3001, #3006. + - Fixed several other issues not reported to tigris, including: + issues with versioned libraries in subdirectories with tricky names, + issues with versioned libraries and variant directories, + issue with soname not being injected to library when using D linkers, + - Switched to direct symlinks instead of daisy-chained ones -- soname and + development symlinks point directly to the versioned shared library now), + for rationale see: + https://www.debian.org/doc/debian-policy/ch-sharedlibs.html + https://fedoraproject.org/wiki/Packaging:Guidelines#Devel_Packages + https://bitbucket.org/scons/scons/pull-requests/247/new-versioned-libraries-gnulink-cyglink/diff#comment-10063929 + - New construction variables to allow override default behavior: SONAME, + SHLIBVERSIONFLAGS, _SHLIBVERSIONFLAGS, SHLIBNOVERSIONSYMLINKS, + LDMODULEVERSION, LDMODULEVERSIONFLAGS, _LDMODULEVERSIONFLAGS, + LDMODULENOVERSIONSYMLINKS. + - Changed logic used to configure the versioning machinery from + platform-centric to linker-oriented. + - The SHLIBVERSION/LDMODULEVERSION variables are no longer validated by + SCons (more freedom to users). + - InstallVersionedLib() doesn't use SHLIBVERSION anymore. + - Enchanced docs for the library versioning stuff. + - New tests for versioned libraries. + - Library versioning is currently implemented for the following linker + tools: 'cyglink', 'gnulink', 'sunlink'. + From Dirk Baechle: - Switched several core classes to using "slots", for reducing the overall memory consumption in large -- cgit v0.12