renamed SCons.Tool.ninja -> SCons.Tool.ninja_tool and added alias in tool loading...
[scons.git] / SCons / Tool / linkCommon / __init__.py
blob6a558d81c5c1c75f4ae98ed9ea63c9fc4061b806
1 # MIT License
3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """
25 Common link/shared library logic
26 """
28 import SCons.Util
29 import SCons.Warnings
30 from SCons.Tool.DCommon import isD
31 from SCons.Util import is_List
33 issued_mixed_link_warning = False
36 def StringizeLibSymlinks(symlinks):
37 """Converts list with pairs of nodes to list with pairs of node paths
38 (strings). Used mainly for debugging."""
39 if is_List(symlinks):
40 try:
41 return [(k.get_path(), v.get_path()) for k, v in symlinks]
42 except (TypeError, ValueError):
43 return symlinks
44 else:
45 return symlinks
48 def EmitLibSymlinks(env, symlinks, libnode, **kw) -> None:
49 """Used by emitters to handle (shared/versioned) library symlinks"""
50 Verbose = False
52 # nodes involved in process... all symlinks + library
53 nodes = list(set([x for x, y in symlinks] + [libnode]))
55 clean_targets = kw.get('clean_targets', [])
56 if not is_List(clean_targets):
57 clean_targets = [clean_targets]
59 for link, linktgt in symlinks:
60 env.SideEffect(link, linktgt)
61 if Verbose:
62 print("EmitLibSymlinks: SideEffect(%r,%r)" % (link.get_path(), linktgt.get_path()))
63 clean_list = [x for x in nodes if x != linktgt]
64 env.Clean(list(set([linktgt] + clean_targets)), clean_list)
65 if Verbose:
66 print("EmitLibSymlinks: Clean(%r,%r)" % (linktgt.get_path(), [x.get_path() for x in clean_list]))
69 def CreateLibSymlinks(env, symlinks) -> int:
70 """Physically creates symlinks. The symlinks argument must be a list in
71 form [ (link, linktarget), ... ], where link and linktarget are SCons
72 nodes.
73 """
74 Verbose = False
76 for link, linktgt in symlinks:
77 linktgt = link.get_dir().rel_path(linktgt)
78 link = link.get_path()
79 if Verbose:
80 print("CreateLibSymlinks: preparing to add symlink %r -> %r" % (link, linktgt))
81 # Delete the (previously created) symlink if exists. Let only symlinks
82 # to be deleted to prevent accidental deletion of source files...
83 if env.fs.islink(link):
84 env.fs.unlink(link)
85 if Verbose:
86 print("CreateLibSymlinks: removed old symlink %r" % link)
87 # If a file or directory exists with the same name as link, an OSError
88 # will be thrown, which should be enough, I think.
89 env.fs.symlink(linktgt, link)
90 if Verbose:
91 print("CreateLibSymlinks: add symlink %r -> %r" % (link, linktgt))
92 return 0
95 def LibSymlinksActionFunction(target, source, env) -> int:
96 for tgt in target:
97 symlinks = getattr(getattr(tgt, 'attributes', None), 'shliblinks', None)
98 if symlinks:
99 CreateLibSymlinks(env, symlinks)
100 return 0
103 def LibSymlinksStrFun(target, source, env, *args):
104 cmd = None
105 for tgt in target:
106 symlinks = getattr(getattr(tgt, 'attributes', None), 'shliblinks', None)
107 if symlinks:
108 if cmd is None: cmd = ""
109 if cmd: cmd += "\n"
110 cmd += "Create symlinks for: %r\n " % tgt.get_path()
111 try:
112 linkstr = '\n '.join(["%r->%r" % (k, v) for k, v in StringizeLibSymlinks(symlinks)])
113 except (KeyError, ValueError):
114 pass
115 else:
116 cmd += "%s" % linkstr
117 return cmd
120 def _call_env_subst(env, string, *args, **kw):
121 kw2 = {}
122 for k in ('raw', 'target', 'source', 'conv', 'executor'):
123 try:
124 kw2[k] = kw[k]
125 except KeyError:
126 pass
127 return env.subst(string, *args, **kw2)
130 def smart_link(source, target, env, for_signature) -> str:
131 import SCons.Tool.cxx
132 import SCons.Tool.FortranCommon
134 has_cplusplus = SCons.Tool.cxx.iscplusplus(source)
135 has_fortran = SCons.Tool.FortranCommon.isfortran(env, source)
136 has_d = isD(env, source)
137 if has_cplusplus and has_fortran and not has_d:
138 global issued_mixed_link_warning
139 if not issued_mixed_link_warning:
140 msg = (
141 "Using $CXX to link Fortran and C++ code together.\n"
142 " This may generate a buggy executable if the '%s'\n"
143 " compiler does not know how to deal with Fortran runtimes."
145 SCons.Warnings.warn(
146 SCons.Warnings.FortranCxxMixWarning, msg % env.subst('$CXX')
148 issued_mixed_link_warning = True
149 return '$CXX'
150 elif has_d:
151 env['LINKCOM'] = env['DLINKCOM']
152 env['SHLINKCOM'] = env['SHDLINKCOM']
153 return '$DC'
154 elif has_fortran:
155 return '$FORTRAN'
156 elif has_cplusplus:
157 return '$CXX'
158 return '$CC'
161 def lib_emitter(target, source, env, **kw):
162 verbose = False
163 if verbose:
164 print(f"_lib_emitter: target[0]={target[0].get_path()!r}")
165 for tgt in target:
166 if SCons.Util.is_String(tgt):
167 tgt = env.File(tgt)
168 tgt.attributes.shared = True
170 return target, source