1"""SCons.Tool.link 2 3Tool-specific initialization for the generic Posix linker. 4 5There normally shouldn't be any need to import this module directly. 6It will usually be imported through the generic SCons.Tool.Tool() 7selection method. 8 9""" 10 11# 12# Copyright (c) 2001 - 2016 The SCons Foundation 13# 14# Permission is hereby granted, free of charge, to any person obtaining 15# a copy of this software and associated documentation files (the 16# "Software"), to deal in the Software without restriction, including 17# without limitation the rights to use, copy, modify, merge, publish, 18# distribute, sublicense, and/or sell copies of the Software, and to 19# permit persons to whom the Software is furnished to do so, subject to 20# the following conditions: 21# 22# The above copyright notice and this permission notice shall be included 23# in all copies or substantial portions of the Software. 24# 25# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 26# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 27# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 29# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 30# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 31# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32# 33 34__revision__ = "src/engine/SCons/Tool/link.py rel_2.5.0:3543:937e55cd78f7 2016/04/09 11:29:54 bdbaddog" 35 36import sys 37import re 38import os 39 40import SCons.Tool 41import SCons.Util 42import SCons.Warnings 43 44from SCons.Tool.FortranCommon import isfortran 45 46from SCons.Tool.DCommon import isD 47 48cplusplus = __import__('c++', globals(), locals(), []) 49 50issued_mixed_link_warning = False 51 52def smart_link(source, target, env, for_signature): 53 has_cplusplus = cplusplus.iscplusplus(source) 54 has_fortran = isfortran(env, source) 55 has_d = isD(env, source) 56 if has_cplusplus and has_fortran and not has_d: 57 global issued_mixed_link_warning 58 if not issued_mixed_link_warning: 59 msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \ 60 "This may generate a buggy executable if the '%s'\n\t" + \ 61 "compiler does not know how to deal with Fortran runtimes." 62 SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning, 63 msg % env.subst('$CXX')) 64 issued_mixed_link_warning = True 65 return '$CXX' 66 elif has_d: 67 env['LINKCOM'] = env['DLINKCOM'] 68 env['SHLINKCOM'] = env['SHDLINKCOM'] 69 return '$DC' 70 elif has_fortran: 71 return '$FORTRAN' 72 elif has_cplusplus: 73 return '$CXX' 74 return '$CC' 75 76def _lib_emitter(target, source, env, **kw): 77 Verbose = False 78 if Verbose: 79 print "_lib_emitter: target[0]=%r" % target[0].get_path() 80 for tgt in target: 81 tgt.attributes.shared = 1 82 83 try: 84 symlink_generator = kw['symlink_generator'] 85 except KeyError: 86 pass 87 else: 88 if Verbose: 89 print "_lib_emitter: symlink_generator=%r" % symlink_generator 90 symlinks = symlink_generator(env, target[0]) 91 if Verbose: 92 print "_lib_emitter: symlinks=%r" % symlinks 93 94 if symlinks: 95 SCons.Tool.EmitLibSymlinks(env, symlinks, target[0]) 96 target[0].attributes.shliblinks = symlinks 97 return (target, source) 98 99def shlib_emitter(target, source, env): 100 return _lib_emitter(target, source, env, symlink_generator = SCons.Tool.ShLibSymlinkGenerator) 101 102def ldmod_emitter(target, source, env): 103 return _lib_emitter(target, source, env, symlink_generator = SCons.Tool.LdModSymlinkGenerator) 104 105# This is generic enough to be included here... 106def _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, suffix_generator, **kw): 107 """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so'""" 108 Verbose = False 109 110 if Verbose: 111 print "_versioned_lib_name: libnode=%r" % libnode.get_path() 112 print "_versioned_lib_name: version=%r" % version 113 print "_versioned_lib_name: prefix=%r" % prefix 114 print "_versioned_lib_name: suffix=%r" % suffix 115 print "_versioned_lib_name: suffix_generator=%r" % suffix_generator 116 117 versioned_name = os.path.basename(libnode.get_path()) 118 if Verbose: 119 print "_versioned_lib_name: versioned_name=%r" % versioned_name 120 121 versioned_prefix = prefix_generator(env, **kw) 122 versioned_suffix = suffix_generator(env, **kw) 123 if Verbose: 124 print "_versioned_lib_name: versioned_prefix=%r" % versioned_prefix 125 print "_versioned_lib_name: versioned_suffix=%r" % versioned_suffix 126 127 versioned_prefix_re = '^' + re.escape(versioned_prefix) 128 versioned_suffix_re = re.escape(versioned_suffix) + '$' 129 name = re.sub(versioned_prefix_re, prefix, versioned_name) 130 name = re.sub(versioned_suffix_re, suffix, name) 131 if Verbose: 132 print "_versioned_lib_name: name=%r" % name 133 return name 134 135def _versioned_shlib_name(env, libnode, version, prefix, suffix, **kw): 136 pg = SCons.Tool.ShLibPrefixGenerator 137 sg = SCons.Tool.ShLibSuffixGenerator 138 return _versioned_lib_name(env, libnode, version, prefix, suffix, pg, sg, **kw) 139 140def _versioned_ldmod_name(env, libnode, version, prefix, suffix, **kw): 141 pg = SCons.Tool.LdModPrefixGenerator 142 sg = SCons.Tool.LdModSuffixGenerator 143 return _versioned_lib_name(env, libnode, version, prefix, suffix, pg, sg, **kw) 144 145def _versioned_lib_suffix(env, suffix, version): 146 """For suffix='.so' and version='0.1.2' it returns '.so.0.1.2'""" 147 Verbose = False 148 if Verbose: 149 print "_versioned_lib_suffix: suffix=%r" % suffix 150 print "_versioned_lib_suffix: version=%r" % version 151 if not suffix.endswith(version): 152 suffix = suffix + '.' + version 153 if Verbose: 154 print "_versioned_lib_suffix: return suffix=%r" % suffix 155 return suffix 156 157def _versioned_lib_soname(env, libnode, version, prefix, suffix, name_func): 158 """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so.X'""" 159 Verbose = False 160 if Verbose: 161 print "_versioned_lib_soname: version=%r" % version 162 name = name_func(env, libnode, version, prefix, suffix) 163 if Verbose: 164 print "_versioned_lib_soname: name=%r" % name 165 major = version.split('.')[0] 166 soname = name + '.' + major 167 if Verbose: 168 print "_versioned_lib_soname: soname=%r" % soname 169 return soname 170 171def _versioned_shlib_soname(env, libnode, version, prefix, suffix): 172 return _versioned_lib_soname(env, libnode, version, prefix, suffix, _versioned_shlib_name) 173 174def _versioned_ldmod_soname(env, libnode, version, prefix, suffix): 175 return _versioned_lib_soname(env, libnode, version, prefix, suffix, _versioned_ldmod_name) 176 177def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, soname_func): 178 """Generate link names that should be created for a versioned shared lirbrary. 179 Returns a dictionary in the form { linkname : linktarget } 180 """ 181 Verbose = False 182 183 if Verbose: 184 print "_versioned_lib_symlinks: libnode=%r" % libnode.get_path() 185 print "_versioned_lib_symlinks: version=%r" % version 186 187 if sys.platform.startswith('openbsd'): 188 # OpenBSD uses x.y shared library versioning numbering convention 189 # and doesn't use symlinks to backwards-compatible libraries 190 if Verbose: 191 print "_versioned_lib_symlinks: return symlinks=%r" % None 192 return None 193 194 linkdir = libnode.get_dir() 195 if Verbose: 196 print "_versioned_lib_symlinks: linkdir=%r" % linkdir.get_path() 197 198 name = name_func(env, libnode, version, prefix, suffix) 199 if Verbose: 200 print "_versioned_lib_symlinks: name=%r" % name 201 202 soname = soname_func(env, libnode, version, prefix, suffix) 203 204 link0 = env.fs.File(soname, linkdir) 205 link1 = env.fs.File(name, linkdir) 206 207 # We create direct symlinks, not daisy-chained. 208 if link0 == libnode: 209 # This enables SHLIBVERSION without periods (e.g. SHLIBVERSION=1) 210 symlinks = [ (link1, libnode) ] 211 else: 212 # This handles usual SHLIBVERSION, i.e. '1.2', '1.2.3', etc. 213 symlinks = [ (link0, libnode), (link1, libnode) ] 214 215 if Verbose: 216 print "_versioned_lib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks) 217 218 return symlinks 219 220def _versioned_shlib_symlinks(env, libnode, version, prefix, suffix): 221 nf = _versioned_shlib_name 222 sf = _versioned_shlib_soname 223 return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) 224 225def _versioned_ldmod_symlinks(env, libnode, version, prefix, suffix): 226 nf = _versioned_ldmod_name 227 sf = _versioned_ldmod_soname 228 return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf) 229 230def _versioned_lib_callbacks(): 231 return { 232 'VersionedShLibSuffix' : _versioned_lib_suffix, 233 'VersionedLdModSuffix' : _versioned_lib_suffix, 234 'VersionedShLibSymlinks' : _versioned_shlib_symlinks, 235 'VersionedLdModSymlinks' : _versioned_ldmod_symlinks, 236 'VersionedShLibName' : _versioned_shlib_name, 237 'VersionedLdModName' : _versioned_ldmod_name, 238 'VersionedShLibSoname' : _versioned_shlib_soname, 239 'VersionedLdModSoname' : _versioned_ldmod_soname, 240 }.copy() 241 242# Setup all variables required by the versioning machinery 243def _setup_versioned_lib_variables(env, **kw): 244 245 tool = None 246 try: tool = kw['tool'] 247 except KeyError: pass 248 249 use_soname = False 250 try: use_soname = kw['use_soname'] 251 except KeyError: pass 252 253 # The $_SHLIBVERSIONFLAGS define extra commandline flags used when 254 # building VERSIONED shared libraries. It's always set, but used only 255 # when VERSIONED library is built (see __SHLIBVERSIONFLAGS in SCons/Defaults.py). 256 if use_soname: 257 # If the linker uses SONAME, then we need this little automata 258 if tool == 'sunlink': 259 env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLIBSONAME' 260 env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME' 261 else: 262 env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME' 263 env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME' 264 env['_SHLIBSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}' 265 env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}' 266 env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator 267 env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator 268 else: 269 env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' 270 env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS' 271 272 # LDOMDULVERSIONFLAGS should always default to $SHLIBVERSIONFLAGS 273 env['LDMODULEVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS' 274 275 276def generate(env): 277 """Add Builders and construction variables for gnulink to an Environment.""" 278 SCons.Tool.createSharedLibBuilder(env) 279 SCons.Tool.createProgBuilder(env) 280 281 env['SHLINK'] = '$LINK' 282 env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') 283 env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' 284 # don't set up the emitter, cause AppendUnique will generate a list 285 # starting with None :-( 286 env.Append(SHLIBEMITTER = [shlib_emitter]) 287 env['SMARTLINK'] = smart_link 288 env['LINK'] = "$SMARTLINK" 289 env['LINKFLAGS'] = SCons.Util.CLVar('') 290 # __RPATH is only set to something ($_RPATH typically) on platforms that support it. 291 env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' 292 env['LIBDIRPREFIX']='-L' 293 env['LIBDIRSUFFIX']='' 294 env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' 295 env['LIBLINKPREFIX']='-l' 296 env['LIBLINKSUFFIX']='' 297 298 if env['PLATFORM'] == 'hpux': 299 env['SHLIBSUFFIX'] = '.sl' 300 elif env['PLATFORM'] == 'aix': 301 env['SHLIBSUFFIX'] = '.a' 302 303 # For most platforms, a loadable module is the same as a shared 304 # library. Platforms which are different can override these, but 305 # setting them the same means that LoadableModule works everywhere. 306 SCons.Tool.createLoadableModuleBuilder(env) 307 env['LDMODULE'] = '$SHLINK' 308 env.Append(LDMODULEEMITTER = [ldmod_emitter]) 309 env['LDMODULEPREFIX'] = '$SHLIBPREFIX' 310 env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' 311 env['LDMODULEFLAGS'] = '$SHLINKFLAGS' 312 env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__LDMODULEVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' 313 env['LDMODULEVERSION'] = '$SHLIBVERSION' 314 env['LDMODULENOVERSIONSYMLINKS'] = '$SHLIBNOVERSIONSYMLINKS' 315 316def exists(env): 317 # This module isn't really a Tool on its own, it's common logic for 318 # other linkers. 319 return None 320 321# Local Variables: 322# tab-width:4 323# indent-tabs-mode:nil 324# End: 325# vim: set expandtab tabstop=4 shiftwidth=4: 326