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