6a425fd8f4dd6bc5e4f3b3f87c8e022593ef1a58
[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-2018 (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         weight  = 3
139         """Try to process link tasks as early as possible"""
140
141         inst_to = None
142         """Default installation path for the link task outputs, or None to disable"""
143
144         chmod   = Utils.O755
145         """Default installation mode for the link task outputs"""
146
147         def add_target(self, target):
148                 """
149                 Process the *target* attribute to add the platform-specific prefix/suffix such as *.so* or *.exe*.
150                 The settings are retrieved from ``env.clsname_PATTERN``
151                 """
152                 if isinstance(target, str):
153                         base = self.generator.path
154                         if target.startswith('#'):
155                                 # for those who like flat structures
156                                 target = target[1:]
157                                 base = self.generator.bld.bldnode
158
159                         pattern = self.env[self.__class__.__name__ + '_PATTERN']
160                         if not pattern:
161                                 pattern = '%s'
162                         folder, name = os.path.split(target)
163
164                         if self.__class__.__name__.find('shlib') > 0 and getattr(self.generator, 'vnum', None):
165                                 nums = self.generator.vnum.split('.')
166                                 if self.env.DEST_BINFMT == 'pe':
167                                         # include the version in the dll file name,
168                                         # the import lib file name stays unversionned.
169                                         name = name + '-' + nums[0]
170                                 elif self.env.DEST_OS == 'openbsd':
171                                         pattern = '%s.%s' % (pattern, nums[0])
172                                         if len(nums) >= 2:
173                                                 pattern += '.%s' % nums[1]
174
175                         if folder:
176                                 tmp = folder + os.sep + pattern % name
177                         else:
178                                 tmp = pattern % name
179                         target = base.find_or_declare(tmp)
180                 self.set_outputs(target)
181
182         def exec_command(self, *k, **kw):
183                 ret = super(link_task, self).exec_command(*k, **kw)
184                 if not ret and self.env.DO_MANIFEST:
185                         ret = self.exec_mf()
186                 return ret
187
188         def exec_mf(self):
189                 """
190                 Create manifest files for VS-like compilers (msvc, ifort, ...)
191                 """
192                 if not self.env.MT:
193                         return 0
194
195                 manifest = None
196                 for out_node in self.outputs:
197                         if out_node.name.endswith('.manifest'):
198                                 manifest = out_node.abspath()
199                                 break
200                 else:
201                         # Should never get here.  If we do, it means the manifest file was
202                         # never added to the outputs list, thus we don't have a manifest file
203                         # to embed, so we just return.
204                         return 0
205
206                 # embedding mode. Different for EXE's and DLL's.
207                 # see: http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx
208                 mode = ''
209                 for x in Utils.to_list(self.generator.features):
210                         if x in ('cprogram', 'cxxprogram', 'fcprogram', 'fcprogram_test'):
211                                 mode = 1
212                         elif x in ('cshlib', 'cxxshlib', 'fcshlib'):
213                                 mode = 2
214
215                 Logs.debug('msvc: embedding manifest in mode %r', mode)
216
217                 lst = [] + self.env.MT
218                 lst.extend(Utils.to_list(self.env.MTFLAGS))
219                 lst.extend(['-manifest', manifest])
220                 lst.append('-outputresource:%s;%s' % (self.outputs[0].abspath(), mode))
221
222                 return super(link_task, self).exec_command(lst)
223
224 class stlink_task(link_task):
225         """
226         Base for static link tasks, which use *ar* most of the time.
227         The target is always removed before being written.
228         """
229         run_str = '${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}'
230
231         chmod   = Utils.O644
232         """Default installation mode for the static libraries"""
233
234 def rm_tgt(cls):
235         old = cls.run
236         def wrap(self):
237                 try:
238                         os.remove(self.outputs[0].abspath())
239                 except OSError:
240                         pass
241                 return old(self)
242         setattr(cls, 'run', wrap)
243 rm_tgt(stlink_task)
244
245 @feature('c', 'cxx', 'd', 'fc', 'asm')
246 @after_method('process_source')
247 def apply_link(self):
248         """
249         Collect the tasks stored in ``compiled_tasks`` (created by :py:func:`waflib.Tools.ccroot.create_compiled_task`), and
250         use the outputs for a new instance of :py:class:`waflib.Tools.ccroot.link_task`. The class to use is the first link task
251         matching a name from the attribute *features*, for example::
252
253                         def build(bld):
254                                 tg = bld(features='cxx cxxprogram cprogram', source='main.c', target='app')
255
256         will create the task ``tg.link_task`` as a new instance of :py:class:`waflib.Tools.cxx.cxxprogram`
257         """
258
259         for x in self.features:
260                 if x == 'cprogram' and 'cxx' in self.features: # limited compat
261                         x = 'cxxprogram'
262                 elif x == 'cshlib' and 'cxx' in self.features:
263                         x = 'cxxshlib'
264
265                 if x in Task.classes:
266                         if issubclass(Task.classes[x], link_task):
267                                 link = x
268                                 break
269         else:
270                 return
271
272         objs = [t.outputs[0] for t in getattr(self, 'compiled_tasks', [])]
273         self.link_task = self.create_task(link, objs)
274         self.link_task.add_target(self.target)
275
276         # remember that the install paths are given by the task generators
277         try:
278                 inst_to = self.install_path
279         except AttributeError:
280                 inst_to = self.link_task.inst_to
281         if inst_to:
282                 # install a copy of the node list we have at this moment (implib not added)
283                 self.install_task = self.add_install_files(
284                         install_to=inst_to, install_from=self.link_task.outputs[:],
285                         chmod=self.link_task.chmod, task=self.link_task)
286
287 @taskgen_method
288 def use_rec(self, name, **kw):
289         """
290         Processes the ``use`` keyword recursively. This method is kind of private and only meant to be used from ``process_use``
291         """
292
293         if name in self.tmp_use_not or name in self.tmp_use_seen:
294                 return
295
296         try:
297                 y = self.bld.get_tgen_by_name(name)
298         except Errors.WafError:
299                 self.uselib.append(name)
300                 self.tmp_use_not.add(name)
301                 return
302
303         self.tmp_use_seen.append(name)
304         y.post()
305
306         # bind temporary attributes on the task generator
307         y.tmp_use_objects = objects = kw.get('objects', True)
308         y.tmp_use_stlib   = stlib   = kw.get('stlib', True)
309         try:
310                 link_task = y.link_task
311         except AttributeError:
312                 y.tmp_use_var = ''
313         else:
314                 objects = False
315                 if not isinstance(link_task, stlink_task):
316                         stlib = False
317                         y.tmp_use_var = 'LIB'
318                 else:
319                         y.tmp_use_var = 'STLIB'
320
321         p = self.tmp_use_prec
322         for x in self.to_list(getattr(y, 'use', [])):
323                 if self.env["STLIB_" + x]:
324                         continue
325                 try:
326                         p[x].append(name)
327                 except KeyError:
328                         p[x] = [name]
329                 self.use_rec(x, objects=objects, stlib=stlib)
330
331 @feature('c', 'cxx', 'd', 'use', 'fc')
332 @before_method('apply_incpaths', 'propagate_uselib_vars')
333 @after_method('apply_link', 'process_source')
334 def process_use(self):
335         """
336         Process the ``use`` attribute which contains a list of task generator names::
337
338                 def build(bld):
339                         bld.shlib(source='a.c', target='lib1')
340                         bld.program(source='main.c', target='app', use='lib1')
341
342         See :py:func:`waflib.Tools.ccroot.use_rec`.
343         """
344
345         use_not = self.tmp_use_not = set()
346         self.tmp_use_seen = [] # we would like an ordered set
347         use_prec = self.tmp_use_prec = {}
348         self.uselib = self.to_list(getattr(self, 'uselib', []))
349         self.includes = self.to_list(getattr(self, 'includes', []))
350         names = self.to_list(getattr(self, 'use', []))
351
352         for x in names:
353                 self.use_rec(x)
354
355         for x in use_not:
356                 if x in use_prec:
357                         del use_prec[x]
358
359         # topological sort
360         out = self.tmp_use_sorted = []
361         tmp = []
362         for x in self.tmp_use_seen:
363                 for k in use_prec.values():
364                         if x in k:
365                                 break
366                 else:
367                         tmp.append(x)
368
369         while tmp:
370                 e = tmp.pop()
371                 out.append(e)
372                 try:
373                         nlst = use_prec[e]
374                 except KeyError:
375                         pass
376                 else:
377                         del use_prec[e]
378                         for x in nlst:
379                                 for y in use_prec:
380                                         if x in use_prec[y]:
381                                                 break
382                                 else:
383                                         tmp.append(x)
384         if use_prec:
385                 raise Errors.WafError('Cycle detected in the use processing %r' % use_prec)
386         out.reverse()
387
388         link_task = getattr(self, 'link_task', None)
389         for x in out:
390                 y = self.bld.get_tgen_by_name(x)
391                 var = y.tmp_use_var
392                 if var and link_task:
393                         if var == 'LIB' or y.tmp_use_stlib or x in names:
394                                 self.env.append_value(var, [y.target[y.target.rfind(os.sep) + 1:]])
395                                 self.link_task.dep_nodes.extend(y.link_task.outputs)
396                                 tmp_path = y.link_task.outputs[0].parent.path_from(self.get_cwd())
397                                 self.env.append_unique(var + 'PATH', [tmp_path])
398                 else:
399                         if y.tmp_use_objects:
400                                 self.add_objects_from_tgen(y)
401
402                 if getattr(y, 'export_includes', None):
403                         # self.includes may come from a global variable #2035
404                         self.includes = self.includes + y.to_incnodes(y.export_includes)
405
406                 if getattr(y, 'export_defines', None):
407                         self.env.append_value('DEFINES', self.to_list(y.export_defines))
408
409
410         # and finally, add the use variables (no recursion needed)
411         for x in names:
412                 try:
413                         y = self.bld.get_tgen_by_name(x)
414                 except Errors.WafError:
415                         if not self.env['STLIB_' + x] and not x in self.uselib:
416                                 self.uselib.append(x)
417                 else:
418                         for k in self.to_list(getattr(y, 'use', [])):
419                                 if not self.env['STLIB_' + k] and not k in self.uselib:
420                                         self.uselib.append(k)
421
422 @taskgen_method
423 def accept_node_to_link(self, node):
424         """
425         PRIVATE INTERNAL USE ONLY
426         """
427         return not node.name.endswith('.pdb')
428
429 @taskgen_method
430 def add_objects_from_tgen(self, tg):
431         """
432         Add the objects from the depending compiled tasks as link task inputs.
433
434         Some objects are filtered: for instance, .pdb files are added
435         to the compiled tasks but not to the link tasks (to avoid errors)
436         PRIVATE INTERNAL USE ONLY
437         """
438         try:
439                 link_task = self.link_task
440         except AttributeError:
441                 pass
442         else:
443                 for tsk in getattr(tg, 'compiled_tasks', []):
444                         for x in tsk.outputs:
445                                 if self.accept_node_to_link(x):
446                                         link_task.inputs.append(x)
447
448 @taskgen_method
449 def get_uselib_vars(self):
450         """
451         :return: the *uselib* variables associated to the *features* attribute (see :py:attr:`waflib.Tools.ccroot.USELIB_VARS`)
452         :rtype: list of string
453         """
454         _vars = set()
455         for x in self.features:
456                 if x in USELIB_VARS:
457                         _vars |= USELIB_VARS[x]
458         return _vars
459
460 @feature('c', 'cxx', 'd', 'fc', 'javac', 'cs', 'uselib', 'asm')
461 @after_method('process_use')
462 def propagate_uselib_vars(self):
463         """
464         Process uselib variables for adding flags. For example, the following target::
465
466                 def build(bld):
467                         bld.env.AFLAGS_aaa = ['bar']
468                         from waflib.Tools.ccroot import USELIB_VARS
469                         USELIB_VARS['aaa'] = ['AFLAGS']
470
471                         tg = bld(features='aaa', aflags='test')
472
473         The *aflags* attribute will be processed and this method will set::
474
475                         tg.env.AFLAGS = ['bar', 'test']
476         """
477         _vars = self.get_uselib_vars()
478         env = self.env
479         app = env.append_value
480         feature_uselib = self.features + self.to_list(getattr(self, 'uselib', []))
481         for var in _vars:
482                 y = var.lower()
483                 val = getattr(self, y, [])
484                 if val:
485                         app(var, self.to_list(val))
486
487                 for x in feature_uselib:
488                         val = env['%s_%s' % (var, x)]
489                         if val:
490                                 app(var, val)
491
492 # ============ the code above must not know anything about import libs ==========
493
494 @feature('cshlib', 'cxxshlib', 'fcshlib')
495 @after_method('apply_link')
496 def apply_implib(self):
497         """
498         Handle dlls and their import libs on Windows-like systems.
499
500         A ``.dll.a`` file called *import library* is generated.
501         It must be installed as it is required for linking the library.
502         """
503         if not self.env.DEST_BINFMT == 'pe':
504                 return
505
506         dll = self.link_task.outputs[0]
507         if isinstance(self.target, Node.Node):
508                 name = self.target.name
509         else:
510                 name = os.path.split(self.target)[1]
511         implib = self.env.implib_PATTERN % name
512         implib = dll.parent.find_or_declare(implib)
513         self.env.append_value('LINKFLAGS', self.env.IMPLIB_ST % implib.bldpath())
514         self.link_task.outputs.append(implib)
515
516         if getattr(self, 'defs', None) and self.env.DEST_BINFMT == 'pe':
517                 node = self.path.find_resource(self.defs)
518                 if not node:
519                         raise Errors.WafError('invalid def file %r' % self.defs)
520                 if 'msvc' in (self.env.CC_NAME, self.env.CXX_NAME):
521                         self.env.append_value('LINKFLAGS', '/def:%s' % node.path_from(self.get_cwd()))
522                         self.link_task.dep_nodes.append(node)
523                 else:
524                         #gcc for windows takes *.def file a an input without any special flag
525                         self.link_task.inputs.append(node)
526
527         # where to put the import library
528         if getattr(self, 'install_task', None):
529                 try:
530                         # user has given a specific installation path for the import library
531                         inst_to = self.install_path_implib
532                 except AttributeError:
533                         try:
534                                 # user has given an installation path for the main library, put the import library in it
535                                 inst_to = self.install_path
536                         except AttributeError:
537                                 # else, put the library in BINDIR and the import library in LIBDIR
538                                 inst_to = '${IMPLIBDIR}'
539                                 self.install_task.install_to = '${BINDIR}'
540                                 if not self.env.IMPLIBDIR:
541                                         self.env.IMPLIBDIR = self.env.LIBDIR
542                 self.implib_install_task = self.add_install_files(install_to=inst_to, install_from=implib,
543                         chmod=self.link_task.chmod, task=self.link_task)
544
545 # ============ the code above must not know anything about vnum processing on unix platforms =========
546
547 re_vnum = re.compile('^([1-9]\\d*|0)([.]([1-9]\\d*|0)){0,2}?$')
548 @feature('cshlib', 'cxxshlib', 'dshlib', 'fcshlib', 'vnum')
549 @after_method('apply_link', 'propagate_uselib_vars')
550 def apply_vnum(self):
551         """
552         Enforce version numbering on shared libraries. The valid version numbers must have either zero or two dots::
553
554                 def build(bld):
555                         bld.shlib(source='a.c', target='foo', vnum='14.15.16')
556
557         In this example on Linux platform, ``libfoo.so`` is installed as ``libfoo.so.14.15.16``, and the following symbolic links are created:
558
559         * ``libfoo.so    â†’ libfoo.so.14.15.16``
560         * ``libfoo.so.14 â†’ libfoo.so.14.15.16``
561
562         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:
563
564                 def build(bld):
565                         bld.shlib(source='a.c', target='foo', vnum='14.15.16', cnum='14.15')
566
567         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.
568
569         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.
570         """
571         if not getattr(self, 'vnum', '') or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'):
572                 return
573
574         link = self.link_task
575         if not re_vnum.match(self.vnum):
576                 raise Errors.WafError('Invalid vnum %r for target %r' % (self.vnum, getattr(self, 'name', self)))
577         nums = self.vnum.split('.')
578         node = link.outputs[0]
579
580         cnum = getattr(self, 'cnum', str(nums[0]))
581         cnums = cnum.split('.')
582         if len(cnums)>len(nums) or nums[0:len(cnums)] != cnums:
583                 raise Errors.WafError('invalid compatibility version %s' % cnum)
584
585         libname = node.name
586         if libname.endswith('.dylib'):
587                 name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum)
588                 name2 = libname.replace('.dylib', '.%s.dylib' % cnum)
589         else:
590                 name3 = libname + '.' + self.vnum
591                 name2 = libname + '.' + cnum
592
593         # add the so name for the ld linker - to disable, just unset env.SONAME_ST
594         if self.env.SONAME_ST:
595                 v = self.env.SONAME_ST % name2
596                 self.env.append_value('LINKFLAGS', v.split())
597
598         # the following task is just to enable execution from the build dir :-/
599         if self.env.DEST_OS != 'openbsd':
600                 outs = [node.parent.make_node(name3)]
601                 if name2 != name3:
602                         outs.append(node.parent.make_node(name2))
603                 self.create_task('vnum', node, outs)
604
605         if getattr(self, 'install_task', None):
606                 self.install_task.hasrun = Task.SKIPPED
607                 path = self.install_task.install_to
608                 if self.env.DEST_OS == 'openbsd':
609                         libname = self.link_task.outputs[0].name
610                         t1 = self.add_install_as(install_to='%s/%s' % (path, libname), install_from=node, chmod=self.link_task.chmod)
611                         self.vnum_install_task = (t1,)
612                 else:
613                         t1 = self.add_install_as(install_to=path + os.sep + name3, install_from=node, chmod=self.link_task.chmod)
614                         t3 = self.add_symlink_as(install_to=path + os.sep + libname, install_from=name3)
615                         if name2 != name3:
616                                 t2 = self.add_symlink_as(install_to=path + os.sep + name2, install_from=name3)
617                                 self.vnum_install_task = (t1, t2, t3)
618                         else:
619                                 self.vnum_install_task = (t1, t3)
620
621         if '-dynamiclib' in self.env.LINKFLAGS:
622                 # this requires after(propagate_uselib_vars)
623                 try:
624                         inst_to = self.install_path
625                 except AttributeError:
626                         inst_to = self.link_task.inst_to
627                 if inst_to:
628                         p = Utils.subst_vars(inst_to, self.env)
629                         path = os.path.join(p, name2)
630                         self.env.append_value('LINKFLAGS', ['-install_name', path])
631                         self.env.append_value('LINKFLAGS', '-Wl,-compatibility_version,%s' % cnum)
632                         self.env.append_value('LINKFLAGS', '-Wl,-current_version,%s' % self.vnum)
633
634 class vnum(Task.Task):
635         """
636         Create the symbolic links for a versioned shared library. Instances are created by :py:func:`waflib.Tools.ccroot.apply_vnum`
637         """
638         color = 'CYAN'
639         ext_in = ['.bin']
640         def keyword(self):
641                 return 'Symlinking'
642         def run(self):
643                 for x in self.outputs:
644                         path = x.abspath()
645                         try:
646                                 os.remove(path)
647                         except OSError:
648                                 pass
649
650                         try:
651                                 os.symlink(self.inputs[0].name, path)
652                         except OSError:
653                                 return 1
654
655 class fake_shlib(link_task):
656         """
657         Task used for reading a system library and adding the dependency on it
658         """
659         def runnable_status(self):
660                 for t in self.run_after:
661                         if not t.hasrun:
662                                 return Task.ASK_LATER
663                 return Task.SKIP_ME
664
665 class fake_stlib(stlink_task):
666         """
667         Task used for reading a system library and adding the dependency on it
668         """
669         def runnable_status(self):
670                 for t in self.run_after:
671                         if not t.hasrun:
672                                 return Task.ASK_LATER
673                 return Task.SKIP_ME
674
675 @conf
676 def read_shlib(self, name, paths=[], export_includes=[], export_defines=[]):
677         """
678         Read a system shared library, enabling its use as a local library. Will trigger a rebuild if the file changes::
679
680                 def build(bld):
681                         bld.read_shlib('m')
682                         bld.program(source='main.c', use='m')
683         """
684         return self(name=name, features='fake_lib', lib_paths=paths, lib_type='shlib', export_includes=export_includes, export_defines=export_defines)
685
686 @conf
687 def read_stlib(self, name, paths=[], export_includes=[], export_defines=[]):
688         """
689         Read a system static library, enabling a use as a local library. Will trigger a rebuild if the file changes.
690         """
691         return self(name=name, features='fake_lib', lib_paths=paths, lib_type='stlib', export_includes=export_includes, export_defines=export_defines)
692
693 lib_patterns = {
694         'shlib' : ['lib%s.so', '%s.so', 'lib%s.dylib', 'lib%s.dll', '%s.dll'],
695         'stlib' : ['lib%s.a', '%s.a', 'lib%s.dll', '%s.dll', 'lib%s.lib', '%s.lib'],
696 }
697
698 @feature('fake_lib')
699 def process_lib(self):
700         """
701         Find the location of a foreign library. Used by :py:class:`waflib.Tools.ccroot.read_shlib` and :py:class:`waflib.Tools.ccroot.read_stlib`.
702         """
703         node = None
704
705         names = [x % self.name for x in lib_patterns[self.lib_type]]
706         for x in self.lib_paths + [self.path] + SYSTEM_LIB_PATHS:
707                 if not isinstance(x, Node.Node):
708                         x = self.bld.root.find_node(x) or self.path.find_node(x)
709                         if not x:
710                                 continue
711
712                 for y in names:
713                         node = x.find_node(y)
714                         if node:
715                                 try:
716                                         Utils.h_file(node.abspath())
717                                 except EnvironmentError:
718                                         raise ValueError('Could not read %r' % y)
719                                 break
720                 else:
721                         continue
722                 break
723         else:
724                 raise Errors.WafError('could not find library %r' % self.name)
725         self.link_task = self.create_task('fake_%s' % self.lib_type, [], [node])
726         self.target = self.name
727
728
729 class fake_o(Task.Task):
730         def runnable_status(self):
731                 return Task.SKIP_ME
732
733 @extension('.o', '.obj')
734 def add_those_o_files(self, node):
735         tsk = self.create_task('fake_o', [], node)
736         try:
737                 self.compiled_tasks.append(tsk)
738         except AttributeError:
739                 self.compiled_tasks = [tsk]
740
741 @feature('fake_obj')
742 @before_method('process_source')
743 def process_objs(self):
744         """
745         Puts object files in the task generator outputs
746         """
747         for node in self.to_nodes(self.source):
748                 self.add_those_o_files(node)
749         self.source = []
750
751 @conf
752 def read_object(self, obj):
753         """
754         Read an object file, enabling injection in libs/programs. Will trigger a rebuild if the file changes.
755
756         :param obj: object file path, as string or Node
757         """
758         if not isinstance(obj, self.path.__class__):
759                 obj = self.path.find_resource(obj)
760         return self(features='fake_obj', source=obj, name=obj.name)
761
762 @feature('cxxprogram', 'cprogram')
763 @after_method('apply_link', 'process_use')
764 def set_full_paths_hpux(self):
765         """
766         On hp-ux, extend the libpaths and static library paths to absolute paths
767         """
768         if self.env.DEST_OS != 'hp-ux':
769                 return
770         base = self.bld.bldnode.abspath()
771         for var in ['LIBPATH', 'STLIBPATH']:
772                 lst = []
773                 for x in self.env[var]:
774                         if x.startswith('/'):
775                                 lst.append(x)
776                         else:
777                                 lst.append(os.path.normpath(os.path.join(base, x)))
778                 self.env[var] = lst
779