506bd12445b7b2e79e43a94a3459d3fb787dec70
[amitay/samba.git] / third_party / waf / waflib / Tools / ccroot.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Thomas Nagy, 2005-2016 (ita)
8
9 """
10 Classes and methods shared by tools providing support for C-like language such
11 as C/C++/D/Assembly/Go (this support module is almost never used alone).
12 """
13
14 import os, re
15 from waflib import Task, Utils, Node, Errors, Logs
16 from waflib.TaskGen import after_method, before_method, feature, taskgen_method, extension
17 from waflib.Tools import c_aliases, c_preproc, c_config, c_osx, c_tests
18 from waflib.Configure import conf
19
20 SYSTEM_LIB_PATHS = ['/usr/lib64', '/usr/lib', '/usr/local/lib64', '/usr/local/lib']
21
22 USELIB_VARS = Utils.defaultdict(set)
23 """
24 Mapping for features to :py:class:`waflib.ConfigSet.ConfigSet` variables. See :py:func:`waflib.Tools.ccroot.propagate_uselib_vars`.
25 """
26
27 USELIB_VARS['c']        = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CCDEPS', 'CFLAGS', 'ARCH'])
28 USELIB_VARS['cxx']      = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CXXDEPS', 'CXXFLAGS', 'ARCH'])
29 USELIB_VARS['d']        = set(['INCLUDES', 'DFLAGS'])
30 USELIB_VARS['includes'] = set(['INCLUDES', 'FRAMEWORKPATH', 'ARCH'])
31
32 USELIB_VARS['cprogram'] = USELIB_VARS['cxxprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH', 'LDFLAGS'])
33 USELIB_VARS['cshlib']   = USELIB_VARS['cxxshlib']   = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH', 'LDFLAGS'])
34 USELIB_VARS['cstlib']   = USELIB_VARS['cxxstlib']   = set(['ARFLAGS', 'LINKDEPS'])
35
36 USELIB_VARS['dprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
37 USELIB_VARS['dshlib']   = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
38 USELIB_VARS['dstlib']   = set(['ARFLAGS', 'LINKDEPS'])
39
40 USELIB_VARS['asm'] = set(['ASFLAGS'])
41
42 # =================================================================================================
43
44 @taskgen_method
45 def create_compiled_task(self, name, node):
46         """
47         Create the compilation task: c, cxx, asm, etc. The output node is created automatically (object file with a typical **.o** extension).
48         The task is appended to the list *compiled_tasks* which is then used by :py:func:`waflib.Tools.ccroot.apply_link`
49
50         :param name: name of the task class
51         :type name: string
52         :param node: the file to compile
53         :type node: :py:class:`waflib.Node.Node`
54         :return: The task created
55         :rtype: :py:class:`waflib.Task.Task`
56         """
57         out = '%s.%d.o' % (node.name, self.idx)
58         task = self.create_task(name, node, node.parent.find_or_declare(out))
59         try:
60                 self.compiled_tasks.append(task)
61         except AttributeError:
62                 self.compiled_tasks = [task]
63         return task
64
65 @taskgen_method
66 def to_incnodes(self, inlst):
67         """
68         Task generator method provided to convert a list of string/nodes into a list of includes folders.
69
70         The paths are assumed to be relative to the task generator path, except if they begin by **#**
71         in which case they are searched from the top-level directory (``bld.srcnode``).
72         The folders are simply assumed to be existing.
73
74         The node objects in the list are returned in the output list. The strings are converted
75         into node objects if possible. The node is searched from the source directory, and if a match is found,
76         the equivalent build directory is created and added to the returned list too. When a folder cannot be found, it is ignored.
77
78         :param inlst: list of folders
79         :type inlst: space-delimited string or a list of string/nodes
80         :rtype: list of :py:class:`waflib.Node.Node`
81         :return: list of include folders as nodes
82         """
83         lst = []
84         seen = set()
85         for x in self.to_list(inlst):
86                 if x in seen or not x:
87                         continue
88                 seen.add(x)
89
90                 # with a real lot of targets, it is sometimes interesting to cache the results below
91                 if isinstance(x, Node.Node):
92                         lst.append(x)
93                 else:
94                         if os.path.isabs(x):
95                                 lst.append(self.bld.root.make_node(x) or x)
96                         else:
97                                 if x[0] == '#':
98                                         p = self.bld.bldnode.make_node(x[1:])
99                                         v = self.bld.srcnode.make_node(x[1:])
100                                 else:
101                                         p = self.path.get_bld().make_node(x)
102                                         v = self.path.make_node(x)
103                                 if p.is_child_of(self.bld.bldnode):
104                                         p.mkdir()
105                                 lst.append(p)
106                                 lst.append(v)
107         return lst
108
109 @feature('c', 'cxx', 'd', 'asm', 'fc', 'includes')
110 @after_method('propagate_uselib_vars', 'process_source')
111 def apply_incpaths(self):
112         """
113         Task generator method that processes the attribute *includes*::
114
115                 tg = bld(features='includes', includes='.')
116
117         The folders only need to be relative to the current directory, the equivalent build directory is
118         added automatically (for headers created in the build directory). This enable using a build directory
119         or not (``top == out``).
120
121         This method will add a list of nodes read by :py:func:`waflib.Tools.ccroot.to_incnodes` in ``tg.env.INCPATHS``,
122         and the list of include paths in ``tg.env.INCLUDES``.
123         """
124
125         lst = self.to_incnodes(self.to_list(getattr(self, 'includes', [])) + self.env.INCLUDES)
126         self.includes_nodes = lst
127         cwd = self.get_cwd()
128         self.env.INCPATHS = [x.path_from(cwd) for x in lst]
129
130 class link_task(Task.Task):
131         """
132         Base class for all link tasks. A task generator is supposed to have at most one link task bound in the attribute *link_task*. See :py:func:`waflib.Tools.ccroot.apply_link`.
133
134         .. inheritance-diagram:: waflib.Tools.ccroot.stlink_task waflib.Tools.c.cprogram waflib.Tools.c.cshlib waflib.Tools.cxx.cxxstlib  waflib.Tools.cxx.cxxprogram waflib.Tools.cxx.cxxshlib waflib.Tools.d.dprogram waflib.Tools.d.dshlib waflib.Tools.d.dstlib waflib.Tools.ccroot.fake_shlib waflib.Tools.ccroot.fake_stlib waflib.Tools.asm.asmprogram waflib.Tools.asm.asmshlib waflib.Tools.asm.asmstlib
135         """
136         color   = 'YELLOW'
137
138         inst_to = None
139         """Default installation path for the link task outputs, or None to disable"""
140
141         chmod   = Utils.O755
142         """Default installation mode for the link task outputs"""
143
144         def add_target(self, target):
145                 """
146                 Process the *target* attribute to add the platform-specific prefix/suffix such as *.so* or *.exe*.
147                 The settings are retrieved from ``env.clsname_PATTERN``
148                 """
149                 if isinstance(target, str):
150                         base = self.generator.path
151                         if target.startswith('#'):
152                                 # for those who like flat structures
153                                 target = target[1:]
154                                 base = self.generator.bld.bldnode
155
156                         pattern = self.env[self.__class__.__name__ + '_PATTERN']
157                         if not pattern:
158                                 pattern = '%s'
159                         folder, name = os.path.split(target)
160
161                         if self.__class__.__name__.find('shlib') > 0 and getattr(self.generator, 'vnum', None):
162                                 nums = self.generator.vnum.split('.')
163                                 if self.env.DEST_BINFMT == 'pe':
164                                         # include the version in the dll file name,
165                                         # the import lib file name stays unversionned.
166                                         name = name + '-' + nums[0]
167                                 elif self.env.DEST_OS == 'openbsd':
168                                         pattern = '%s.%s' % (pattern, nums[0])
169                                         if len(nums) >= 2:
170                                                 pattern += '.%s' % nums[1]
171
172                         if folder:
173                                 tmp = folder + os.sep + pattern % name
174                         else:
175                                 tmp = pattern % name
176                         target = base.find_or_declare(tmp)
177                 self.set_outputs(target)
178
179         def exec_command(self, *k, **kw):
180                 ret = super(link_task, self).exec_command(*k, **kw)
181                 if not ret and self.env.DO_MANIFEST:
182                         ret = self.exec_mf()
183                 return ret
184
185         def exec_mf(self):
186                 """
187                 Create manifest files for VS-like compilers (msvc, ifort, ...)
188                 """
189                 if not self.env.MT:
190                         return 0
191
192                 manifest = None
193                 for out_node in self.outputs:
194                         if out_node.name.endswith('.manifest'):
195                                 manifest = out_node.abspath()
196                                 break
197                 else:
198                         # Should never get here.  If we do, it means the manifest file was
199                         # never added to the outputs list, thus we don't have a manifest file
200                         # to embed, so we just return.
201                         return 0
202
203                 # embedding mode. Different for EXE's and DLL's.
204                 # see: http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx
205                 mode = ''
206                 for x in Utils.to_list(self.generator.features):
207                         if x in ('cprogram', 'cxxprogram', 'fcprogram', 'fcprogram_test'):
208                                 mode = 1
209                         elif x in ('cshlib', 'cxxshlib', 'fcshlib'):
210                                 mode = 2
211
212                 Logs.debug('msvc: embedding manifest in mode %r', mode)
213
214                 lst = [] + self.env.MT
215                 lst.extend(Utils.to_list(self.env.MTFLAGS))
216                 lst.extend(['-manifest', manifest])
217                 lst.append('-outputresource:%s;%s' % (self.outputs[0].abspath(), mode))
218
219                 return super(link_task, self).exec_command(lst)
220
221 class stlink_task(link_task):
222         """
223         Base for static link tasks, which use *ar* most of the time.
224         The target is always removed before being written.
225         """
226         run_str = '${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}'
227
228         chmod   = Utils.O644
229         """Default installation mode for the static libraries"""
230
231 def rm_tgt(cls):
232         old = cls.run
233         def wrap(self):
234                 try: os.remove(self.outputs[0].abspath())
235                 except OSError: pass
236                 return old(self)
237         setattr(cls, 'run', wrap)
238 rm_tgt(stlink_task)
239
240 @feature('c', 'cxx', 'd', 'fc', 'asm')
241 @after_method('process_source')
242 def apply_link(self):
243         """
244         Collect the tasks stored in ``compiled_tasks`` (created by :py:func:`waflib.Tools.ccroot.create_compiled_task`), and
245         use the outputs for a new instance of :py:class:`waflib.Tools.ccroot.link_task`. The class to use is the first link task
246         matching a name from the attribute *features*, for example::
247
248                         def build(bld):
249                                 tg = bld(features='cxx cxxprogram cprogram', source='main.c', target='app')
250
251         will create the task ``tg.link_task`` as a new instance of :py:class:`waflib.Tools.cxx.cxxprogram`
252         """
253
254         for x in self.features:
255                 if x == 'cprogram' and 'cxx' in self.features: # limited compat
256                         x = 'cxxprogram'
257                 elif x == 'cshlib' and 'cxx' in self.features:
258                         x = 'cxxshlib'
259
260                 if x in Task.classes:
261                         if issubclass(Task.classes[x], link_task):
262                                 link = x
263                                 break
264         else:
265                 return
266
267         objs = [t.outputs[0] for t in getattr(self, 'compiled_tasks', [])]
268         self.link_task = self.create_task(link, objs)
269         self.link_task.add_target(self.target)
270
271         # remember that the install paths are given by the task generators
272         try:
273                 inst_to = self.install_path
274         except AttributeError:
275                 inst_to = self.link_task.__class__.inst_to
276         if inst_to:
277                 # install a copy of the node list we have at this moment (implib not added)
278                 self.install_task = self.add_install_files(
279                         install_to=inst_to, install_from=self.link_task.outputs[:],
280                         chmod=self.link_task.chmod, task=self.link_task)
281
282 @taskgen_method
283 def use_rec(self, name, **kw):
284         """
285         Processes the ``use`` keyword recursively. This method is kind of private and only meant to be used from ``process_use``
286         """
287
288         if name in self.tmp_use_not or name in self.tmp_use_seen:
289                 return
290
291         try:
292                 y = self.bld.get_tgen_by_name(name)
293         except Errors.WafError:
294                 self.uselib.append(name)
295                 self.tmp_use_not.add(name)
296                 return
297
298         self.tmp_use_seen.append(name)
299         y.post()
300
301         # bind temporary attributes on the task generator
302         y.tmp_use_objects = objects = kw.get('objects', True)
303         y.tmp_use_stlib   = stlib   = kw.get('stlib', True)
304         try:
305                 link_task = y.link_task
306         except AttributeError:
307                 y.tmp_use_var = ''
308         else:
309                 objects = False
310                 if not isinstance(link_task, stlink_task):
311                         stlib = False
312                         y.tmp_use_var = 'LIB'
313                 else:
314                         y.tmp_use_var = 'STLIB'
315
316         p = self.tmp_use_prec
317         for x in self.to_list(getattr(y, 'use', [])):
318                 if self.env["STLIB_" + x]:
319                         continue
320                 try:
321                         p[x].append(name)
322                 except KeyError:
323                         p[x] = [name]
324                 self.use_rec(x, objects=objects, stlib=stlib)
325
326 @feature('c', 'cxx', 'd', 'use', 'fc')
327 @before_method('apply_incpaths', 'propagate_uselib_vars')
328 @after_method('apply_link', 'process_source')
329 def process_use(self):
330         """
331         Process the ``use`` attribute which contains a list of task generator names::
332
333                 def build(bld):
334                         bld.shlib(source='a.c', target='lib1')
335                         bld.program(source='main.c', target='app', use='lib1')
336
337         See :py:func:`waflib.Tools.ccroot.use_rec`.
338         """
339
340         use_not = self.tmp_use_not = set()
341         self.tmp_use_seen = [] # we would like an ordered set
342         use_prec = self.tmp_use_prec = {}
343         self.uselib = self.to_list(getattr(self, 'uselib', []))
344         self.includes = self.to_list(getattr(self, 'includes', []))
345         names = self.to_list(getattr(self, 'use', []))
346
347         for x in names:
348                 self.use_rec(x)
349
350         for x in use_not:
351                 if x in use_prec:
352                         del use_prec[x]
353
354         # topological sort
355         out = self.tmp_use_sorted = []
356         tmp = []
357         for x in self.tmp_use_seen:
358                 for k in use_prec.values():
359                         if x in k:
360                                 break
361                 else:
362                         tmp.append(x)
363
364         while tmp:
365                 e = tmp.pop()
366                 out.append(e)
367                 try:
368                         nlst = use_prec[e]
369                 except KeyError:
370                         pass
371                 else:
372                         del use_prec[e]
373                         for x in nlst:
374                                 for y in use_prec:
375                                         if x in use_prec[y]:
376                                                 break
377                                 else:
378                                         tmp.append(x)
379         if use_prec:
380                 raise Errors.WafError('Cycle detected in the use processing %r' % use_prec)
381         out.reverse()
382
383         link_task = getattr(self, 'link_task', None)
384         for x in out:
385                 y = self.bld.get_tgen_by_name(x)
386                 var = y.tmp_use_var
387                 if var and link_task:
388                         if var == 'LIB' or y.tmp_use_stlib or x in names:
389                                 self.env.append_value(var, [y.target[y.target.rfind(os.sep) + 1:]])
390                                 self.link_task.dep_nodes.extend(y.link_task.outputs)
391                                 tmp_path = y.link_task.outputs[0].parent.path_from(self.get_cwd())
392                                 self.env.append_unique(var + 'PATH', [tmp_path])
393                 else:
394                         if y.tmp_use_objects:
395                                 self.add_objects_from_tgen(y)
396
397                 if getattr(y, 'export_includes', None):
398                         self.includes.extend(y.to_incnodes(y.export_includes))
399
400                 if getattr(y, 'export_defines', None):
401                         self.env.append_value('DEFINES', self.to_list(y.export_defines))
402
403
404         # and finally, add the use variables (no recursion needed)
405         for x in names:
406                 try:
407                         y = self.bld.get_tgen_by_name(x)
408                 except Errors.WafError:
409                         if not self.env['STLIB_' + x] and not x in self.uselib:
410                                 self.uselib.append(x)
411                 else:
412                         for k in self.to_list(getattr(y, 'use', [])):
413                                 if not self.env['STLIB_' + k] and not k in self.uselib:
414                                         self.uselib.append(k)
415
416 @taskgen_method
417 def accept_node_to_link(self, node):
418         """
419         PRIVATE INTERNAL USE ONLY
420         """
421         return not node.name.endswith('.pdb')
422
423 @taskgen_method
424 def add_objects_from_tgen(self, tg):
425         """
426         Add the objects from the depending compiled tasks as link task inputs.
427
428         Some objects are filtered: for instance, .pdb files are added
429         to the compiled tasks but not to the link tasks (to avoid errors)
430         PRIVATE INTERNAL USE ONLY
431         """
432         try:
433                 link_task = self.link_task
434         except AttributeError:
435                 pass
436         else:
437                 for tsk in getattr(tg, 'compiled_tasks', []):
438                         for x in tsk.outputs:
439                                 if self.accept_node_to_link(x):
440                                         link_task.inputs.append(x)
441
442 @taskgen_method
443 def get_uselib_vars(self):
444         """
445         :return: the *uselib* variables associated to the *features* attribute (see :py:attr:`waflib.Tools.ccroot.USELIB_VARS`)
446         :rtype: list of string
447         """
448         _vars = set()
449         for x in self.features:
450                 if x in USELIB_VARS:
451                         _vars |= USELIB_VARS[x]
452         return _vars
453
454 @feature('c', 'cxx', 'd', 'fc', 'javac', 'cs', 'uselib', 'asm')
455 @after_method('process_use')
456 def propagate_uselib_vars(self):
457         """
458         Process uselib variables for adding flags. For example, the following target::
459
460                 def build(bld):
461                         bld.env.AFLAGS_aaa = ['bar']
462                         from waflib.Tools.ccroot import USELIB_VARS
463                         USELIB_VARS['aaa'] = ['AFLAGS']
464
465                         tg = bld(features='aaa', aflags='test')
466
467         The *aflags* attribute will be processed and this method will set::
468
469                         tg.env.AFLAGS = ['bar', 'test']
470         """
471         _vars = self.get_uselib_vars()
472         env = self.env
473         app = env.append_value
474         feature_uselib = self.features + self.to_list(getattr(self, 'uselib', []))
475         for var in _vars:
476                 y = var.lower()
477                 val = getattr(self, y, [])
478                 if val:
479                         app(var, self.to_list(val))
480
481                 for x in feature_uselib:
482                         val = env['%s_%s' % (var, x)]
483                         if val:
484                                 app(var, val)
485
486 # ============ the code above must not know anything about import libs ==========
487
488 @feature('cshlib', 'cxxshlib', 'fcshlib')
489 @after_method('apply_link')
490 def apply_implib(self):
491         """
492         Handle dlls and their import libs on Windows-like systems.
493
494         A ``.dll.a`` file called *import library* is generated.
495         It must be installed as it is required for linking the library.
496         """
497         if not self.env.DEST_BINFMT == 'pe':
498                 return
499
500         dll = self.link_task.outputs[0]
501         if isinstance(self.target, Node.Node):
502                 name = self.target.name
503         else:
504                 name = os.path.split(self.target)[1]
505         implib = self.env.implib_PATTERN % name
506         implib = dll.parent.find_or_declare(implib)
507         self.env.append_value('LINKFLAGS', self.env.IMPLIB_ST % implib.bldpath())
508         self.link_task.outputs.append(implib)
509
510         if getattr(self, 'defs', None) and self.env.DEST_BINFMT == 'pe':
511                 node = self.path.find_resource(self.defs)
512                 if not node:
513                         raise Errors.WafError('invalid def file %r' % self.defs)
514                 if 'msvc' in (self.env.CC_NAME, self.env.CXX_NAME):
515                         self.env.append_value('LINKFLAGS', '/def:%s' % node.path_from(self.get_cwd()))
516                         self.link_task.dep_nodes.append(node)
517                 else:
518                         #gcc for windows takes *.def file a an input without any special flag
519                         self.link_task.inputs.append(node)
520
521         # where to put the import library
522         if getattr(self, 'install_task', None):
523                 try:
524                         # user has given a specific installation path for the import library
525                         inst_to = self.install_path_implib
526                 except AttributeError:
527                         try:
528                                 # user has given an installation path for the main library, put the import library in it
529                                 inst_to = self.install_path
530                         except AttributeError:
531                                 # else, put the library in BINDIR and the import library in LIBDIR
532                                 inst_to = '${IMPLIBDIR}'
533                                 self.install_task.install_to = '${BINDIR}'
534                                 if not self.env.IMPLIBDIR:
535                                         self.env.IMPLIBDIR = self.env.LIBDIR
536                 self.implib_install_task = self.add_install_files(install_to=inst_to, install_from=implib,
537                         chmod=self.link_task.chmod, task=self.link_task)
538
539 # ============ the code above must not know anything about vnum processing on unix platforms =========
540
541 re_vnum = re.compile('^([1-9]\\d*|0)([.]([1-9]\\d*|0)){0,2}?$')
542 @feature('cshlib', 'cxxshlib', 'dshlib', 'fcshlib', 'vnum')
543 @after_method('apply_link', 'propagate_uselib_vars')
544 def apply_vnum(self):
545         """
546         Enforce version numbering on shared libraries. The valid version numbers must have either zero or two dots::
547
548                 def build(bld):
549                         bld.shlib(source='a.c', target='foo', vnum='14.15.16')
550
551         In this example on Linux platform, ``libfoo.so`` is installed as ``libfoo.so.14.15.16``, and the following symbolic links are created:
552
553         * ``libfoo.so    â†’ libfoo.so.14.15.16``
554         * ``libfoo.so.14 â†’ libfoo.so.14.15.16``
555
556         By default, the library will be assigned SONAME ``libfoo.so.14``, effectively declaring ABI compatibility between all minor and patch releases for the major version of the library.  When necessary, the compatibility can be explicitly defined using `cnum` parameter:
557
558                 def build(bld):
559                         bld.shlib(source='a.c', target='foo', vnum='14.15.16', cnum='14.15')
560
561         In this case, the assigned SONAME will be ``libfoo.so.14.15`` with ABI compatibility only between path releases for a specific major and minor version of the library.
562
563         On OS X platform, install-name parameter will follow the above logic for SONAME with exception that it also specifies an absolute path (based on install_path) of the library.
564         """
565         if not getattr(self, 'vnum', '') or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'):
566                 return
567
568         link = self.link_task
569         if not re_vnum.match(self.vnum):
570                 raise Errors.WafError('Invalid vnum %r for target %r' % (self.vnum, getattr(self, 'name', self)))
571         nums = self.vnum.split('.')
572         node = link.outputs[0]
573
574         cnum = getattr(self, 'cnum', str(nums[0]))
575         cnums = cnum.split('.')
576         if len(cnums)>len(nums) or nums[0:len(cnums)] != cnums:
577                 raise Errors.WafError('invalid compatibility version %s' % cnum)
578
579         libname = node.name
580         if libname.endswith('.dylib'):
581                 name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum)
582                 name2 = libname.replace('.dylib', '.%s.dylib' % cnum)
583         else:
584                 name3 = libname + '.' + self.vnum
585                 name2 = libname + '.' + cnum
586
587         # add the so name for the ld linker - to disable, just unset env.SONAME_ST
588         if self.env.SONAME_ST:
589                 v = self.env.SONAME_ST % name2
590                 self.env.append_value('LINKFLAGS', v.split())
591
592         # the following task is just to enable execution from the build dir :-/
593         if self.env.DEST_OS != 'openbsd':
594                 outs = [node.parent.make_node(name3)]
595                 if name2 != name3:
596                         outs.append(node.parent.make_node(name2))
597                 self.create_task('vnum', node, outs)
598
599         if getattr(self, 'install_task', None):
600                 self.install_task.hasrun = Task.SKIP_ME
601                 path = self.install_task.install_to
602                 if self.env.DEST_OS == 'openbsd':
603                         libname = self.link_task.outputs[0].name
604                         t1 = self.add_install_as(install_to='%s/%s' % (path, libname), install_from=node, chmod=self.link_task.chmod)
605                         self.vnum_install_task = (t1,)
606                 else:
607                         t1 = self.add_install_as(install_to=path + os.sep + name3, install_from=node, chmod=self.link_task.chmod)
608                         t3 = self.add_symlink_as(install_to=path + os.sep + libname, install_from=name3)
609                         if name2 != name3:
610                                 t2 = self.add_symlink_as(install_to=path + os.sep + name2, install_from=name3)
611                                 self.vnum_install_task = (t1, t2, t3)
612                         else:
613                                 self.vnum_install_task = (t1, t3)
614
615         if '-dynamiclib' in self.env.LINKFLAGS:
616                 # this requires after(propagate_uselib_vars)
617                 try:
618                         inst_to = self.install_path
619                 except AttributeError:
620                         inst_to = self.link_task.__class__.inst_to
621                 if inst_to:
622                         p = Utils.subst_vars(inst_to, self.env)
623                         path = os.path.join(p, name2)
624                         self.env.append_value('LINKFLAGS', ['-install_name', path])
625                         self.env.append_value('LINKFLAGS', '-Wl,-compatibility_version,%s' % cnum)
626                         self.env.append_value('LINKFLAGS', '-Wl,-current_version,%s' % self.vnum)
627
628 class vnum(Task.Task):
629         """
630         Create the symbolic links for a versioned shared library. Instances are created by :py:func:`waflib.Tools.ccroot.apply_vnum`
631         """
632         color = 'CYAN'
633         ext_in = ['.bin']
634         def keyword(self):
635                 return 'Symlinking'
636         def run(self):
637                 for x in self.outputs:
638                         path = x.abspath()
639                         try:
640                                 os.remove(path)
641                         except OSError:
642                                 pass
643
644                         try:
645                                 os.symlink(self.inputs[0].name, path)
646                         except OSError:
647                                 return 1
648
649 class fake_shlib(link_task):
650         """
651         Task used for reading a system library and adding the dependency on it
652         """
653         def runnable_status(self):
654                 for t in self.run_after:
655                         if not t.hasrun:
656                                 return Task.ASK_LATER
657                 return Task.SKIP_ME
658
659 class fake_stlib(stlink_task):
660         """
661         Task used for reading a system library and adding the dependency on it
662         """
663         def runnable_status(self):
664                 for t in self.run_after:
665                         if not t.hasrun:
666                                 return Task.ASK_LATER
667                 return Task.SKIP_ME
668
669 @conf
670 def read_shlib(self, name, paths=[], export_includes=[], export_defines=[]):
671         """
672         Read a system shared library, enabling its use as a local library. Will trigger a rebuild if the file changes::
673
674                 def build(bld):
675                         bld.read_shlib('m')
676                         bld.program(source='main.c', use='m')
677         """
678         return self(name=name, features='fake_lib', lib_paths=paths, lib_type='shlib', export_includes=export_includes, export_defines=export_defines)
679
680 @conf
681 def read_stlib(self, name, paths=[], export_includes=[], export_defines=[]):
682         """
683         Read a system static library, enabling a use as a local library. Will trigger a rebuild if the file changes.
684         """
685         return self(name=name, features='fake_lib', lib_paths=paths, lib_type='stlib', export_includes=export_includes, export_defines=export_defines)
686
687 lib_patterns = {
688         'shlib' : ['lib%s.so', '%s.so', 'lib%s.dylib', 'lib%s.dll', '%s.dll'],
689         'stlib' : ['lib%s.a', '%s.a', 'lib%s.dll', '%s.dll', 'lib%s.lib', '%s.lib'],
690 }
691
692 @feature('fake_lib')
693 def process_lib(self):
694         """
695         Find the location of a foreign library. Used by :py:class:`waflib.Tools.ccroot.read_shlib` and :py:class:`waflib.Tools.ccroot.read_stlib`.
696         """
697         node = None
698
699         names = [x % self.name for x in lib_patterns[self.lib_type]]
700         for x in self.lib_paths + [self.path] + SYSTEM_LIB_PATHS:
701                 if not isinstance(x, Node.Node):
702                         x = self.bld.root.find_node(x) or self.path.find_node(x)
703                         if not x:
704                                 continue
705
706                 for y in names:
707                         node = x.find_node(y)
708                         if node:
709                                 try:
710                                         Utils.h_file(node.abspath())
711                                 except EnvironmentError:
712                                         raise ValueError('Could not read %r' % y)
713                                 break
714                 else:
715                         continue
716                 break
717         else:
718                 raise Errors.WafError('could not find library %r' % self.name)
719         self.link_task = self.create_task('fake_%s' % self.lib_type, [], [node])
720         self.target = self.name
721
722
723 class fake_o(Task.Task):
724         def runnable_status(self):
725                 return Task.SKIP_ME
726
727 @extension('.o', '.obj')
728 def add_those_o_files(self, node):
729         tsk = self.create_task('fake_o', [], node)
730         try:
731                 self.compiled_tasks.append(tsk)
732         except AttributeError:
733                 self.compiled_tasks = [tsk]
734
735 @feature('fake_obj')
736 @before_method('process_source')
737 def process_objs(self):
738         """
739         Puts object files in the task generator outputs
740         """
741         for node in self.to_nodes(self.source):
742                 self.add_those_o_files(node)
743         self.source = []
744
745 @conf
746 def read_object(self, obj):
747         """
748         Read an object file, enabling injection in libs/programs. Will trigger a rebuild if the file changes.
749
750         :param obj: object file path, as string or Node
751         """
752         if not isinstance(obj, self.path.__class__):
753                 obj = self.path.find_resource(obj)
754         return self(features='fake_obj', source=obj, name=obj.name)
755
756 @feature('cxxprogram', 'cprogram')
757 @after_method('apply_link', 'process_use')
758 def set_full_paths_hpux(self):
759         """
760         On hp-ux, extend the libpaths and static library paths to absolute paths
761         """
762         if self.env.DEST_OS != 'hp-ux':
763                 return
764         base = self.bld.bldnode.abspath()
765         for var in ['LIBPATH', 'STLIBPATH']:
766                 lst = []
767                 for x in self.env[var]:
768                         if x.startswith('/'):
769                                 lst.append(x)
770                         else:
771                                 lst.append(os.path.normpath(os.path.join(base, x)))
772                 self.env[var] = lst