From 20e2a0683d2f2730a3ff636ab3fd04a2676ca7a8 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Aug 2020 12:39:42 -0700 Subject: Refactor shared library/module versioning logic to SCons/Tool/linkCommon/ --- SCons/Tool/__init__.py | 485 +----------------------------------- SCons/Tool/applelink.py | 3 +- SCons/Tool/cyglink.py | 131 ++++++---- SCons/Tool/dmd.py | 17 +- SCons/Tool/gdc.py | 3 +- SCons/Tool/install.py | 8 +- SCons/Tool/ldc.py | 10 +- SCons/Tool/link.py | 28 +-- SCons/Tool/linkCommon/__init__.py | 503 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 616 insertions(+), 572 deletions(-) create mode 100644 SCons/Tool/linkCommon/__init__.py diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py index 32017cb..24fb9d2 100644 --- a/SCons/Tool/__init__.py +++ b/SCons/Tool/__init__.py @@ -39,7 +39,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys import os -from collections.abc import Callable import importlib.util import SCons.Builder @@ -51,6 +50,8 @@ import SCons.Scanner.D import SCons.Scanner.LaTeX import SCons.Scanner.Prog import SCons.Scanner.SWIG +from SCons.Tool.linkCommon import ShLibPrefixGenerator, LdModPrefixGenerator, ShLibSuffixGenerator, \ + LdModSuffixGenerator, LibSymlinksActionFunction, LibSymlinksStrFun DefaultToolpath = [] @@ -272,6 +273,9 @@ class Tool: return self.name +LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun) + + ########################################################################## # Create common executable program / library / object builders @@ -325,485 +329,6 @@ def createStaticLibBuilder(env): return static_lib -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 - - 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('_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' % callback) - pass - else: - if Verbose: - print('_call_linker_cb: env["LINKCALLBACKS"][%r] found' % callback) - print('_call_linker_cb: env["LINKCALLBACKS"][%r]=%r' % (callback, cbfun)) - if isinstance(cbfun, Callable): - if Verbose: - print('_call_linker_cb: env["LINKCALLBACKS"][%r] is callable' % callback) - 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: - @property - def 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: - @property - def 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: - @property - def 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: - """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.libtype = libtype - self.infoname = infoname - - @property - def libtype(self): - return self._support.libtype - - @libtype.setter - def libtype(self, libtype): - try: - support_class = self._support_classes[libtype] - except KeyError: - raise ValueError('unsupported libtype %r' % libtype) - self._support = support_class() - - 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) - - def get_versioned_lib_info_generator(self, **kw): - """ - Returns name of generator linker callback that will 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'. - """ - try: - libtype = kw['generator_libtype'] - except KeyError: - libtype = self.libtype - return 'Versioned%s%s' % (libtype, self.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("_LibPrefixGenerator: input prefix=%r" % prefix) - - 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, **kw2) - - 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 - 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("_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.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 = [x for x in nodes if x != linktgt] - env.Clean(list(set([linktgt] + clean_targets)), clean_list) - if Verbose: - print("EmitLibSymlinks: Clean(%r,%r)" % (linktgt.get_path(), [x.get_path() for x in 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: - 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 Builder in an Environment if it is not there already. diff --git a/SCons/Tool/applelink.py b/SCons/Tool/applelink.py index 8c081a2..4d7b0e2 100644 --- a/SCons/Tool/applelink.py +++ b/SCons/Tool/applelink.py @@ -39,7 +39,8 @@ import SCons.Util # the -rpath option, so we use the "link" tool instead of "gnulink". from . import link -from SCons.Tool import ShLibSonameGenerator +from .linkCommon import ShLibSonameGenerator + class AppleLinkInvalidCurrentVersionException(Exception): pass diff --git a/SCons/Tool/cyglink.py b/SCons/Tool/cyglink.py index fbb6d24..074945f 100644 --- a/SCons/Tool/cyglink.py +++ b/SCons/Tool/cyglink.py @@ -12,19 +12,25 @@ import re import os import SCons.Action +from SCons.Tool.linkCommon import ImpLibSymlinkGenerator, StringizeLibSymlinks, EmitLibSymlinks, ImpLibPrefixGenerator, \ + ImpLibSuffixGenerator, ImpLibNameGenerator import SCons.Util import SCons.Tool -#MAYBE: from . import gnulink from . import gnulink from . import link + def _lib_generator(target, source, env, for_signature, **kw): - try: cmd = kw['cmd'] - except KeyError: cmd = SCons.Util.CLVar(['$SHLINK']) + try: + cmd = kw['cmd'] + except KeyError: + cmd = SCons.Util.CLVar(['$SHLINK']) - try: vp = kw['varprefix'] - except KeyError: vp = 'SHLIB' + try: + vp = kw['varprefix'] + except KeyError: + vp = 'SHLIB' dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp) if dll: cmd.extend(['-o', dll]) @@ -34,12 +40,12 @@ def _lib_generator(target, source, env, for_signature, **kw): implib = env.FindIxes(target, 'IMPLIBPREFIX', 'IMPLIBSUFFIX') if implib: cmd.extend([ - '-Wl,--out-implib='+implib.get_string(for_signature), + '-Wl,--out-implib=' + implib.get_string(for_signature), '-Wl,--export-all-symbols', '-Wl,--enable-auto-import', '-Wl,--whole-archive', '$SOURCES', '-Wl,--no-whole-archive', '$_LIBDIRFLAGS', '$_LIBFLAGS' - ]) + ]) else: cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) @@ -49,12 +55,14 @@ def _lib_generator(target, source, env, for_signature, **kw): def shlib_generator(target, source, env, for_signature): return _lib_generator(target, source, env, for_signature, varprefix='SHLIB', - cmd = SCons.Util.CLVar(['$SHLINK'])) + 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'])) + cmd=SCons.Util.CLVar(['$LDMODULE'])) + def _lib_emitter(target, source, env, **kw): Verbose = False @@ -62,11 +70,15 @@ def _lib_emitter(target, source, env, **kw): if Verbose: print("_lib_emitter: target[0]=%r" % target[0].get_path()) - try: vp = kw['varprefix'] - except KeyError: vp = 'SHLIB' + try: + vp = kw['varprefix'] + except KeyError: + vp = 'SHLIB' - try: libtype = kw['libtype'] - except KeyError: libtype = '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) @@ -75,12 +87,13 @@ def _lib_emitter(target, source, env, **kw): 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)) + 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('$%sPREFIX' % vp) - if dll.name[len(pre):len(pre)+3] == 'lib': - dll.name = pre + dll.name[len(pre)+3:] + 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) @@ -107,23 +120,26 @@ def _lib_emitter(target, source, env, **kw): implib_target.attributes.shared = 1 target.append(implib_target) - symlinks = SCons.Tool.ImpLibSymlinkGenerator(env, implib_target, - implib_libtype=libtype, - generator_libtype=libtype+'ImpLib') + symlinks = ImpLibSymlinkGenerator(env, implib_target, + implib_libtype=libtype, + generator_libtype=libtype + 'ImpLib') if Verbose: - print("_lib_emitter: implib symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks)) + print("_lib_emitter: implib symlinks=%r" % StringizeLibSymlinks(symlinks)) if symlinks: - SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target, clean_targets = target[0]) + 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'""" @@ -138,12 +154,14 @@ def _versioned_lib_suffix(env, suffix, version): 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, + ImpLibPrefixGenerator, + 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 library. Returns a list in the form [ (link, linktarget), ... ] @@ -154,17 +172,18 @@ def _versioned_implib_symlinks(env, libnode, version, prefix, suffix, **kw): print("_versioned_implib_symlinks: libnode=%r" % libnode.get_path()) print("_versioned_implib_symlinks: version=%r" % version) - try: libtype = kw['libtype'] - except KeyError: libtype = 'ShLib' - + 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') + name = ImpLibNameGenerator(env, libnode, + implib_libtype=libtype, + generator_libtype=libtype + 'ImpLib') if Verbose: print("_versioned_implib_symlinks: name=%r" % name) @@ -174,59 +193,65 @@ def _versioned_implib_symlinks(env, libnode, version, prefix, suffix, **kw): symlinks = [(link0, libnode)] if Verbose: - print("_versioned_implib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks)) + print("_versioned_implib_symlinks: return symlinks=%r" % linkStringizeLibSymlinks(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.""" gnulink.generate(env) - env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,-no-undefined') + env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,-no-undefined') env['SHLINKCOM'] = shlib_action env['LDMODULECOM'] = ldmod_action - env.Append(SHLIBEMITTER = [shlib_emitter]) - env.Append(LDMODULEEMITTER = [ldmod_emitter]) + env.Append(SHLIBEMITTER=[shlib_emitter]) + env.Append(LDMODULEEMITTER=[ldmod_emitter]) - env['SHLIBPREFIX'] = 'cyg' - env['SHLIBSUFFIX'] = '.dll' + env['SHLIBPREFIX'] = 'cyg' + env['SHLIBSUFFIX'] = '.dll' - env['IMPLIBPREFIX'] = 'lib' - env['IMPLIBSUFFIX'] = '.dll.a' + env['IMPLIBPREFIX'] = 'lib' + env['IMPLIBSUFFIX'] = '.dll.a' # Variables used by versioned shared libraries - env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' - env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' + 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'), + '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 + try: + del env['_SHLIBSONAME'] + except KeyError: + pass + try: + del env['_LDMODULESONAME'] + except KeyError: + pass + def exists(env): return gnulink.exists(env) - # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/SCons/Tool/dmd.py b/SCons/Tool/dmd.py index ba12131..5970246 100644 --- a/SCons/Tool/dmd.py +++ b/SCons/Tool/dmd.py @@ -50,8 +50,6 @@ LIBS """ # -# __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 @@ -72,11 +70,6 @@ LIBS # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import subprocess - import SCons.Action import SCons.Builder import SCons.Defaults @@ -84,6 +77,7 @@ import SCons.Scanner.D import SCons.Tool import SCons.Tool.DCommon as DCommon +from SCons.Tool.linkCommon import ShLibSonameGenerator def generate(env): @@ -128,7 +122,8 @@ def generate(env): env['SHDLINK'] = '$DC' env['SHDLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared -defaultlib=libphobos2.so') - env['SHDLINKCOM'] = '$DLINK -of$TARGET $SHDLINKFLAGS $__SHDLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' + env[ + 'SHDLINKCOM'] = '$DLINK -of$TARGET $SHDLINKFLAGS $__SHDLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l' env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else '' @@ -139,7 +134,8 @@ def generate(env): env['_DLIBDIRFLAGS'] = '${_concat(DLIBDIRPREFIX, LIBPATH, DLIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)}' env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr' - env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '') + env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLIBFLAGS'.format( + '-c ' if env['PLATFORM'] == 'win32' else '') # env['_DLIBFLAGS'] = '${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)}' @@ -157,7 +153,7 @@ def generate(env): env['_SHDLIBSONAME'] = '${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 + env['DShLibSonameGenerator'] = ShLibSonameGenerator # NOTE: this is only for further reference, currently $SHDLIBVERSION does # not work, the user must use $SHLIBVERSION env['SHDLIBVERSION'] = '$SHLIBVERSION' @@ -172,7 +168,6 @@ def generate(env): def exists(env): return env.Detect(['dmd', 'ldmd2', 'gdmd']) - # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/SCons/Tool/gdc.py b/SCons/Tool/gdc.py index ecf4f3a..12c327e 100644 --- a/SCons/Tool/gdc.py +++ b/SCons/Tool/gdc.py @@ -53,6 +53,7 @@ import SCons.Defaults import SCons.Tool import SCons.Tool.DCommon as DCommon +import SCons.Tool.linkCommon def generate(env): @@ -120,7 +121,7 @@ def generate(env): env['_SHDLIBSONAME'] = '${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 + env['DShLibSonameGenerator'] = SCons.Tool.linkCommon.ShLibSonameGenerator # NOTE: this is only for further reference, currently $SHDLIBVERSION does # not work, the user must use $SHLIBVERSION env['SHDLIBVERSION'] = '$SHLIBVERSION' diff --git a/SCons/Tool/install.py b/SCons/Tool/install.py index 67c9ec8..e79203e 100644 --- a/SCons/Tool/install.py +++ b/SCons/Tool/install.py @@ -29,13 +29,13 @@ selection method. # 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 from shutil import copy2, copymode, copystat import SCons.Action import SCons.Tool +from SCons.Tool.linkCommon import StringizeLibSymlinks, CreateLibSymlinks, EmitLibSymlinks import SCons.Util # @@ -213,9 +213,9 @@ def installShlibLinks(dest, source, env): Verbose = False symlinks = listShlibLinksToInstall(dest, source, env) if Verbose: - print('installShlibLinks: symlinks={!r}'.format(SCons.Tool.StringizeLibSymlinks(symlinks))) + print('installShlibLinks: symlinks={!r}'.format(StringizeLibSymlinks(symlinks))) if symlinks: - SCons.Tool.CreateLibSymlinks(env, symlinks) + CreateLibSymlinks(env, symlinks) return def installFunc(target, source, env): @@ -292,7 +292,7 @@ def add_versioned_targets_to_INSTALLED_FILES(target, source, env): print("add_versioned_targets_to_INSTALLED_FILES: target={!r}".format(list(map(str, target)))) symlinks = listShlibLinksToInstall(target[0], source, env) if symlinks: - SCons.Tool.EmitLibSymlinks(env, symlinks, target[0]) + EmitLibSymlinks(env, symlinks, target[0]) _UNIQUE_INSTALLED_FILES = None return (target, source) diff --git a/SCons/Tool/ldc.py b/SCons/Tool/ldc.py index f915569..d893841 100644 --- a/SCons/Tool/ldc.py +++ b/SCons/Tool/ldc.py @@ -24,8 +24,6 @@ Lib tool variables: """ # -# __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 @@ -46,11 +44,6 @@ Lib tool variables: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os -import subprocess - import SCons.Action import SCons.Builder import SCons.Defaults @@ -58,6 +51,7 @@ import SCons.Scanner.D import SCons.Tool import SCons.Tool.DCommon as DCommon +from SCons.Tool.linkCommon import ShLibSonameGenerator def generate(env): @@ -133,7 +127,7 @@ def generate(env): env['_SHDLIBSONAME'] = '${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 + env['DShLibSonameGenerator'] = ShLibSonameGenerator # NOTE: this is only for further reference, currently $SHDLIBVERSION does # not work, the user must use $SHLIBVERSION env['SHDLIBVERSION'] = '$SHLIBVERSION' diff --git a/SCons/Tool/link.py b/SCons/Tool/link.py index a3567aa..de377ac 100644 --- a/SCons/Tool/link.py +++ b/SCons/Tool/link.py @@ -9,8 +9,6 @@ selection method. """ # -# __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 @@ -30,7 +28,6 @@ selection method. # 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 sys import re @@ -46,7 +43,9 @@ from SCons.Tool.DCommon import isD from SCons.Tool.cxx import iscplusplus -from SCons.Tool import ShLibSonameGenerator +from SCons.Tool.linkCommon import StringizeLibSymlinks, ShLibSonameGenerator, EmitLibSymlinks, ShLibSymlinkGenerator, \ + LdModSymlinkGenerator, ShLibPrefixGenerator, ShLibSuffixGenerator, LdModPrefixGenerator, LdModSuffixGenerator, \ + LdModSonameGenerator issued_mixed_link_warning = False @@ -97,17 +96,17 @@ def _lib_emitter(target, source, env, **kw): print("_lib_emitter: symlinks={!r}".format(symlinks)) if symlinks: - SCons.Tool.EmitLibSymlinks(env, symlinks, target[0]) + EmitLibSymlinks(env, symlinks, target[0]) target[0].attributes.shliblinks = symlinks return (target, source) def shlib_emitter(target, source, env): - return _lib_emitter(target, source, env, symlink_generator=SCons.Tool.ShLibSymlinkGenerator) + return _lib_emitter(target, source, env, symlink_generator=ShLibSymlinkGenerator) def ldmod_emitter(target, source, env): - return _lib_emitter(target, source, env, symlink_generator=SCons.Tool.LdModSymlinkGenerator) + return _lib_emitter(target, source, env, symlink_generator=LdModSymlinkGenerator) # This is generic enough to be included here... @@ -142,14 +141,14 @@ def _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, def _versioned_shlib_name(env, libnode, version, prefix, suffix, **kw): - prefix_generator = SCons.Tool.ShLibPrefixGenerator - suffix_generator = SCons.Tool.ShLibSuffixGenerator + prefix_generator = ShLibPrefixGenerator + suffix_generator = ShLibSuffixGenerator return _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, suffix_generator, **kw) def _versioned_ldmod_name(env, libnode, version, prefix, suffix, **kw): - prefix_generator = SCons.Tool.LdModPrefixGenerator - suffix_generator = SCons.Tool.LdModSuffixGenerator + prefix_generator = LdModPrefixGenerator + suffix_generator = LdModSuffixGenerator return _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, suffix_generator, **kw) @@ -236,7 +235,8 @@ def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, so symlinks = [(link0, libnode), (link1, libnode)] if Verbose: - print("_versioned_lib_symlinks: return symlinks={!r}".format(SCons.Tool.StringizeLibSymlinks(symlinks))) + print("_versioned_lib_symlinks: return symlinks={!r}".format( + StringizeLibSymlinks(symlinks))) return symlinks @@ -301,8 +301,8 @@ def _setup_versioned_lib_variables(env, **kw): 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 + env['ShLibSonameGenerator'] = ShLibSonameGenerator + env['LdModSonameGenerator'] = LdModSonameGenerator else: env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' diff --git a/SCons/Tool/linkCommon/__init__.py b/SCons/Tool/linkCommon/__init__.py new file mode 100644 index 0000000..f11aa14 --- /dev/null +++ b/SCons/Tool/linkCommon/__init__.py @@ -0,0 +1,503 @@ +"""SCons.Tool.linkCommon + +Common link/shared library logic +""" + +# +# 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. +import os +from typing import Callable +from SCons.Util import is_List + + +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 + + 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('_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' % callback) + pass + else: + if Verbose: + print('_call_linker_cb: env["LINKCALLBACKS"][%r] found' % callback) + print('_call_linker_cb: env["LINKCALLBACKS"][%r]=%r' % (callback, cbfun)) + if isinstance(cbfun, Callable): + if Verbose: + print('_call_linker_cb: env["LINKCALLBACKS"][%r] is callable' % callback) + result = cbfun(env, *args) + return result + + +class _ShLibInfoSupport: + @property + def 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: + @property + def 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: + @property + def 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: + """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.libtype = libtype + self.infoname = infoname + + @property + def libtype(self): + return self._support.libtype + + @libtype.setter + def libtype(self, libtype): + try: + support_class = self._support_classes[libtype] + except KeyError: + raise ValueError('unsupported libtype %r' % libtype) + self._support = support_class() + + 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) + + def get_versioned_lib_info_generator(self, **kw): + """ + Returns name of generator linker callback that will 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'. + """ + try: + libtype = kw['generator_libtype'] + except KeyError: + libtype = self.libtype + return 'Versioned%s%s' % (libtype, self.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("_LibPrefixGenerator: input prefix=%r" % prefix) + + 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, **kw2) + + 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 + 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("_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.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 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 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 = [x for x in nodes if x != linktgt] + env.Clean(list(set([linktgt] + clean_targets)), clean_list) + if Verbose: + print("EmitLibSymlinks: Clean(%r,%r)" % (linktgt.get_path(), [x.get_path() for x in 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: + linkstr = ', '.join(["%r->%r" % (k, v) for k, v in StringizeLibSymlinks(symlinks)]) + except (KeyError, ValueError): + pass + else: + cmd += ": %s" % linkstr + return cmd + + +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) \ No newline at end of file -- cgit v0.12 From 52f8072e6e18953132fe21a8c99bbb6c62a8dfb8 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Aug 2020 22:13:14 -0400 Subject: undo use_soname for this branch --- SCons/Tool/applelink.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/SCons/Tool/applelink.py b/SCons/Tool/applelink.py index 4d7b0e2..aec8b93 100644 --- a/SCons/Tool/applelink.py +++ b/SCons/Tool/applelink.py @@ -45,6 +45,7 @@ from .linkCommon import ShLibSonameGenerator class AppleLinkInvalidCurrentVersionException(Exception): pass + class AppleLinkInvalidCompatibilityVersionException(Exception): pass @@ -71,7 +72,7 @@ def _applelib_versioned_lib_soname(env, libnode, version, prefix, suffix, name_f if Verbose: print("_applelib_versioned_lib_soname: name={!r}".format(name)) major = version.split('.')[0] - (libname,_suffix) = name.split('.') + (libname, _suffix) = name.split('.') # if a desired SONAME was supplied, use that, otherwise create # a default from the major version if env.get('SONAME'): @@ -82,12 +83,15 @@ def _applelib_versioned_lib_soname(env, libnode, version, prefix, suffix, name_f print("_applelib_versioned_lib_soname: soname={!r}".format(soname)) return soname + def _applelib_versioned_shlib_soname(env, libnode, version, prefix, suffix): return _applelib_versioned_lib_soname(env, libnode, version, prefix, suffix, link._versioned_shlib_name) # User programmatically describes how SHLIBVERSION maps to values for compat/current. _applelib_max_version_values = (65535, 255, 255) + + def _applelib_check_valid_version(version_string): """ Check that the version # is valid. @@ -100,17 +104,18 @@ def _applelib_check_valid_version(version_string): """ parts = version_string.split('.') if len(parts) > 3: - return False, "Version string has too many periods [%s]"%version_string + return False, "Version string has too many periods [%s]" % version_string if len(parts) <= 0: - return False, "Version string unspecified [%s]"%version_string + return False, "Version string unspecified [%s]" % version_string for (i, p) in enumerate(parts): try: p_i = int(p) except ValueError: - return False, "Version component %s (from %s) is not a number"%(p, version_string) + return False, "Version component %s (from %s) is not a number" % (p, version_string) if p_i < 0 or p_i > _applelib_max_version_values[i]: - return False, "Version component %s (from %s) is not valid value should be between 0 and %d"%(p, version_string, _applelib_max_version_values[i]) + return False, "Version component %s (from %s) is not valid value should be between 0 and %d" % ( + p, version_string, _applelib_max_version_values[i]) return True, "" @@ -191,9 +196,8 @@ def generate(env): env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - # see: http://docstore.mik.ua/orelly/unix3/mac/ch05_04.htm for proper naming - link._setup_versioned_lib_variables(env, tool = 'applelink')#, use_soname = use_soname) + link._setup_versioned_lib_variables(env, tool='applelink') #, use_soname=True) env['LINKCALLBACKS'] = link._versioned_lib_callbacks() env['LINKCALLBACKS']['VersionedShLibSuffix'] = _applelib_versioned_lib_suffix env['LINKCALLBACKS']['VersionedShLibSoname'] = _applelib_versioned_shlib_soname @@ -206,15 +210,14 @@ def generate(env): # override the default for loadable modules, which are different # on OS X than dynamic shared libs. echoing what XCode does for # pre/suffixes: - env['LDMODULEPREFIX'] = '' - env['LDMODULESUFFIX'] = '' + env['LDMODULEPREFIX'] = '' + env['LDMODULESUFFIX'] = '' env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' env['__SHLIBVERSIONFLAGS'] = '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}' - def exists(env): return env['PLATFORM'] == 'darwin' -- cgit v0.12 From 36427e8292c1e111b57bdee8bce513cf28e694cc Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Aug 2020 22:14:39 -0400 Subject: Add CHANGES.txt entry --- CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.txt b/CHANGES.txt index a359608..81ba741 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,6 +16,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From William Deegan: - Fix yacc tool, not respecting YACC set at time of tool initialization. + - Refactor SCons.Tool to move all common shared and loadable module linking logic to SCons.Tool.linkCommon From Adam Gross: - Fix minor bug affecting SCons.Node.FS.File.get_csig()'s usage of the MD5 chunksize. -- cgit v0.12 From b84a457e407082a555a52dc7ea75328670c78f7a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Aug 2020 22:24:05 -0400 Subject: Fix test/import to skip linkCommon --- test/import.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/import.py b/test/import.py index 99a40d7..df64a1e 100644 --- a/test/import.py +++ b/test/import.py @@ -1,7 +1,5 @@ #!/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 @@ -22,8 +20,6 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - """ Verify that we can import and use the contents of Platform and Tool modules directly. @@ -78,6 +74,8 @@ ignore = ('__init__.py', 'MSCommon', # clang common "clangCommon", + # link common logic + "linkCommon", # Sun pkgchk and pkginfo common stuff 'sun_pkg.py', # RPM utilities -- cgit v0.12 From 7f0df2151f891497b818aa406e614015132a4224 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 25 Aug 2020 22:29:44 -0400 Subject: Fix sider complaint which was real missign symbol issue --- SCons/Tool/cyglink.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/cyglink.py b/SCons/Tool/cyglink.py index 074945f..08b8a98 100644 --- a/SCons/Tool/cyglink.py +++ b/SCons/Tool/cyglink.py @@ -193,7 +193,7 @@ def _versioned_implib_symlinks(env, libnode, version, prefix, suffix, **kw): symlinks = [(link0, libnode)] if Verbose: - print("_versioned_implib_symlinks: return symlinks=%r" % linkStringizeLibSymlinks(symlinks)) + print("_versioned_implib_symlinks: return symlinks=%r" % StringizeLibSymlinks(symlinks)) return symlinks -- cgit v0.12