diff options
author | William Deegan <bill@baddogconsulting.com> | 2015-09-22 19:28:41 (GMT) |
---|---|---|
committer | William Deegan <bill@baddogconsulting.com> | 2015-09-22 19:28:41 (GMT) |
commit | a60fae3f33db50836fb56225b71db6333da9e5cf (patch) | |
tree | b6394d1d55ebf19a4455a977232657876ebed79e /src | |
parent | 9fee88e50ebc808dff8aae58fea35b354af83d0a (diff) | |
parent | e902d86ed981c406ce5a8d120c46f580d8ef0717 (diff) | |
download | SCons-a60fae3f33db50836fb56225b71db6333da9e5cf.zip SCons-a60fae3f33db50836fb56225b71db6333da9e5cf.tar.gz SCons-a60fae3f33db50836fb56225b71db6333da9e5cf.tar.bz2 |
Merged in ptomulik/scons2 (pull request #247)
Diffstat (limited to 'src')
24 files changed, 1141 insertions, 369 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index fd617cb..802ca5e 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -6,10 +6,32 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - From John Doe: - - - Whatever John Doe did. - + 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'. RELEASE 2.4.0 - Mon, 21 Sep 2015 08:56:00 -0700 diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 6500443..744da5f 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -482,6 +482,15 @@ class Variable_Method_Caller(object): frame = frame.f_back return None +# 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' : {}, 'SCANNERS' : [], @@ -499,6 +508,12 @@ 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__)}', + + '__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'), '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..7374687 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -236,150 +236,436 @@ def createStaticLibBuilder(env): return static_lib -def VersionShLibLinkNames(version, libname, env): - """Generate names of symlinks to the versioned shared library""" +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 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""" + 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 '_call_linker_cb: args=%r' % args + print '_call_linker_cb: callback=%r' % callback + + try: + cbfun = env['LINKCALLBACKS'][callback] + except (KeyError, TypeError): 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) + print '_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' % callback + pass + else: 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, ]: + 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 "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""" - Verbose = False - try: - version = env.subst('$SHLIBVERSION') - except KeyError: - version = None + print '_call_linker_cb: env["LINKCALLBACKS"][%r] is callable' % callback + result = cbfun(env, *args) + return result - # 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' ] +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): + try: + support_class = self._support_classes[libtype] + except KeyError: + raise ValueError('unsupported libtype %r' % libtype) + self._support = support_class() + + def get_libtype(self): + return self._support.get_libtype() + + def set_infoname(self, infoname): + self.infoname = infoname + + def get_infoname(self): + return self.infoname + + def get_lib_prefix(self, env, *args, **kw): + return self._support.get_lib_prefix(env,*args,**kw) + + def get_lib_suffix(self, env, *args, **kw): + return self._support.get_lib_suffix(env,*args,**kw) + + 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() + infoname = self.get_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_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 + + 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 "VersionShLib: shlink_flags = ",shlink_flags - envlink = env.Clone() - envlink['SHLINKFLAGS'] = shlink_flags - else: - envlink = env + print "_LibPrefixGenerator: input prefix=%r" % prefix + + version = self.get_lib_version(env, **kw2) + if Verbose: + print "_LibPrefixGenerator: version=%r" % version - result = SCons.Defaults.ShLinkAction(target, source, envlink) + if version: + prefix = self.generate_versioned_lib_info(env, [prefix, version], prefix, **kw2) - 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 "_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 + LoadableModule builders""" + def __init__(self, libtype): + super(_LibSuffixGenerator, self).__init__(libtype, 'Suffix') + + def __call__(self, env, sources = None, **kw): + Verbose = False + + 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 "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: + print "_LibSuffixGenerator: input suffix=%r" % suffix + + 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, **kw2) + + if Verbose: + 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 list of symlinks that + should be created by SharedLibrary or LoadableModule builders""" + def __init__(self, libtype): + super(_LibSymlinkGenerator, self).__init__(libtype, 'Symlinks') + + 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, **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,**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) + return symlinks + +ShLibSymlinkGenerator = _LibSymlinkGenerator('ShLib') +LdModSymlinkGenerator = _LibSymlinkGenerator('LdMod') +ImpLibSymlinkGenerator = _LibSymlinkGenerator('ImpLib') + +class _LibNameGenerator(_LibInfoGeneratorBase): + """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') + + def __call__(self, env, libnode, **kw): + """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, **kw2) + if Verbose: + print '_LibNameGenerator: version=%r' % version + + name = None + if version: + 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()) + + 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 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 = _call_env_subst(env, '$SONAME', **kw2) + if not soname: + version = self.get_lib_version(env,**kw2) + 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, prefix, suffix], **kw2) + + 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 + + return soname + +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, ValueError): + return symlinks + else: + return symlinks + +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(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)) + +def CreateLibSymlinks(env, symlinks): + """Physically creates symlinks. The symlinks argument must be a list in + form [ (link, linktarget), ... ], where link and linktarget are SCons + nodes. + """ + + Verbose = False + for link, linktgt in symlinks: + linktgt = link.get_dir().rel_path(linktgt) + link = link.get_path() + if(Verbose): + print "CreateLibSymlinks: preparing to add symlink %r -> %r" % (link, linktgt) + # 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 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 + +def LibSymlinksActionFunction(target, source, env): + for tgt in target: + symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None) + if symlinks: + CreateLibSymlinks(env, symlinks) + return 0 + +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: - os.remove(lastlinkname) - except: + linkstr = ', '.join([ "%r->%r" %(k,v) for k,v in StringizeLibSymlinks(symlinks)]) + except (KeyError, ValueError): pass - os.symlink(lib_ver,lastlinkname) - if Verbose: - print "VerShLib: made sym link of %s -> %s" % (linkname, lib_ver) - return result + else: + cmd += ": %s" % linkstr + return cmd + -# 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']) +LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun) def createSharedLibBuilder(env): """This is a utility function that creates the SharedLibrary @@ -393,11 +679,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', + prefix = ShLibPrefixGenerator, + suffix = ShLibSuffixGenerator, target_scanner = ProgramScanner, src_suffix = '$SHOBJSUFFIX', src_builder = 'SharedObject') @@ -417,11 +704,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', + prefix = LdModPrefixGenerator, + 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..7f19bfc 100644 --- a/src/engine/SCons/Tool/__init__.xml +++ b/src/engine/SCons/Tool/__init__.xml @@ -464,6 +464,17 @@ as C++ files. </summary> </cvar> +<cvar name="IMPLIBVERSION"> +<summary> +<para> +Used to override &cv-link-SHLIBVERSION;/&cv-link-LDMODULEVERSION; when +generating versioned import library for a shared library/loadable module. If +undefined, the &cv-link-SHLIBVERSION;/&cv-link-LDMODULEVERSION; is used to +determine the version of versioned import library. +</para> +</summary> +</cvar> + <cvar name="LIBEMITTER"> <summary> <para> @@ -472,6 +483,21 @@ TODO </summary> </cvar> +<cvar name="LDMODULEVERSION"> +<summary> +<para> +When this construction variable is defined, a versioned loadable module +is created by &b-link-LoadableModule; builder. This activates the +&cv-link-_LDMODULEVERSIONFLAGS; and thus modifies the &cv-link-LDMODULECOM; as +required, adds the version number to the library name, and creates the symlinks +that are needed. &cv-link-LDMODULEVERSION; needs to be of the form X.Y.Z, where +X and Y are numbers, and Z is a number but can also contain letters to +designate alpha, beta, or release candidate patch levels. By default +&cv-link-LDMODULEVERSION; is set to $SHLIBVERSION. +</para> +</summary> +</cvar> + <cvar name="SHLIBEMITTER"> <summary> <para> @@ -492,11 +518,12 @@ TODO <summary> <para> When this construction variable is defined, a versioned shared library -is created. This modifies the &cv-link-SHLINKFLAGS; as required, adds -the version number to the library name, and creates the symlinks that -are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z, -where X and Y are numbers, and Z is a number but can also contain -letters to designate alpha, beta, or release candidate patch levels. +is created by &b-link-SharedLibrary; builder. This activates the +&cv-link-_SHLIBVERSIONFLAGS; and thus modifies the &cv-link-SHLINKCOM; as +required, adds the version number to the library name, and creates the symlinks +that are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z, where X +and Y are numbers, and Z is a number but can also contain letters to designate +alpha, beta, or release candidate patch levels. </para> </summary> </cvar> diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index 87716cf..5230910 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -7,19 +7,27 @@ 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 +import link -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,37 +43,141 @@ 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 + + if Verbose: + print "_lib_emitter: target[0]=%r" % target[0].get_path() + + 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 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("$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:] + 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], - 'SHLIBPREFIX', 'SHLIBSUFFIX', + '%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" % implib_target.get_path() 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" % SCons.Tool.StringizeLibSymlinks(symlinks) + if symlinks: + SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target, clean_targets = target[0]) + 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, 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, prefix, suffix, **kw): + """Generate link names that should be created for a versioned shared lirbrary. + Returns a list in the form [ (link, linktarget), ... ] + """ + Verbose = False + + if Verbose: + print "_versioned_implib_symlinks: libnode=%r" % libnode.get_path() + print "_versioned_implib_symlinks: version=%r" % version + + try: libtype = kw['libtype'] + except KeyError: libtype = 'ShLib' + + + linkdir = os.path.dirname(libnode.get_path()) + 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 = env.fs.File(os.path.join(linkdir, name)) + symlinks = [(link0, libnode)] + + if Verbose: + print "_versioned_implib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(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 +186,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 +196,31 @@ 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... + + # 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 + try: del env['_SHLIBSONAME'] + except KeyError: pass + try: del env['_LDMODULESONAME'] + except KeyError: pass + def exists(env): return gnulink.exists(env) diff --git a/src/engine/SCons/Tool/cyglink.xml b/src/engine/SCons/Tool/cyglink.xml new file mode 100644 index 0000000..42208f1 --- /dev/null +++ b/src/engine/SCons/Tool/cyglink.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +__COPYRIGHT__ + +This file is processed by the bin/SConsDoc.py module. +See its __doc__ string for a discussion of the format. +--> + +<!DOCTYPE sconsdoc [ +<!ENTITY % scons SYSTEM '../../../../doc/scons.mod'> +%scons; +<!ENTITY % builders-mod SYSTEM '../../../../doc/generated/builders.mod'> +%builders-mod; +<!ENTITY % functions-mod SYSTEM '../../../../doc/generated/functions.mod'> +%functions-mod; +<!ENTITY % tools-mod SYSTEM '../../../../doc/generated/tools.mod'> +%tools-mod; +<!ENTITY % variables-mod SYSTEM '../../../../doc/generated/variables.mod'> +%variables-mod; +]> + +<sconsdoc xmlns="http://www.scons.org/dbxsd/v1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> + +<tool name="cyglink"> +<summary> +<para> +Set construction variables for cygwin linker/loader. +</para> +</summary> +<sets> +<item>IMPLIBPREFIX</item> +<item>IMPLIBSUFFIX</item> +<item>LDMODULEVERSIONFLAGS</item> +<item>LINKFLAGS</item> +<item>RPATHPREFIX</item> +<item>RPATHSUFFIX</item> +<item>SHLIBPREFIX</item> +<item>SHLIBSUFFIX</item> +<item>SHLIBVERSIONFLAGS</item> +<item>SHLINKCOM</item> +<item>SHLINKFLAGS</item> +<item>_LDMODULEVERSIONFLAGS</item> +<item>_SHLIBVERSIONFLAGS</item> +</sets> +</tool> + +</sconsdoc> diff --git a/src/engine/SCons/Tool/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 799c3ab..32199b3 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 '') @@ -115,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/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 3dc8f51..6b0d5b3 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -34,9 +34,14 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Util +import SCons.Tool +import os +import sys +import re import link + def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" link.generate(env) @@ -49,6 +54,14 @@ def generate(env): env['RPATHPREFIX'] = '-Wl,-rpath=' env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + + # 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() + + # 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/gnulink.xml b/src/engine/SCons/Tool/gnulink.xml index 2a36de2..0e055c7 100644 --- a/src/engine/SCons/Tool/gnulink.xml +++ b/src/engine/SCons/Tool/gnulink.xml @@ -33,6 +33,10 @@ Set construction variables for GNU linker/loader. <item>SHLINKFLAGS</item> <item>RPATHPREFIX</item> <item>RPATHSUFFIX</item> +<item>_LDMODULESONAME</item> +<item>_SHLIBSONAME</item> +<item>LDMODULEVERSIONFLAGS</item> +<item>SHLIBVERSIONFLAGS</item> </sets> </tool> diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 9f2e937..9d5db9f 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,35 @@ 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.""" +def listShlibLinksToInstall(dest, source, env): + install_links = [] + source = env.arg2nodes(source) + dest = env.fs.File(dest) + install_dir = dest.get_dir() + for src in source: + 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): + """If we are installing a versioned shared library create the required links.""" 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)) - 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] - + symlinks = listShlibLinksToInstall(dest, source, env) 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): - """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) + print 'installShlibLinks: symlinks=%r' % SCons.Tool.StringizeLibSymlinks(symlinks) + if symlinks: + SCons.Tool.CreateLibSymlinks(env, symlinks) return def installFunc(target, source, env): @@ -306,22 +244,11 @@ def add_versioned_targets_to_INSTALLED_FILES(target, source, env): Verbose = False _INSTALLED_FILES.extend(target) if Verbose: - print "ver lib emitter ",repr(target) - - # see if we have a versioned shared library, if so generate side effects - version, libname, install_dir = versionedLibVersion(target[0], source, env) - if version != None: - # generate list of link names - linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env) - for linkname in linknames: - if Verbose: - print "make side effect of %s" % os.path.join(install_dir, linkname) - fulllinkname = os.path.join(install_dir, linkname) - env.SideEffect(fulllinkname,target[0]) - env.Clean(target[0],fulllinkname) - _INSTALLED_FILES.append(fulllinkname) - if Verbose: - print "installed list ", _INSTALLED_FILES + print "add_versioned_targets_to_INSTALLED_FILES: target=%r" % map(str, target) + + symlinks = listShlibLinksToInstall(target[0], source, env) + if symlinks: + SCons.Tool.EmitLibSymlinks(env, symlinks, target[0]) _UNIQUE_INSTALLED_FILES = None return (target, source) diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml index 0aa9384..6ae3e30 100644 --- a/src/engine/SCons/Tool/install.xml +++ b/src/engine/SCons/Tool/install.xml @@ -82,20 +82,13 @@ env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], <builder name="InstallVersionedLib"> <summary> <para> -Installs a versioned shared library. The &cv-link-SHLIBVERSION; -construction variable should be defined in the environment -to confirm the version number in the library name. -If &cv-link-SHLIBVERSION; is not defined a warning will be issued -and the name of the library will be parsed to derive the version. -The symlinks appropriate to the architecture will be generated. +Installs a versioned shared library. The symlinks appropriate to the +architecture will be generated based on symlinks of the source library. </para> <example_commands> env.InstallVersionedLib(target = '/usr/local/bin/foo', source = 'libxyz.1.5.2.so') -env.InstallVersionedLib(target = '/usr/local/bin/foo', - source = 'libxyz.1.5.2.so', - SHLIBVERSION='1.5.2') </example_commands> </summary> </builder> diff --git a/src/engine/SCons/Tool/ldc.py b/src/engine/SCons/Tool/ldc.py index 8f9b117..ade95db 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,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/link.py b/src/engine/SCons/Tool/link.py index a084bc4..a4a2a4c 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,205 @@ 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: target[0]=%r" % target[0].get_path() 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, prefix, suffix, prefix_generator, 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: 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(libnode.get_path()) + if Verbose: + print "_versioned_lib_name: versioned_name=%r" % versioned_name + + 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_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, 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, prefix, suffix, **kw): + pg = SCons.Tool.LdModPrefixGenerator + 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 _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 $_SHLIBSONAME' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME' + else: + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME' + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME' + env['_SHLIBSONAME'] = '${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.""" @@ -171,7 +280,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 +305,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..2f913fe 100644 --- a/src/engine/SCons/Tool/link.xml +++ b/src/engine/SCons/Tool/link.xml @@ -41,11 +41,16 @@ Sets construction variables for generic POSIX linkers. <item>LIBLINKPREFIX</item> <item>LIBLINKSUFFIX</item> <item>SHLIBSUFFIX</item> +<item>__SHLIBVERSIONFLAGS</item> <item>LDMODULE</item> <item>LDMODULEPREFIX</item> <item>LDMODULESUFFIX</item> <item>LDMODULEFLAGS</item> <item>LDMODULECOM</item> +<item>LDMODULEVERSION</item> +<item>LDMODULENOVERSIONSYMLINKS</item> +<item>LDMODULEVERSIONFLAGS</item> +<item>__LDMODULEVERSIONFLAGS</item> </sets> <uses> <item>SHLINKCOMSTR</item> @@ -54,6 +59,105 @@ Sets construction variables for generic POSIX linkers. </uses> </tool> +<cvar name="__LDMODULEVERSIONFLAGS"> +<summary> +<para> +This construction variable automatically introduces &cv-link-_LDMODULEVERSIONFLAGS; +if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string. +</para> +</summary> +</cvar> + +<cvar name="__SHLIBVERSIONFLAGS"> +<summary> +<para> +This construction variable automatically introduces &cv-link-_SHLIBVERSIONFLAGS; +if &cv-link-SHLIBVERSION; is set. Othervise it evaluates to an empty string. +</para> +</summary> +</cvar> + +<cvar name="_LDMODULESONAME"> +<summary> +<para> +A macro that automatically generates loadable module's SONAME based on $TARGET, +$LDMODULEVERSION and $LDMODULESUFFIX. Used by &b-link-LoadableModule; builder +when the linker tool supports SONAME (e.g. &t-link-gnulink;). +</para> +</summary> +</cvar> + +<cvar name="_LDMODULEVERSIONFLAGS"> +<summary> +<para> +This macro automatically introduces extra flags to &cv-link-LDMODULECOM; when +building versioned &b-link-LoadableModule; (that is when +&cv-link-LDMODULEVERSION; is set). <literal>_LDMODULEVERSIONFLAGS</literal> +usually adds &cv-link-SHLIBVERSIONFLAGS; and some extra dynamically generated +options (such as <literal>-Wl,-soname=$_LDMODULESONAME</literal>). It is unused +by plain (unversioned) loadable modules. +</para> +</summary> +</cvar> + +<cvar name="_SHLIBVERSIONFLAGS"> +<summary> +<para> +This macro automatically introduces extra flags to &cv-link-SHLINKCOM; when +building versioned &b-link-SharedLibrary; (that is when &cv-link-SHLIBVERSION; +is set). <literal>_SHLIBVERSIONFLAGS</literal> usually adds &cv-link-SHLIBVERSIONFLAGS; +and some extra dynamically generated options (such as +<literal>-Wl,-soname=$_SHLIBSONAME</literal>. It is unused by "plain" +(unversioned) shared libraries. +</para> +</summary> +</cvar> + +<cvar name="_SHLIBSONAME"> +<summary> +<para> +A macro that automatically generates shared library's SONAME based on $TARGET, +$SHLIBVERSION and $SHLIBSUFFIX. Used by &b-link-SharedLibrary; builder when +the linker tool supports SONAME (e.g. &t-link-gnulink;). +</para> +</summary> +</cvar> + +<cvar name="IMPLIBPREFIX"> +<summary> +<para> +The prefix used for import library names. For example, cygwin uses import +libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries +(<literal>cygfoo.dll</literal>). The &t-link-cyglink; linker sets +&cv-link-IMPLIBPREFIX; to <literal>'lib'</literal> and &cv-link-SHLIBPREFIX; +to <literal>'cyg'</literal>. +</para> +</summary> +</cvar> + +<cvar name="IMPLIBSUFFIX"> +<summary> +<para> +The suffix used for import library names. For example, cygwin uses import +libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries +(<literal>cygfoo.dll</literal>). The &t-link-cyglink; linker sets +&cv-link-IMPLIBSUFFIX; to <literal>'.dll.a'</literal> and &cv-link-SHLIBSUFFIX; +to <literal>'.dll'</literal>. +</para> +</summary> +</cvar> + +<cvar name="IMPLIBNOVERSIONSYMLINKS"> +<summary> +<para> +Used to override &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; when +creating versioned import library for a shared library/loadable module. If not defined, +then &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; is used to determine +whether to disable symlink generation or not. +</para> +</summary> +</cvar> + <cvar name="LDMODULE"> <summary> <para> @@ -92,6 +196,15 @@ General user options passed to the linker for building loadable modules. </summary> </cvar> +<cvar name="LDMODULENOVERSIONSYMLINKS"> +<summary> +<para> +Instructs the &b-link-LoadableModule; builder to not automatically create symlinks +for versioned modules. Defaults to <literal>$SHLIBNOVERSIONSYMLINKS</literal> +</para> +</summary> +</cvar> + <cvar name="LDMODULEPREFIX"> <summary> <para> @@ -114,6 +227,16 @@ the same as $SHLIBSUFFIX. </summary> </cvar> +<cvar name="LDMODULEVERSIONFLAGS"> +<summary> +<para> +Extra flags added to &cv-link-LDMODULECOM; when building versioned +&b-link-LoadableModule;. These flags are only used when &cv-link-LDMODULEVERSION; is +set. +</para> +</summary> +</cvar> + <cvar name="LINK"> <summary> <para> @@ -169,6 +292,25 @@ for the variable that expands to library search path options. </summary> </cvar> +<cvar name="SHLIBNOVERSIONSYMLINKS"> +<summary> +<para> +Instructs the &b-link-SharedLibrary; builder to not create symlinks for versioned +shared libraries. +</para> +</summary> +</cvar> + +<cvar name="SHLIBVERSIONFLAGS"> +<summary> +<para> +Extra flags added to &cv-link-SHLINKCOM; when building versioned +&b-link-SharedLibrary;. These flags are only used when &cv-link-SHLIBVERSION; is +set. +</para> +</summary> +</cvar> + <cvar name="SHLINK"> <summary> <para> @@ -223,6 +365,18 @@ for the variable that expands to library search path options. </summary> </cvar> +<cvar name="SONAME"> +<summary> +<para> +Variable used to hard-code SONAME for versioned shared library/loadable module. +<example_commands> +env.SharedLibrary('test', 'test.c', SHLIBVERSION='0.1.2', SONAME='libtest.so.2') +</example_commands> +The variable is used, for example, by &t-link-gnulink; linker tool. +</para> +</summary> +</cvar> + <cvar name="STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"> <summary> <para> diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py index 9c58561..bd643f7 100644 --- a/src/engine/SCons/Tool/linkloc.py +++ b/src/engine/SCons/Tool/linkloc.py @@ -86,6 +86,7 @@ def generate(env): env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}' env['SHLIBEMITTER']= None + env['LDMODULEEMITTER']= None env['LINK'] = "linkloc" env['LINKFLAGS'] = SCons.Util.CLVar('') env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}' diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index 601ec3b..948ebe5 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -146,6 +146,7 @@ def generate(env): env['SHLINKCOM'] = shlib_action env['LDMODULECOM'] = shlib_action env.Append(SHLIBEMITTER = [shlib_emitter]) + env.Append(LDMODULEEMITTER = [shlib_emitter]) env['AS'] = 'as' env['WIN32DEFPREFIX'] = '' diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 6606e10..1390c20 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -263,6 +263,7 @@ def generate(env): env['_SHLINK_SOURCES'] = windowsShlinkSources env['SHLINKCOM'] = compositeShLinkAction env.Append(SHLIBEMITTER = [windowsLibEmitter]) + env.Append(LDMODULEEMITTER = [windowsLibEmitter]) env['LINK'] = 'link' env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') env['_PDB'] = pdbGenerator diff --git a/src/engine/SCons/Tool/mwld.py b/src/engine/SCons/Tool/mwld.py index e762d55..2067660 100644 --- a/src/engine/SCons/Tool/mwld.py +++ b/src/engine/SCons/Tool/mwld.py @@ -56,6 +56,7 @@ def generate(env): env['SHLINKFLAGS'] = '$LINKFLAGS' env['SHLINKCOM'] = shlib_action env['SHLIBEMITTER']= shlib_emitter + env['LDMODULEEMITTER']= shlib_emitter def exists(env): diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 716c7d5..7bc0ef6 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -320,6 +320,7 @@ def generate(env): # correctly later by our emitter. env.AppendUnique(PROGEMITTER =[AutomocStatic], SHLIBEMITTER=[AutomocShared], + LDMODULEEMITTER=[AutomocShared], LIBEMITTER =[AutomocStatic], # Of course, we need to link against the qt libraries CPPPATH=["$QT_CPPPATH"], diff --git a/src/engine/SCons/Tool/sunar.py b/src/engine/SCons/Tool/sunar.py index 779414f..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 $SHLINKFLAGS -o $TARGET $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. <item>AR</item> <item>ARFLAGS</item> <item>ARCOM</item> -<item>SHLINK</item> -<item>SHLINKFLAGS</item> -<item>SHLINKCOM</item> <item>LIBPREFIX</item> <item>LIBSUFFIX</item> </sets> <uses> <item>ARCOMSTR</item> -<item>SHLINKCOMSTR</item> </uses> </tool> diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py index 5996a30..680af03 100644 --- a/src/engine/SCons/Tool/sunlink.py +++ b/src/engine/SCons/Tool/sunlink.py @@ -66,6 +66,10 @@ def generate(env): env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + # Support for versioned libraries + link._setup_versioned_lib_variables(env, tool = 'sunlink', use_soname = True) + env['LINKCALLBACKS'] = link._versioned_lib_callbacks() + def exists(env): return ccLinker |