From 5e6fe1a165f2e9a8c9db23a8ea958704200e95de Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 20 Dec 2020 18:21:45 -0800 Subject: initial checkin of versioned shared library rewrite --- SCons/Defaults.py | 145 +++++--- SCons/Tool/__init__.py | 11 +- SCons/Tool/applelink.py | 86 ++--- SCons/Tool/cyglink.py | 314 +++++++--------- SCons/Tool/dmd.py | 12 +- SCons/Tool/gnulink.py | 11 +- SCons/Tool/ldc.py | 13 +- SCons/Tool/link.py | 33 +- SCons/Tool/linkCommon/LoadableModule.py | 97 +++++ SCons/Tool/linkCommon/SharedLibrary.py | 153 ++++++++ SCons/Tool/linkCommon/__init__.py | 622 +------------------------------- SCons/Tool/sunlink.py | 13 +- SCons/__init__.py | 6 +- test/LINK/SHLIBVERSIONFLAGS.py | 35 +- 14 files changed, 570 insertions(+), 981 deletions(-) create mode 100644 SCons/Tool/linkCommon/LoadableModule.py create mode 100644 SCons/Tool/linkCommon/SharedLibrary.py diff --git a/SCons/Defaults.py b/SCons/Defaults.py index c59fbcf..2bacc10 100644 --- a/SCons/Defaults.py +++ b/SCons/Defaults.py @@ -31,12 +31,12 @@ The code that reads the registry to find MSVC components was borrowed from distutils.msvccompiler. """ -import os import errno +import os import shutil import stat -import time import sys +import time import SCons.Action import SCons.Builder @@ -52,6 +52,7 @@ import SCons.Tool # interface. _default_env = None + # Lazily instantiate the default environment so the overhead of creating # it doesn't apply when it's not needed. def _fetch_DefaultEnvironment(*args, **kw): @@ -59,6 +60,7 @@ def _fetch_DefaultEnvironment(*args, **kw): global _default_env return _default_env + def DefaultEnvironment(*args, **kw): """ Initial public entry point for creating the default construction @@ -88,6 +90,7 @@ def DefaultEnvironment(*args, **kw): _default_env._CacheDir_path = None return _default_env + # Emitters for setting the shared attribute on object files, # and an action for checking that all of the source files # going into a shared library are, in fact, shared. @@ -96,11 +99,13 @@ def StaticObjectEmitter(target, source, env): tgt.attributes.shared = None return (target, source) + def SharedObjectEmitter(target, source, env): for tgt in target: tgt.attributes.shared = 1 return (target, source) + def SharedFlagChecker(source, target, env): same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') if same == '0' or same == '' or same == 'False': @@ -110,7 +115,9 @@ def SharedFlagChecker(source, target, env): except AttributeError: shared = None if not shared: - raise SCons.Errors.UserError("Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])) + raise SCons.Errors.UserError( + "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])) + SharedCheck = SCons.Action.Action(SharedFlagChecker, None) @@ -128,6 +135,7 @@ ProgScan = SCons.Tool.ProgramScanner # the rest of those in Tool/__init__.py, but I'm not sure where else # they should go. Leave them here for now. import SCons.Scanner.Dir + DirScanner = SCons.Scanner.Dir.DirScanner() DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner() @@ -152,6 +160,7 @@ LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") # ways by creating ActionFactory instances. ActionFactory = SCons.Action.ActionFactory + def get_paths_str(dest): # If dest is a list, we need to manually call str() on each element if SCons.Util.is_List(dest): @@ -162,24 +171,26 @@ def get_paths_str(dest): else: return '"' + str(dest) + '"' + permission_dic = { - 'u':{ - 'r':stat.S_IRUSR, - 'w':stat.S_IWUSR, - 'x':stat.S_IXUSR + 'u': { + 'r': stat.S_IRUSR, + 'w': stat.S_IWUSR, + 'x': stat.S_IXUSR }, - 'g':{ - 'r':stat.S_IRGRP, - 'w':stat.S_IWGRP, - 'x':stat.S_IXGRP + 'g': { + 'r': stat.S_IRGRP, + 'w': stat.S_IWGRP, + 'x': stat.S_IXGRP }, - 'o':{ - 'r':stat.S_IROTH, - 'w':stat.S_IWOTH, - 'x':stat.S_IXOTH + 'o': { + 'r': stat.S_IROTH, + 'w': stat.S_IWOTH, + 'x': stat.S_IXOTH } } + def chmod_func(dest, mode): import SCons.Util from string import digits @@ -223,6 +234,7 @@ def chmod_func(dest, mode): elif operator == "-": os.chmod(str(element), curr_perm & ~new_perm) + def chmod_strfunc(dest, mode): import SCons.Util if not SCons.Util.is_String(mode): @@ -230,8 +242,10 @@ def chmod_strfunc(dest, mode): else: return 'Chmod(%s, "%s")' % (get_paths_str(dest), str(mode)) + Chmod = ActionFactory(chmod_func, chmod_strfunc) + def copy_func(dest, src, symlinks=True): """ If symlinks (is true), then a symbolic link will be @@ -262,11 +276,13 @@ def copy_func(dest, src, symlinks=True): # A error is raised in both cases, so we can just return 0 for success return 0 + Copy = ActionFactory( copy_func, lambda dest, src, symlinks=True: 'Copy("%s", "%s")' % (dest, src) ) + def delete_func(dest, must_exist=0): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): @@ -283,11 +299,14 @@ def delete_func(dest, must_exist=0): continue os.unlink(entry) + def delete_strfunc(dest, must_exist=0): return 'Delete(%s)' % get_paths_str(dest) + Delete = ActionFactory(delete_func, delete_strfunc) + def mkdir_func(dest): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): @@ -298,24 +317,28 @@ def mkdir_func(dest): except os.error as e: p = str(entry) if (e.args[0] == errno.EEXIST or - (sys.platform=='win32' and e.args[0]==183)) \ + (sys.platform == 'win32' and e.args[0] == 183)) \ and os.path.isdir(str(entry)): - pass # not an error if already exists + pass # not an error if already exists else: raise + Mkdir = ActionFactory(mkdir_func, lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) + def move_func(dest, src): SCons.Node.FS.invalidate_node_memos(dest) SCons.Node.FS.invalidate_node_memos(src) shutil.move(src, dest) + Move = ActionFactory(move_func, lambda dest, src: 'Move("%s", "%s")' % (dest, src), convert=str) + def touch_func(dest): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): @@ -330,9 +353,11 @@ def touch_func(dest): atime = mtime os.utime(file, (atime, mtime)) + Touch = ActionFactory(touch_func, lambda file: 'Touch(%s)' % get_paths_str(file)) + # Internal utility functions @@ -386,7 +411,7 @@ def _concat_ixes(prefix, list, suffix, env): if suffix[0] == ' ': result.append(suffix[1:]) elif x[-len(suffix):] != suffix: - result[-1] = result[-1]+suffix + result[-1] = result[-1] + suffix return result @@ -443,6 +468,7 @@ def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): return c(prefix, stripped, suffix, env) + def processDefines(defs): """process defines, resolving strings, lists, dictionaries, into a list of strings @@ -458,7 +484,7 @@ def processDefines(defs): else: l.append(str(d[0])) elif SCons.Util.is_Dict(d): - for macro,value in d.items(): + for macro, value in d.items(): if value is not None: l.append(str(macro) + '=' + str(value)) else: @@ -466,7 +492,7 @@ def processDefines(defs): elif SCons.Util.is_String(d): l.append(str(d)) else: - raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) + raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None." % repr(d)) elif SCons.Util.is_Dict(defs): # The items in a dictionary are stored in random order, but # if the order of the command-line options changes from @@ -475,7 +501,7 @@ def processDefines(defs): # Consequently, we have to sort the keys to ensure a # consistent order... l = [] - for k,v in sorted(defs.items()): + for k, v in sorted(defs.items()): if v is None: l.append(str(k)) else: @@ -524,11 +550,14 @@ class Variable_Method_Caller: the way of Memoizing construction environments, because we had to create new environment objects to hold the variables.) """ + def __init__(self, variable, method): self.variable = variable self.method = method + def __call__(self, *args, **kw): - try: 1//0 + try: + 1 // 0 except ZeroDivisionError: # Don't start iterating with the current stack-frame to # prevent creating reference cycles (f_back is safe). @@ -543,6 +572,7 @@ class Variable_Method_Caller: frame = frame.f_back return None + def __libversionflags(env, version_var, flags_var): """ if $version_var is not empty, returns env[flags_var], otherwise returns None @@ -552,42 +582,59 @@ def __libversionflags(env, version_var, flags_var): :return: """ try: - if env.subst('$'+version_var): + if env.subst('$' + version_var): return env[flags_var] except KeyError: pass return None +def __lib_either_version_flag(env, version_var1, version_var2, flags_var): + """ + if $version_var1 or $version_var2 is not empty, returns env[flags_var], otherwise returns None + :param env: + :param version_var1: + :param version_var2: + :param flags_var: + :return: + """ + try: + if env.subst('$' + version_var1) or env.subst('$' + version_var2): + return env[flags_var] + except KeyError: + pass + return None + ConstructionEnvironment = { - 'BUILDERS' : {}, - 'SCANNERS' : [ SCons.Tool.SourceFileScanner ], - 'CONFIGUREDIR' : '#/.sconf_temp', - 'CONFIGURELOG' : '#/config.log', - 'CPPSUFFIXES' : SCons.Tool.CSuffixes, - 'DSUFFIXES' : SCons.Tool.DSuffixes, - 'ENV' : {}, - 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, - '_concat' : _concat, - '_defines' : _defines, - '_stripixes' : _stripixes, - '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', - '_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, + 'BUILDERS': {}, + 'SCANNERS': [SCons.Tool.SourceFileScanner], + 'CONFIGUREDIR': '#/.sconf_temp', + 'CONFIGURELOG': '#/config.log', + 'CPPSUFFIXES': SCons.Tool.CSuffixes, + 'DSUFFIXES': SCons.Tool.DSuffixes, + 'ENV': {}, + 'IDLSUFFIXES': SCons.Tool.IDLSuffixes, + '_concat': _concat, + '_defines': _defines, + '_stripixes': _stripixes, + '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', + '_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")}', + '__lib_either_version_flag': __lib_either_version_flag, + + 'TEMPFILE': NullCmdGenerator, 'TEMPFILEARGJOIN': ' ', - 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), - 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), - 'File' : Variable_Method_Caller('TARGET', 'File'), - 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), + 'Dir': Variable_Method_Caller('TARGET', 'Dir'), + 'Dirs': Variable_Method_Caller('TARGET', 'Dirs'), + 'File': Variable_Method_Caller('TARGET', 'File'), + 'RDirs': Variable_Method_Caller('TARGET', 'RDirs'), } # Local Variables: diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py index 87139f1..7c78dba 100644 --- a/SCons/Tool/__init__.py +++ b/SCons/Tool/__init__.py @@ -50,8 +50,7 @@ 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 +from SCons.Tool.linkCommon import LibSymlinksActionFunction, LibSymlinksStrFun DefaultToolpath = [] @@ -348,8 +347,8 @@ def createSharedLibBuilder(env): LibSymlinksAction] shared_lib = SCons.Builder.Builder(action=action_list, emitter="$SHLIBEMITTER", - prefix=ShLibPrefixGenerator, - suffix=ShLibSuffixGenerator, + prefix="$SHLIBPREFIX", + suffix="$_SHLIBSUFFIX", target_scanner=ProgramScanner, src_suffix='$SHOBJSUFFIX', src_builder='SharedObject') @@ -374,8 +373,8 @@ def createLoadableModuleBuilder(env): LibSymlinksAction] ld_module = SCons.Builder.Builder(action=action_list, emitter="$LDMODULEEMITTER", - prefix=LdModPrefixGenerator, - suffix=LdModSuffixGenerator, + prefix="$LDMODULEPREFIX", + suffix="$_LDMODULESUFFIX", target_scanner=ProgramScanner, src_suffix='$SHOBJSUFFIX', src_builder='SharedObject') diff --git a/SCons/Tool/applelink.py b/SCons/Tool/applelink.py index f51a6af..6a4a37c 100644 --- a/SCons/Tool/applelink.py +++ b/SCons/Tool/applelink.py @@ -9,6 +9,10 @@ selection method. """ # +# MIT License +# +# Copyright The SCons Foundation +# # 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 @@ -29,14 +33,14 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -import SCons.Tool.linkCommon -import SCons.Util - # Even though the Mac is based on the GNU toolchain, it doesn't understand # the -rpath option, so we use the "link" tool instead of "gnulink". +from SCons.Util import CLVar + from . import link -from .linkCommon import ShLibSonameGenerator +# User programmatically describes how SHLIBVERSION maps to values for compat/current. +_APPLELIB_MAX_VERSION_VALUES = (65535, 255, 255) class AppleLinkInvalidCurrentVersionException(Exception): @@ -47,49 +51,6 @@ class AppleLinkInvalidCompatibilityVersionException(Exception): pass -def _applelib_versioned_lib_suffix(env, suffix, version): - """For suffix='.dylib' and version='0.1.2' it returns '.0.1.2.dylib'""" - Verbose = False - if Verbose: - print("_applelib_versioned_lib_suffix: suffix={!r}".format(suffix)) - print("_applelib_versioned_lib_suffix: version={!r}".format(version)) - if version not in suffix: - suffix = "." + version + suffix - if Verbose: - print("_applelib_versioned_lib_suffix: return suffix={!r}".format(suffix)) - return suffix - - -def _applelib_versioned_lib_soname(env, libnode, version, prefix, suffix, name_func): - """For libnode='/optional/dir/libfoo.X.Y.Z.dylib' it returns 'libfoo.X.dylib'""" - Verbose = False - if Verbose: - print("_applelib_versioned_lib_soname: version={!r}".format(version)) - name = name_func(env, libnode, version, prefix, suffix) - if Verbose: - print("_applelib_versioned_lib_soname: name={!r}".format(name)) - major = version.split('.')[0] - (libname, _suffix) = name.split('.') - # if a desired SONAME was supplied, use that, otherwise create - # a default from the major version - if env.get('SONAME'): - soname = ShLibSonameGenerator(env, libnode) - else: - soname = '.'.join([libname, major, _suffix]) - if Verbose: - 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, - SCons.Tool.linkCommon._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. @@ -111,9 +72,9 @@ def _applelib_check_valid_version(version_string): p_i = int(p) except ValueError: 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]: + 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]) + p, version_string, _APPLELIB_MAX_VERSION_VALUES[i]) return True, "" @@ -191,15 +152,9 @@ def generate(env): env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') + env['SHLINKFLAGS'] = CLVar('$LINKFLAGS -dynamiclib') env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - # see: http://docstore.mik.ua/orelly/unix3/mac/ch05_04.htm for proper naming - SCons.Tool.linkCommon._setup_versioned_lib_variables(env, tool='applelink', use_soname=True) - env['LINKCALLBACKS'] = SCons.Tool.linkCommon._versioned_lib_callbacks() - env['LINKCALLBACKS']['VersionedShLibSuffix'] = _applelib_versioned_lib_suffix - env['LINKCALLBACKS']['VersionedShLibSoname'] = _applelib_versioned_shlib_soname - env['_APPLELINK_CURRENT_VERSION'] = _applelib_currentVersionFromSoVersion env['_APPLELINK_COMPATIBILITY_VERSION'] = _applelib_compatVersionFromSoVersion env['_SHLIBVERSIONFLAGS'] = '$_APPLELINK_CURRENT_VERSION $_APPLELINK_COMPATIBILITY_VERSION ' @@ -210,10 +165,21 @@ def generate(env): # pre/suffixes: 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")}' + env['LDMODULEFLAGS'] = CLVar('$LINKFLAGS -bundle') + env[ + 'LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS' \ + ' $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + + # New stuff + # + env['_SHLIBSUFFIX'] = '${_SHLIBVERSION}${SHLIBSUFFIX}' + + env[ + '__SHLIBVERSIONFLAGS'] = '${__lib_either_version_flag(__env__,' \ + '"SHLIBVERSION","_APPLELINK_CURRENT_VERSION", "_SHLIBVERSIONFLAGS")}' + env[ + '__LDMODULEVERSIONFLAGS'] = '${__lib_either_version_flag(__env__,' \ + '"LDMODULEVERSION","_APPLELINK_CURRENT_VERSION", "_LDMODULEVERSIONFLAGS")}' def exists(env): diff --git a/SCons/Tool/cyglink.py b/SCons/Tool/cyglink.py index 595cc2e..1995ac3 100644 --- a/SCons/Tool/cyglink.py +++ b/SCons/Tool/cyglink.py @@ -8,211 +8,153 @@ selection method. """ -import re -import os - -import SCons.Action -import SCons.Tool.linkCommon as linkCommon -from SCons.Tool.linkCommon import ImpLibSymlinkGenerator, StringizeLibSymlinks, EmitLibSymlinks, ImpLibPrefixGenerator, \ - ImpLibSuffixGenerator, ImpLibNameGenerator -import SCons.Util -import SCons.Tool - +from SCons.Tool.linkCommon import StringizeLibSymlinks, EmitLibSymlinks +from SCons.Util import CLVar from . import gnulink -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, '%sPREFIX' % vp, '%sSUFFIX' % vp) - if dll: cmd.extend(['-o', dll]) +def cyglink_lib_emitter(target, source, env, **kw): + verbose = True - cmd.extend(['$SHLINKFLAGS', '$__%sVERSIONFLAGS' % vp, '$__RPATH']) - - implib = env.FindIxes(target, 'IMPLIBPREFIX', 'IMPLIBSUFFIX') - if implib: - cmd.extend([ - '-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' - ]) + if 'variable_prefix' in kw: + var_prefix = kw['variable_prefix'] else: - cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - - return [cmd] - + var_prefix = 'SHLIB' -def shlib_generator(target, source, env, for_signature): - return _lib_generator(target, source, env, for_signature, - varprefix='SHLIB', - cmd=SCons.Util.CLVar(['$SHLINK'])) + no_import_lib = env.get('no_import_lib', False) + if verbose: + print("cyglink_lib_emitter: target[0]={!r}".format(target[0].get_path())) -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()) + if not no_import_lib: + # Specify import lib and add to targets - try: - vp = kw['varprefix'] - except KeyError: - vp = 'SHLIB' + import_lib = env.subst('$%s_IMPLIBNAME' % var_prefix, target=target, source=source) + import_lib_target = env.fs.File(import_lib) + import_lib_target.attributes.shared = 1 + target.append(import_lib_target) - try: - libtype = kw['libtype'] - except KeyError: - libtype = 'ShLib' + if verbose: + print("cyglink_lib_emitter: import_lib={}".format(import_lib)) + print("cyglink_lib_emitter: target=%s" % target) - dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp) - no_import_lib = env.get('no_import_lib', 0) + for tgt in target: + if is_String(tgt): + tgt = env.File(tgt) + tgt.attributes.shared = 1 - if Verbose: - print("_lib_emitter: dll=%r" % dll.get_path()) + return target, source - 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)) - # 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:] +def cyglink_ldmodule_emitter(target, source, env, **kw): + return cyglink_lib_emitter(target, source, env, variable_prefix='LDMODULE') - if Verbose: - print("_lib_emitter: dll.name=%r" % dll.name) - orig_target = target - target = [env.fs.File(dll)] - target[0].attributes.shared = 1 +def cyglink_shlib_symlink_emitter(target, source, env, **kw): + """ + On cygwin, we only create a symlink from the non-versioned implib to the versioned implib. + We don't version the shared library itself. + :param target: + :param source: + :param env: + :param kw: + :return: + """ + verbose = True - if Verbose: - print("_lib_emitter: after target=[env.fs.File(dll)]: target[0]=%r" % target[0].get_path()) + if 'variable_prefix' in kw: + var_prefix = kw['variable_prefix'] + else: + var_prefix = 'SHLIB' - # Append an import lib target - if not no_import_lib: - # Create list of target libraries as strings - target_strings = env.ReplaceIxes(orig_target[0], - '%sPREFIX' % vp, '%sSUFFIX' % vp, - 'IMPLIBPREFIX', 'IMPLIBSUFFIX') - if Verbose: - print("_lib_emitter: target_strings=%r" % target_strings) - - implib_target = env.fs.File(target_strings) - if Verbose: - print("_lib_emitter: implib_target=%r" % implib_target.get_path()) - implib_target.attributes.shared = 1 - target.append(implib_target) - - # Only create the symlinks if there is actually an import library - symlinks = ImpLibSymlinkGenerator(env, implib_target, - implib_libtype=libtype, - generator_libtype=libtype + 'ImpLib') - if Verbose: - print("_lib_emitter: implib symlinks=%r" % StringizeLibSymlinks(symlinks)) - if symlinks: - EmitLibSymlinks(env, symlinks, implib_target, clean_targets=target[0]) - implib_target.attributes.shliblinks = symlinks + no_import_lib = env.get('no_import_lib', False) + if no_import_lib in ['1', 'True', 'true', True]: + if verbose: + print("cyglink_shlib_symlink_emitter: no_import_lib=%s" % no_import_lib) + return target, source - return (target, source) + no_symlinks = env.subst('$%sNOVERSIONSYMLINKS' % var_prefix) + if no_symlinks in ['1', 'True', 'true', True]: + return target, source + shlibversion = env.subst('$%sVERSION' % var_prefix) + if shlibversion: + if verbose: + print("cyglink_shlib_symlink_emitter: %sVERSION=%s" % (var_prefix, shlibversion)) -def shlib_emitter(target, source, env): - return _lib_emitter(target, source, env, varprefix='SHLIB', libtype='ShLib') + # The implib (added by the cyglink_lib_emitter) + imp_lib_node = target[1] + shlib_noversion_symlink = env.subst('$%s_NOVERSION_SYMLINK' % var_prefix, target=target[0], source=source) + if verbose: + print("cyglink_shlib_symlink_emitter: shlib_noversion_symlink :%s" % shlib_noversion_symlink) + print("cyglink_shlib_symlink_emitter: imp_lib_node :%s" % imp_lib_node) -def ldmod_emitter(target, source, env): - return _lib_emitter(target, source, env, varprefix='LDMODULE', libtype='LdMod') + symlinks = [(env.File(shlib_noversion_symlink), imp_lib_node)] + if verbose: + print("cyglink_shlib_symlink_emitter: symlinks={!r}".format( + ', '.join(["%r->%r" % (k, v) for k, v in StringizeLibSymlinks(symlinks)]) + )) -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(r'\.', '-', version) - if not suffix.startswith('-' + cygversion): - suffix = '-' + cygversion + suffix - if Verbose: - print("_versioned_lib_suffix: return suffix= ", suffix) - return suffix + if symlinks: + # This does the actual symlinking + EmitLibSymlinks(env, symlinks, target[0]) + # This saves the information so if the versioned shared library is installed + # it can faithfully reproduce the correct symlinks + target[0].attributes.shliblinks = symlinks -def _versioned_implib_name(env, libnode, version, prefix, suffix, **kw): - return linkCommon._versioned_lib_name(env, libnode, version, prefix, suffix, - ImpLibPrefixGenerator, - ImpLibSuffixGenerator, - implib_libtype=kw['libtype']) + return target, source -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), ... ] - """ - Verbose = False +def cyglink_ldmod_symlink_emitter(target, source, env, **kw): + return cyglink_shlib_symlink_emitter(target, source, env, variable_prefix='LDMODULE') - 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' +def cyglink_shlibversion(target, source, env, for_signature): + var_prefix = 'SHLIB' + var = '%sVERSION' % var_prefix + if var not in env: + return '' - linkdir = os.path.dirname(libnode.get_path()) - if Verbose: - print("_versioned_implib_symlinks: linkdir=%r" % linkdir) + version = env.subst("$%s" % var, target=target, source=source) + version = version.replace('.', '-') + return "." + version - name = ImpLibNameGenerator(env, libnode, - implib_libtype=libtype, - generator_libtype=libtype + 'ImpLib') - if Verbose: - print("_versioned_implib_symlinks: name=%r" % name) - major = version.split('.')[0] +def cyglink_ldmodule_version(target, source, env, for_signature): + var_prefix = 'LDMODULE' + var = '%sVERSION' % var_prefix + if var not in env: + return '' - link0 = env.fs.File(os.path.join(linkdir, name)) - symlinks = [(link0, libnode)] + version = env.subst("$%s" % var, target=target, source=source) + version = version.replace('.', '-') + return "." + version - if Verbose: - print("_versioned_implib_symlinks: return symlinks=%r" % StringizeLibSymlinks(symlinks)) - return symlinks +def _implib_pre_flags(target, source, env, for_signature): + no_import_lib = env.get('no_import_lib', False) + if no_import_lib in ['1', 'True', 'true', True]: + return '' + else: + return '-Wl,--out-implib=${TARGETS[1]} -Wl,--export-all-symbols -Wl,--enable-auto-import -Wl,--whole-archive' -shlib_action = SCons.Action.Action(shlib_generator, generator=1) -ldmod_action = SCons.Action.Action(ldmod_generator, generator=1) +def _implib_post_flags(target, source, env, for_signature): + no_import_lib = env.get('no_import_lib', False) + if no_import_lib in ['1', 'True', 'true', True]: + return '' + else: + return '-Wl,--no-whole-archive' 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['SHLINKCOM'] = shlib_action - env['LDMODULECOM'] = ldmod_action - env.Append(SHLIBEMITTER=[shlib_emitter]) - env.Append(LDMODULEEMITTER=[ldmod_emitter]) + env['LINKFLAGS'] = CLVar('-Wl,-no-undefined') env['SHLIBPREFIX'] = 'cyg' env['SHLIBSUFFIX'] = '.dll' @@ -221,33 +163,43 @@ def generate(env): env['IMPLIBSUFFIX'] = '.dll.a' # Variables used by versioned shared libraries + # SHLIBVERSIONFLAGS and LDMODULEVERSIONFLAGS are same as in gnulink... env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' - # SHLIBVERSIONFLAGS and LDMODULEVERSIONFLAGS are same as in gnulink... + env['_IMPLIB_PRE_SOURCES'] = _implib_pre_flags + env['_IMPLIB_POST_SOURCES'] = _implib_post_flags + env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH ' \ + '$_IMPLIB_PRE_SOURCES $SOURCES $_IMPLIB_POST_SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LDMODULECOM'] = '$LDMODULE -o $TARGET $SHLINKFLAGS $__LDMODULEVERSIONFLAGS $__RPATH ' \ + '$_IMPLIB_PRE_SOURCES $SOURCES $_IMPLIB_POST_SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + + # Overwrite emitters. Cyglink does things differently when creating symlinks + env['SHLIBEMITTER'] = [cyglink_lib_emitter, cyglink_shlib_symlink_emitter] + env['LDMODULEEMITTER'] = [cyglink_ldmodule_emitter, cyglink_ldmod_symlink_emitter] + + # This is the non versioned shlib filename + # If SHLIBVERSION is defined then this will symlink to $SHLIBNAME + env['SHLIB_NOVERSION_SYMLINK'] = '${IMPLIBPREFIX}$_get_shlib_stem${IMPLIBSUFFIX}' + env['LDMODULE_NOVERSION_SYMLINK'] = '${IMPLIBPREFIX}$_get_ldmodule_stem${IMPLIBSUFFIX}' + + env['SHLIB_IMPLIBNAME'] = '${IMPLIBPREFIX}$_get_shlib_stem${_SHLIB_IMPLIBSUFFIX}' + env['LDMODULE_IMPLIBNAME'] = '${IMPLIBPREFIX}$_get_ldmodule_stem${_LDMODULE_IMPLIBSUFFIX}' + + env['_cyglink_shlibversion'] = cyglink_shlibversion + env['_SHLIB_IMPLIBSUFFIX'] = '${_cyglink_shlibversion}${IMPLIBSUFFIX}' + env['_SHLIBSUFFIX'] = '${_cyglink_shlibversion}${SHLIBSUFFIX}' + + env['_cyglink_ldmodule_version'] = cyglink_ldmodule_version - # LINKCALLBACKS are NOT inherited from gnulink - env['LINKCALLBACKS'] = { - 'VersionedShLibSuffix': _versioned_lib_suffix, - 'VersionedLdModSuffix': _versioned_lib_suffix, - 'VersionedImpLibSuffix': _versioned_lib_suffix, - 'VersionedShLibName': linkCommon._versioned_shlib_name, - 'VersionedLdModName': linkCommon._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'), - } + env['_LDMODULESUFFIX'] = '${_cyglink_ldmodule_version}${LDMODULESUFFIX}' + env['_LDMODULE_IMPLIBSUFFIX'] = '${_cyglink_ldmodule_version}${IMPLIBSUFFIX}' + # Remove variables set by default initialization which aren't needed/used by cyglink # 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 + for rv in ['_SHLIBSONAME', '_LDMODULESONAME']: + if rv in env: + del env[rv] def exists(env): diff --git a/SCons/Tool/dmd.py b/SCons/Tool/dmd.py index 5970246..05c54ec 100644 --- a/SCons/Tool/dmd.py +++ b/SCons/Tool/dmd.py @@ -77,7 +77,6 @@ import SCons.Scanner.D import SCons.Tool import SCons.Tool.DCommon as DCommon -from SCons.Tool.linkCommon import ShLibSonameGenerator def generate(env): @@ -150,10 +149,13 @@ def generate(env): # Support for versioned libraries env['_SHDLIBVERSIONFLAGS'] = '$SHDLIBVERSIONFLAGS -L-soname=$_SHDLIBSONAME' - 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'] = ShLibSonameGenerator + + # TODO: Fix this with new logic + # 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'] = 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/gnulink.py b/SCons/Tool/gnulink.py index 2b09549..fb5acd0 100644 --- a/SCons/Tool/gnulink.py +++ b/SCons/Tool/gnulink.py @@ -9,6 +9,10 @@ selection method. """ # +# MIT License +# +# Copyright The SCons Foundation +# # 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 @@ -33,10 +37,8 @@ selection method. import SCons.Tool.linkCommon import SCons.Util import SCons.Tool -import sys from . import link -import SCons.Tool.linkCommon as linkCommon def generate(env): @@ -52,11 +54,6 @@ def generate(env): env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - # OpenBSD doesn't usually use SONAME for libraries - use_soname = not sys.platform.startswith('openbsd') - linkCommon._setup_versioned_lib_variables(env, tool='gnulink', use_soname=use_soname) - env['LINKCALLBACKS'] = linkCommon._versioned_lib_callbacks() - def exists(env): # TODO: sync with link.smart_link() to choose a linker diff --git a/SCons/Tool/ldc.py b/SCons/Tool/ldc.py index d893841..342f3f0 100644 --- a/SCons/Tool/ldc.py +++ b/SCons/Tool/ldc.py @@ -51,7 +51,6 @@ import SCons.Scanner.D import SCons.Tool import SCons.Tool.DCommon as DCommon -from SCons.Tool.linkCommon import ShLibSonameGenerator def generate(env): @@ -124,10 +123,14 @@ def generate(env): # Support for versioned libraries env['_SHDLIBVERSIONFLAGS'] = '$SHDLIBVERSIONFLAGS -L-soname=$_SHDLIBSONAME' - 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'] = ShLibSonameGenerator + + # TODO: Fix to work with new logic + # 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'] = 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 c552273..efc84f1 100644 --- a/SCons/Tool/link.py +++ b/SCons/Tool/link.py @@ -31,26 +31,21 @@ selection method. """ - import SCons.Tool import SCons.Util import SCons.Warnings - -from SCons.Tool.linkCommon import smart_link, shlib_emitter, ldmod_emitter +from SCons.Tool import createProgBuilder +from SCons.Tool.linkCommon import smart_link +from SCons.Tool.linkCommon.LoadableModule import setup_loadable_module_logic +from SCons.Tool.linkCommon.SharedLibrary import setup_shared_lib_logic def generate(env): """Add Builders and construction variables for gnulink to an Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) + createProgBuilder(env) - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - - # don't set up the emitter, because AppendUnique will generate a list - # starting with None :-( - env.Append(SHLIBEMITTER=[shlib_emitter]) + setup_shared_lib_logic(env) + setup_loadable_module_logic(env) env['SMARTLINK'] = smart_link env['LINK'] = "$SMARTLINK" @@ -64,20 +59,6 @@ def generate(env): env['LIBLINKPREFIX'] = '-l' env['LIBLINKSUFFIX'] = '' - # For most platforms, a loadable module is the same as a shared - # library. Platforms which are different can override these, but - # setting them the same means that LoadableModule works everywhere. - SCons.Tool.createLoadableModuleBuilder(env) - env['LDMODULE'] = '$SHLINK' - env.Append(LDMODULEEMITTER=[ldmod_emitter]) - env['LDMODULEPREFIX'] = '$SHLIBPREFIX' - env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' - env['LDMODULEFLAGS'] = '$SHLINKFLAGS' - 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/SCons/Tool/linkCommon/LoadableModule.py b/SCons/Tool/linkCommon/LoadableModule.py new file mode 100644 index 0000000..36921f6 --- /dev/null +++ b/SCons/Tool/linkCommon/LoadableModule.py @@ -0,0 +1,97 @@ +from SCons.Tool import createLoadableModuleBuilder +from .SharedLibrary import shlib_symlink_emitter +from . import lib_emitter + + +def ldmod_symlink_emitter(target, source, env, **kw): + return shlib_symlink_emitter(target, source, env, variable_prefix='LDMODULE') + + +def _get_ldmodule_stem(target, source, env, for_signature): + """ + Get the basename for a library (so for libxyz.so, return xyz) + :param target: + :param source: + :param env: + :param for_signature: + :return: + """ + target_name = str(target) + ldmodule_prefix = env.subst('$LDMODULEPREFIX') + ldmodule_suffix = env.subst("$_LDMODULESUFFIX") + + if target_name.startswith(ldmodule_prefix): + target_name = target_name[len(ldmodule_prefix):] + + if target_name.endswith(ldmodule_suffix): + target_name = target_name[:-len(ldmodule_suffix)] + + return target_name + + +def _ldmodule_soversion(target, source, env, for_signature): + """Function to determine what to use for SOVERSION""" + + if 'SOVERSION' in env: + return '.$SOVERSION' + elif 'LDMODULEVERSION' in env: + ldmod_version = env.subst('$LDMODULEVERSION') + # We use only the most significant digit of LDMODULEVERSION + return '.' + ldmod_version.split('.')[0] + else: + return '' + + +def _ldmodule_soname(target, source, env, for_signature): + if 'SONAME' in env: + return '$SONAME' + else: + return "$LDMODULEPREFIX$_get_ldmodule_stem$_LDMODULESOVERSION${LDMODULESUFFIX}" + + +def setup_loadable_module_logic(env): + """ + Just the logic for loadable modules + + For most platforms, a loadable module is the same as a shared + library. Platforms which are different can override these, but + setting them the same means that LoadableModule works everywhere. + + :param env: + :return: + """ + + createLoadableModuleBuilder(env) + + env['_get_ldmodule_stem'] = _get_ldmodule_stem + env['_LDMODULESOVERSION'] = _ldmodule_soversion + env['_LDMODULESONAME'] = _ldmodule_soname + + env['LDMODULENAME'] = '${LDMODULEPREFIX}$_get_ldmodule_stem${_LDMODULESUFFIX}' + + # This is the non versioned LDMODULE filename + # If LDMODULEVERSION is defined then this will symlink to $LDMODULENAME + env['LDMODULE_NOVERSION_SYMLINK'] = '${LDMODULEPREFIX}$_get_ldmodule_stem${LDMODULESUFFIX}' + + # This is the sonamed file name + # If LDMODULEVERSION is defined then this will symlink to $LDMODULENAME + env['LDMODULE_SONAME_SYMLINK'] = '$_LDMODULESONAME' + + env['_LDMODULEVERSION'] = "${LDMODULEVERSION and '.'+LDMODULEVERSION or ''}" + env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME' + + env['LDMODULEEMITTER'] = [lib_emitter, ldmod_symlink_emitter] + + env['LDMODULEPREFIX'] = '$SHLIBPREFIX' + env['_LDMODULESUFFIX'] = '${_LDMODULEVERSION}${LDMODULESUFFIX}' + env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' + + env['LDMODULE'] = '$SHLINK' + + env['LDMODULEFLAGS'] = '$SHLINKFLAGS' + + env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__LDMODULEVERSIONFLAGS $__RPATH $SOURCES ' \ + '$_LIBDIRFLAGS $_LIBFLAGS ' + + env['LDMODULEVERSION'] = '$SHLIBVERSION' + env['LDMODULENOVERSIONSYMLINKS'] = '$SHLIBNOVERSIONSYMLINKS' \ No newline at end of file diff --git a/SCons/Tool/linkCommon/SharedLibrary.py b/SCons/Tool/linkCommon/SharedLibrary.py new file mode 100644 index 0000000..d8f780d --- /dev/null +++ b/SCons/Tool/linkCommon/SharedLibrary.py @@ -0,0 +1,153 @@ +from SCons.Tool import createSharedLibBuilder +from SCons.Util import CLVar +from . import lib_emitter, EmitLibSymlinks, StringizeLibSymlinks + + +def shlib_symlink_emitter(target, source, env, **kw): + verbose = False + + if 'variable_prefix' in kw: + var_prefix = kw['variable_prefix'] + else: + var_prefix = 'SHLIB' + + do_symlinks = env.subst('$%sNOVERSIONSYMLINKS' % var_prefix) + if do_symlinks in ['1', 'True', 'true', True]: + return target, source + + shlibversion = env.subst('$%sVERSION' % var_prefix) + if shlibversion: + if verbose: + print("shlib_symlink_emitter: %sVERSION=%s" % (var_prefix, shlibversion)) + + libnode = target[0] + linkdir = libnode.get_dir() + shlib_soname_symlink = env.subst('$%s_SONAME_SYMLINK' % var_prefix, target=target, source=source) + shlib_noversion_symlink = env.subst('$%s_NOVERSION_SYMLINK' % var_prefix, target=target, source=source) + + if verbose: + print("shlib_soname_symlink :%s" % shlib_soname_symlink) + print("shlib_noversion_symlink :%s" % shlib_noversion_symlink) + print("libnode :%s" % libnode) + + symlinks = [(env.File(shlib_soname_symlink), libnode), + (env.File(shlib_noversion_symlink), libnode)] + + if verbose: + print("_lib_emitter: symlinks={!r}".format( + ', '.join(["%r->%r" % (k, v) for k, v in StringizeLibSymlinks(symlinks)]) + )) + + if symlinks: + # This does the actual symlinking + EmitLibSymlinks(env, symlinks, target[0]) + + # This saves the information so if the versioned shared library is installed + # it can faithfully reproduce the correct symlinks + target[0].attributes.shliblinks = symlinks + + return target, source + + +def _soversion(target, source, env, for_signature): + """Function to determine what to use for SOVERSION""" + + if 'SOVERSION' in env: + return '.$SOVERSION' + elif 'SHLIBVERSION' in env: + shlibversion = env.subst('$SHLIBVERSION') + # We use only the most significant digit of SHLIBVERSION + return '.' + shlibversion.split('.')[0] + else: + return '' + + +def _soname(target, source, env, for_signature): + + if 'SONAME' in env: + # Now verify that SOVERSION is not also set as that is not allowed + if 'SOVERSION' in env: + raise SCons.Errors.UserError('Ambiguous library .so naming, both SONAME: %s and SOVERSION: %s are defined. ' + 'Only one can be defined for a target library.' % (env['SONAME'], env['SOVERSION'])) + return '$SONAME' + else: + return "$_get_shlib_dir$SHLIBPREFIX$_get_shlib_stem$_SHLIBSOVERSION${SHLIBSUFFIX}" + + +def _get_shlib_stem(target, source, env, for_signature): + """ + Get the basename for a library (so for libxyz.so, return xyz) + :param target: + :param source: + :param env: + :param for_signature: + :return: + """ + verbose = False + + target_name = str(target.name) + shlibprefix = env.subst('$SHLIBPREFIX') + shlibsuffix = env.subst("$_SHLIBSUFFIX") + + if verbose and not for_signature: + print("_get_shlib_stem: target_name:%s shlibprefix:%s shlibsuffix:%s"%(target_name, shlibprefix, shlibsuffix)) + + if target_name.startswith(shlibprefix): + target_name = target_name[len(shlibprefix):] + + if target_name.endswith(shlibsuffix): + target_name = target_name[:-len(shlibsuffix)] + + if verbose and not for_signature: + print("_get_shlib_stem: target_name:%s AFTER"%(target_name,)) + + return target_name + + +def _get_shlib_dir(target, source, env, for_signature): + """ + Get the directory the shlib is in. + """ + if target.dir: + return "%s/"%str(target.dir) + else: + return "" + +def setup_shared_lib_logic(env): + """ + Just the logic for shared libraries + :param env: + :return: + """ + createSharedLibBuilder(env) + + env['_get_shlib_stem'] = _get_shlib_stem + env['_get_shlib_dir'] = _get_shlib_dir + env['_SHLIBSOVERSION'] = _soversion + env['_SHLIBSONAME'] = _soname + + env['SHLIBNAME'] = '${_get_shlib_dir}${SHLIBPREFIX}$_get_shlib_stem${_SHLIBSUFFIX}' + + # This is the non versioned shlib filename + # If SHLIBVERSION is defined then this will symlink to $SHLIBNAME + env['SHLIB_NOVERSION_SYMLINK'] = '${_get_shlib_dir}${SHLIBPREFIX}$_get_shlib_stem${SHLIBSUFFIX}' + + # This is the sonamed file name + # If SHLIBVERSION is defined then this will symlink to $SHLIBNAME + env['SHLIB_SONAME_SYMLINK'] = '$_SHLIBSONAME' + + # Note this is gnu style + env['SHLIBSONAMEFLAGS'] = '-Wl,-soname=$_SHLIBSONAME' + env['_SHLIBVERSION'] = "${SHLIBVERSION and '.'+SHLIBVERSION or ''}" + env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME' + + env['SHLIBEMITTER'] = [lib_emitter, shlib_symlink_emitter] + + env['SHLIBPREFIX'] = 'lib' + env['_SHLIBSUFFIX'] = '${SHLIBSUFFIX}${_SHLIBVERSION}' + + env['SHLINKFLAGS'] = CLVar('$LINKFLAGS -shared') + + env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['SHLINKCOMSTR'] = '$SHLINKCOM' + env['SHLINK'] = '$LINK' diff --git a/SCons/Tool/linkCommon/__init__.py b/SCons/Tool/linkCommon/__init__.py index c49bc23..7aaffab 100644 --- a/SCons/Tool/linkCommon/__init__.py +++ b/SCons/Tool/linkCommon/__init__.py @@ -25,396 +25,12 @@ Common link/shared library logic """ -import os -import re -import sys -from typing import Callable - import SCons.Util import SCons.Warnings from SCons.Tool.DCommon import isD 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) - 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') +issued_mixed_link_warning = False def StringizeLibSymlinks(symlinks): @@ -455,8 +71,8 @@ def CreateLibSymlinks(env, symlinks): 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() @@ -491,13 +107,13 @@ def LibSymlinksStrFun(target, source, env, *args): if symlinks: if cmd is None: cmd = "" if cmd: cmd += "\n" - cmd += "Create symlinks for: %r" % tgt.get_path() + cmd += "Create symlinks for: %r\n " % tgt.get_path() try: - linkstr = ', '.join(["%r->%r" % (k, v) for k, v in StringizeLibSymlinks(symlinks)]) + linkstr = '\n '.join(["%r->%r" % (k, v) for k, v in StringizeLibSymlinks(symlinks)]) except (KeyError, ValueError): pass else: - cmd += ": %s" % linkstr + cmd += "%s" % linkstr return cmd @@ -539,235 +155,13 @@ def smart_link(source, target, env, for_signature): return '$CC' -def _lib_emitter(target, source, env, **kw): - Verbose = False - if Verbose: +def lib_emitter(target, source, env, **kw): + verbose = False + if verbose: print("_lib_emitter: target[0]={!r}".format(target[0].get_path())) for tgt in target: if SCons.Util.is_String(tgt): tgt = env.File(tgt) tgt.attributes.shared = 1 - try: - symlink_generator = kw['symlink_generator'] - except KeyError: - pass - else: - if Verbose: - print("_lib_emitter: symlink_generator={!r}".format(symlink_generator)) - symlinks = symlink_generator(env, target[0]) - if Verbose: - print("_lib_emitter: symlinks={!r}".format(symlinks)) - - if symlinks: - 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=ShLibSymlinkGenerator) - - -def ldmod_emitter(target, source, env): - return _lib_emitter(target, source, env, symlink_generator=LdModSymlinkGenerator) - - -def _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, suffix_generator, **kw): - """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so'""" - Verbose = False - - if Verbose: - print("_versioned_lib_name: libnode={!r}".format(libnode.get_path())) - print("_versioned_lib_name: version={!r}".format(version)) - print("_versioned_lib_name: prefix={!r}".format(prefix)) - print("_versioned_lib_name: suffix={!r}".format(suffix)) - print("_versioned_lib_name: suffix_generator={!r}".format(suffix_generator)) - - versioned_name = os.path.basename(libnode.get_path()) - if Verbose: - print("_versioned_lib_name: versioned_name={!r}".format(versioned_name)) - - versioned_prefix = prefix_generator(env, **kw) - versioned_suffix = suffix_generator(env, **kw) - if Verbose: - print("_versioned_lib_name: versioned_prefix={!r}".format(versioned_prefix)) - print("_versioned_lib_name: versioned_suffix={!r}".format(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}".format(name)) - return name - - -def _versioned_shlib_name(env, libnode, version, prefix, suffix, **kw): - 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 = LdModPrefixGenerator - suffix_generator = LdModSuffixGenerator - return _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, suffix_generator, **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}".format(suffix)) - print("_versioned_lib_suffix: version={!r}".format(version)) - if not suffix.endswith(version): - suffix = suffix + '.' + version - if Verbose: - print("_versioned_lib_suffix: return suffix={!r}".format(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}".format(version)) - name = name_func(env, libnode, version, prefix, suffix) - if Verbose: - print("_versioned_lib_soname: name={!r}".format(name)) - major = version.split('.')[0] - - # if a desired SONAME was supplied, use that, otherwise create - # a default from the major version - if env.get('SONAME'): - soname = ShLibSonameGenerator(env, libnode) - else: - soname = name + '.' + major - if Verbose: - print("_versioned_lib_soname: soname={!r}".format(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 library. - Returns a dictionary in the form { linkname : linktarget } - """ - Verbose = False - - if Verbose: - print("_versioned_lib_symlinks: libnode={!r}".format(libnode.get_path())) - print("_versioned_lib_symlinks: version={!r}".format(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}".format(None)) - return None - - linkdir = libnode.get_dir() - if Verbose: - print("_versioned_lib_symlinks: linkdir={!r}".format(linkdir.get_path())) - - name = name_func(env, libnode, version, prefix, suffix) - if Verbose: - print("_versioned_lib_symlinks: name={!r}".format(name)) - - soname = soname_func(env, libnode, version, prefix, suffix) - if Verbose: - print("_versioned_lib_symlinks: soname={!r}".format(soname)) - - 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}".format( - StringizeLibSymlinks(symlinks))) - - return symlinks - - -def _versioned_shlib_symlinks(env, libnode, version, prefix, suffix): - name_func = env['LINKCALLBACKS']['VersionedShLibName'] - soname_func = env['LINKCALLBACKS']['VersionedShLibSoname'] - - return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, soname_func) - - -def _versioned_ldmod_symlinks(env, libnode, version, prefix, suffix): - name_func = _versioned_ldmod_name - soname_func = _versioned_ldmod_soname - - name_func = env['LINKCALLBACKS']['VersionedLdModName'] - soname_func = env['LINKCALLBACKS']['VersionedLdModSoname'] - - return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, soname_func) - - -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() - - -issued_mixed_link_warning = False - - -def _setup_versioned_lib_variables(env, **kw): - """ - Setup all variables required by the versioning machinery - """ - - 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 - env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME' - env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME' - env['_SHLIBSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' - env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}' - env['ShLibSonameGenerator'] = ShLibSonameGenerator - env['LdModSonameGenerator'] = LdModSonameGenerator - else: - env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' - env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' - - # LDOMDULVERSIONFLAGS should always default to $SHLIBVERSIONFLAGS - env['LDMODULEVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' \ No newline at end of file diff --git a/SCons/Tool/sunlink.py b/SCons/Tool/sunlink.py index f668903..b0c9816 100644 --- a/SCons/Tool/sunlink.py +++ b/SCons/Tool/sunlink.py @@ -8,6 +8,10 @@ selection method. """ # +# MIT License +# +# Copyright The SCons Foundation +# # 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,9 +34,7 @@ selection method. import os.path -import SCons.Tool.linkCommon as linkCommon import SCons.Util - from . import link ccLinker = None @@ -52,23 +54,20 @@ for d in dirs: ccLinker = linker break + def generate(env): """Add Builders and construction variables for Forte to an Environment.""" link.generate(env) - + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') env['RPATHPREFIX'] = '-R' env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - # Support for versioned libraries - linkCommon._setup_versioned_lib_variables(env, tool='sunlink', use_soname=True) - env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLIBSONAME' env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME' - env['LINKCALLBACKS'] = linkCommon._versioned_lib_callbacks() def exists(env): return ccLinker diff --git a/SCons/__init__.py b/SCons/__init__.py index fcd665e..c9ca70f 100644 --- a/SCons/__init__.py +++ b/SCons/__init__.py @@ -1,9 +1,9 @@ __version__="4.0.1.9998" __copyright__="Copyright (c) 2001 - 2020 The SCons Foundation" __developer__="bdbaddog" -__date__="2020-10-09 19:00:35" +__date__="2020-12-21 01:23:32" __buildsys__="ProDog2020" -__revision__="93525bed88d19a00f5de400086c0046011d3b833" -__build__="93525bed88d19a00f5de400086c0046011d3b833" +__revision__="78292be9cbb471101e6ed4ec5f6da0161730c656" +__build__="78292be9cbb471101e6ed4ec5f6da0161730c656" # make sure compatibility is always in place import SCons.compat # noqa \ No newline at end of file diff --git a/test/LINK/SHLIBVERSIONFLAGS.py b/test/LINK/SHLIBVERSIONFLAGS.py index 7bcabf0..7832862 100644 --- a/test/LINK/SHLIBVERSIONFLAGS.py +++ b/test/LINK/SHLIBVERSIONFLAGS.py @@ -25,10 +25,10 @@ # - import TestSCons -import SCons.Platform + import SCons.Defaults +import SCons.Platform foo_c_src = "void foo() {}\n" @@ -39,34 +39,33 @@ tool_list = SCons.Platform.DefaultToolList(platform, env) test = TestSCons.TestSCons() if 'gnulink' in tool_list: versionflags = r".+ -Wl,-soname=libfoo.so.1( .+)+" - soname='libfoo.so.4' - sonameVersionFlags=r".+ -Wl,-soname=%s( .+)+" % soname + soname = 'libfoo.so.4' + sonameVersionFlags = r".+ -Wl,-soname=%s( .+)+" % soname elif 'sunlink' in tool_list: versionflags = r".+ -h libfoo.so.1( .+)+" - soname='libfoo.so.4' - sonameVersionFlags=r".+ -h %s( .+)+" % soname + soname = 'libfoo.so.4' + sonameVersionFlags = r".+ -h %s( .+)+" % soname elif 'applelink' in tool_list: - versionflags = r".+ 'libfoo.1.dylib'->'libfoo.1.2.3.dylib'(.+)+" - soname='libfoo.4.dylib' - sonameVersionFlags=r".+ '%s'->'libfoo.1.2.3.dylib'(.+)+" % soname + versionflags = r" 'libfoo.1.dylib'->'libfoo.1.2.3.dylib'" + soname = 'libfoo.4.dylib' + sonameVersionFlags = r" '%s'->'libfoo.1.2.3.dylib'(.+)+" % soname else: test.skip_test('No testable linkers found, skipping the test\n') - # stdout must not contain SHLIBVERSIONFLAGS if there is no SHLIBVERSION provided test.write('foo.c', foo_c_src) test.write('SConstruct', "SharedLibrary('foo','foo.c')\n") test.run() test.fail_test(test.match_re_dotall(test.stdout(), versionflags)) -test.run(arguments = ['-c']) +test.run(arguments=['-c']) # stdout must contain SHLIBVERSIONFLAGS if there is SHLIBVERSION provided test = TestSCons.TestSCons() test.write('foo.c', foo_c_src) test.write('SConstruct', "SharedLibrary('foo','foo.c',SHLIBVERSION='1.2.3')\n") -test.run(stdout = versionflags, match = TestSCons.match_re_dotall) -test.run(arguments = ['-c']) +test.run(stdout=versionflags, match=TestSCons.match_re_dotall) +test.run(arguments=['-c']) # stdout must contain SONAME if there is SONAME provided test = TestSCons.TestSCons() @@ -74,9 +73,9 @@ test.write('foo.c', foo_c_src) test.write('SConstruct', """ SharedLibrary('foo','foo.c',SHLIBVERSION='1.2.3',SONAME='%s') """ % soname) -test.run(stdout = sonameVersionFlags, match = TestSCons.match_re_dotall) +test.run(stdout=sonameVersionFlags, match=TestSCons.match_re_dotall) test.must_exist(test.workpath(soname)) -test.run(arguments = ['-c']) +test.run(arguments=['-c']) # stdout must contain SOVERSION if there is SOVERSION provided test = TestSCons.TestSCons() @@ -84,9 +83,9 @@ test.write('foo.c', foo_c_src) test.write('SConstruct', """ SharedLibrary('foo','foo.c',SHLIBVERSION='1.2.3',SOVERSION='4') """) -test.run(stdout = sonameVersionFlags, match = TestSCons.match_re_dotall) +test.run(stdout=sonameVersionFlags, match=TestSCons.match_re_dotall) test.must_exist(test.workpath(soname)) -test.run(arguments = ['-c']) +test.run(arguments=['-c']) # test if both SONAME and SOVERSION are used test = TestSCons.TestSCons() @@ -94,7 +93,7 @@ test.write('foo.c', foo_c_src) test.write('SConstruct', """ SharedLibrary('foo','foo.c',SHLIBVERSION='1.2.3',SONAME='%s',SOVERSION='4') """ % soname) -test.run(status=2,stderr=None) +test.run(status=2, stderr=None) test.must_contain_all_lines(test.stderr(), ['Ambiguous library .so naming']) test.pass_test() -- cgit v0.12