Force library compat/current version under macOS
[jack2.git] / waflib / Tools / ccroot.py
blob3397456ec0bacca3951daa406b83ad6077827ee4
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2018 (ita)
5 """
6 Classes and methods shared by tools providing support for C-like language such
7 as C/C++/D/Assembly/Go (this support module is almost never used alone).
8 """
10 import os, re
11 from waflib import Task, Utils, Node, Errors, Logs
12 from waflib.TaskGen import after_method, before_method, feature, taskgen_method, extension
13 from waflib.Tools import c_aliases, c_preproc, c_config, c_osx, c_tests
14 from waflib.Configure import conf
16 SYSTEM_LIB_PATHS = ['/usr/lib64', '/usr/lib', '/usr/local/lib64', '/usr/local/lib']
18 USELIB_VARS = Utils.defaultdict(set)
19 """
20 Mapping for features to :py:class:`waflib.ConfigSet.ConfigSet` variables. See :py:func:`waflib.Tools.ccroot.propagate_uselib_vars`.
21 """
23 USELIB_VARS['c'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CCDEPS', 'CFLAGS', 'ARCH'])
24 USELIB_VARS['cxx'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CXXDEPS', 'CXXFLAGS', 'ARCH'])
25 USELIB_VARS['d'] = set(['INCLUDES', 'DFLAGS'])
26 USELIB_VARS['includes'] = set(['INCLUDES', 'FRAMEWORKPATH', 'ARCH'])
28 USELIB_VARS['cprogram'] = USELIB_VARS['cxxprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH', 'LDFLAGS'])
29 USELIB_VARS['cshlib'] = USELIB_VARS['cxxshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH', 'LDFLAGS'])
30 USELIB_VARS['cstlib'] = USELIB_VARS['cxxstlib'] = set(['ARFLAGS', 'LINKDEPS'])
32 USELIB_VARS['dprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
33 USELIB_VARS['dshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
34 USELIB_VARS['dstlib'] = set(['ARFLAGS', 'LINKDEPS'])
36 USELIB_VARS['asm'] = set(['ASFLAGS'])
38 # =================================================================================================
40 @taskgen_method
41 def create_compiled_task(self, name, node):
42 """
43 Create the compilation task: c, cxx, asm, etc. The output node is created automatically (object file with a typical **.o** extension).
44 The task is appended to the list *compiled_tasks* which is then used by :py:func:`waflib.Tools.ccroot.apply_link`
46 :param name: name of the task class
47 :type name: string
48 :param node: the file to compile
49 :type node: :py:class:`waflib.Node.Node`
50 :return: The task created
51 :rtype: :py:class:`waflib.Task.Task`
52 """
53 out = '%s.%d.o' % (node.name, self.idx)
54 task = self.create_task(name, node, node.parent.find_or_declare(out))
55 try:
56 self.compiled_tasks.append(task)
57 except AttributeError:
58 self.compiled_tasks = [task]
59 return task
61 @taskgen_method
62 def to_incnodes(self, inlst):
63 """
64 Task generator method provided to convert a list of string/nodes into a list of includes folders.
66 The paths are assumed to be relative to the task generator path, except if they begin by **#**
67 in which case they are searched from the top-level directory (``bld.srcnode``).
68 The folders are simply assumed to be existing.
70 The node objects in the list are returned in the output list. The strings are converted
71 into node objects if possible. The node is searched from the source directory, and if a match is found,
72 the equivalent build directory is created and added to the returned list too. When a folder cannot be found, it is ignored.
74 :param inlst: list of folders
75 :type inlst: space-delimited string or a list of string/nodes
76 :rtype: list of :py:class:`waflib.Node.Node`
77 :return: list of include folders as nodes
78 """
79 lst = []
80 seen = set()
81 for x in self.to_list(inlst):
82 if x in seen or not x:
83 continue
84 seen.add(x)
86 # with a real lot of targets, it is sometimes interesting to cache the results below
87 if isinstance(x, Node.Node):
88 lst.append(x)
89 else:
90 if os.path.isabs(x):
91 lst.append(self.bld.root.make_node(x) or x)
92 else:
93 if x[0] == '#':
94 p = self.bld.bldnode.make_node(x[1:])
95 v = self.bld.srcnode.make_node(x[1:])
96 else:
97 p = self.path.get_bld().make_node(x)
98 v = self.path.make_node(x)
99 if p.is_child_of(self.bld.bldnode):
100 p.mkdir()
101 lst.append(p)
102 lst.append(v)
103 return lst
105 @feature('c', 'cxx', 'd', 'asm', 'fc', 'includes')
106 @after_method('propagate_uselib_vars', 'process_source')
107 def apply_incpaths(self):
109 Task generator method that processes the attribute *includes*::
111 tg = bld(features='includes', includes='.')
113 The folders only need to be relative to the current directory, the equivalent build directory is
114 added automatically (for headers created in the build directory). This enable using a build directory
115 or not (``top == out``).
117 This method will add a list of nodes read by :py:func:`waflib.Tools.ccroot.to_incnodes` in ``tg.env.INCPATHS``,
118 and the list of include paths in ``tg.env.INCLUDES``.
121 lst = self.to_incnodes(self.to_list(getattr(self, 'includes', [])) + self.env.INCLUDES)
122 self.includes_nodes = lst
123 cwd = self.get_cwd()
124 self.env.INCPATHS = [x.path_from(cwd) for x in lst]
126 class link_task(Task.Task):
128 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`.
130 .. 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
132 color = 'YELLOW'
134 weight = 3
135 """Try to process link tasks as early as possible"""
137 inst_to = None
138 """Default installation path for the link task outputs, or None to disable"""
140 chmod = Utils.O755
141 """Default installation mode for the link task outputs"""
143 def add_target(self, target):
145 Process the *target* attribute to add the platform-specific prefix/suffix such as *.so* or *.exe*.
146 The settings are retrieved from ``env.clsname_PATTERN``
148 if isinstance(target, str):
149 base = self.generator.path
150 if target.startswith('#'):
151 # for those who like flat structures
152 target = target[1:]
153 base = self.generator.bld.bldnode
155 pattern = self.env[self.__class__.__name__ + '_PATTERN']
156 if not pattern:
157 pattern = '%s'
158 folder, name = os.path.split(target)
160 if self.__class__.__name__.find('shlib') > 0 and getattr(self.generator, 'vnum', None):
161 nums = self.generator.vnum.split('.')
162 if self.env.DEST_BINFMT == 'pe':
163 # include the version in the dll file name,
164 # the import lib file name stays unversioned.
165 name = name + '-' + nums[0]
166 elif self.env.DEST_OS == 'openbsd':
167 pattern = '%s.%s' % (pattern, nums[0])
168 if len(nums) >= 2:
169 pattern += '.%s' % nums[1]
171 if folder:
172 tmp = folder + os.sep + pattern % name
173 else:
174 tmp = pattern % name
175 target = base.find_or_declare(tmp)
176 self.set_outputs(target)
178 def exec_command(self, *k, **kw):
179 ret = super(link_task, self).exec_command(*k, **kw)
180 if not ret and self.env.DO_MANIFEST:
181 ret = self.exec_mf()
182 return ret
184 def exec_mf(self):
186 Create manifest files for VS-like compilers (msvc, ifort, ...)
188 if not self.env.MT:
189 return 0
191 manifest = None
192 for out_node in self.outputs:
193 if out_node.name.endswith('.manifest'):
194 manifest = out_node.abspath()
195 break
196 else:
197 # Should never get here. If we do, it means the manifest file was
198 # never added to the outputs list, thus we don't have a manifest file
199 # to embed, so we just return.
200 return 0
202 # embedding mode. Different for EXE's and DLL's.
203 # see: http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx
204 mode = ''
205 for x in Utils.to_list(self.generator.features):
206 if x in ('cprogram', 'cxxprogram', 'fcprogram', 'fcprogram_test'):
207 mode = 1
208 elif x in ('cshlib', 'cxxshlib', 'fcshlib'):
209 mode = 2
211 Logs.debug('msvc: embedding manifest in mode %r', mode)
213 lst = [] + self.env.MT
214 lst.extend(Utils.to_list(self.env.MTFLAGS))
215 lst.extend(['-manifest', manifest])
216 lst.append('-outputresource:%s;%s' % (self.outputs[0].abspath(), mode))
218 return super(link_task, self).exec_command(lst)
220 class stlink_task(link_task):
222 Base for static link tasks, which use *ar* most of the time.
223 The target is always removed before being written.
225 run_str = '${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}'
227 chmod = Utils.O644
228 """Default installation mode for the static libraries"""
230 def rm_tgt(cls):
231 old = cls.run
232 def wrap(self):
233 try:
234 os.remove(self.outputs[0].abspath())
235 except OSError:
236 pass
237 return old(self)
238 setattr(cls, 'run', wrap)
239 rm_tgt(stlink_task)
241 @feature('c', 'cxx', 'd', 'fc', 'asm')
242 @after_method('process_source')
243 def apply_link(self):
245 Collect the tasks stored in ``compiled_tasks`` (created by :py:func:`waflib.Tools.ccroot.create_compiled_task`), and
246 use the outputs for a new instance of :py:class:`waflib.Tools.ccroot.link_task`. The class to use is the first link task
247 matching a name from the attribute *features*, for example::
249 def build(bld):
250 tg = bld(features='cxx cxxprogram cprogram', source='main.c', target='app')
252 will create the task ``tg.link_task`` as a new instance of :py:class:`waflib.Tools.cxx.cxxprogram`
255 for x in self.features:
256 if x == 'cprogram' and 'cxx' in self.features: # limited compat
257 x = 'cxxprogram'
258 elif x == 'cshlib' and 'cxx' in self.features:
259 x = 'cxxshlib'
261 if x in Task.classes:
262 if issubclass(Task.classes[x], link_task):
263 link = x
264 break
265 else:
266 return
268 objs = [t.outputs[0] for t in getattr(self, 'compiled_tasks', [])]
269 self.link_task = self.create_task(link, objs)
270 self.link_task.add_target(self.target)
272 # remember that the install paths are given by the task generators
273 try:
274 inst_to = self.install_path
275 except AttributeError:
276 inst_to = self.link_task.inst_to
277 if inst_to:
278 # install a copy of the node list we have at this moment (implib not added)
279 self.install_task = self.add_install_files(
280 install_to=inst_to, install_from=self.link_task.outputs[:],
281 chmod=self.link_task.chmod, task=self.link_task)
283 @taskgen_method
284 def use_rec(self, name, **kw):
286 Processes the ``use`` keyword recursively. This method is kind of private and only meant to be used from ``process_use``
289 if name in self.tmp_use_not or name in self.tmp_use_seen:
290 return
292 try:
293 y = self.bld.get_tgen_by_name(name)
294 except Errors.WafError:
295 self.uselib.append(name)
296 self.tmp_use_not.add(name)
297 return
299 self.tmp_use_seen.append(name)
300 y.post()
302 # bind temporary attributes on the task generator
303 y.tmp_use_objects = objects = kw.get('objects', True)
304 y.tmp_use_stlib = stlib = kw.get('stlib', True)
305 try:
306 link_task = y.link_task
307 except AttributeError:
308 y.tmp_use_var = ''
309 else:
310 objects = False
311 if not isinstance(link_task, stlink_task):
312 stlib = False
313 y.tmp_use_var = 'LIB'
314 else:
315 y.tmp_use_var = 'STLIB'
317 p = self.tmp_use_prec
318 for x in self.to_list(getattr(y, 'use', [])):
319 if self.env["STLIB_" + x]:
320 continue
321 try:
322 p[x].append(name)
323 except KeyError:
324 p[x] = [name]
325 self.use_rec(x, objects=objects, stlib=stlib)
327 @feature('c', 'cxx', 'd', 'use', 'fc')
328 @before_method('apply_incpaths', 'propagate_uselib_vars')
329 @after_method('apply_link', 'process_source')
330 def process_use(self):
332 Process the ``use`` attribute which contains a list of task generator names::
334 def build(bld):
335 bld.shlib(source='a.c', target='lib1')
336 bld.program(source='main.c', target='app', use='lib1')
338 See :py:func:`waflib.Tools.ccroot.use_rec`.
341 use_not = self.tmp_use_not = set()
342 self.tmp_use_seen = [] # we would like an ordered set
343 use_prec = self.tmp_use_prec = {}
344 self.uselib = self.to_list(getattr(self, 'uselib', []))
345 self.includes = self.to_list(getattr(self, 'includes', []))
346 names = self.to_list(getattr(self, 'use', []))
348 for x in names:
349 self.use_rec(x)
351 for x in use_not:
352 if x in use_prec:
353 del use_prec[x]
355 # topological sort
356 out = self.tmp_use_sorted = []
357 tmp = []
358 for x in self.tmp_use_seen:
359 for k in use_prec.values():
360 if x in k:
361 break
362 else:
363 tmp.append(x)
365 while tmp:
366 e = tmp.pop()
367 out.append(e)
368 try:
369 nlst = use_prec[e]
370 except KeyError:
371 pass
372 else:
373 del use_prec[e]
374 for x in nlst:
375 for y in use_prec:
376 if x in use_prec[y]:
377 break
378 else:
379 tmp.append(x)
380 if use_prec:
381 raise Errors.WafError('Cycle detected in the use processing %r' % use_prec)
382 out.reverse()
384 link_task = getattr(self, 'link_task', None)
385 for x in out:
386 y = self.bld.get_tgen_by_name(x)
387 var = y.tmp_use_var
388 if var and link_task:
389 if var == 'LIB' or y.tmp_use_stlib or x in names:
390 self.env.append_value(var, [y.target[y.target.rfind(os.sep) + 1:]])
391 self.link_task.dep_nodes.extend(y.link_task.outputs)
392 tmp_path = y.link_task.outputs[0].parent.path_from(self.get_cwd())
393 self.env.append_unique(var + 'PATH', [tmp_path])
394 else:
395 if y.tmp_use_objects:
396 self.add_objects_from_tgen(y)
398 if getattr(y, 'export_includes', None):
399 # self.includes may come from a global variable #2035
400 self.includes = self.includes + y.to_incnodes(y.export_includes)
402 if getattr(y, 'export_defines', None):
403 self.env.append_value('DEFINES', self.to_list(y.export_defines))
406 # and finally, add the use variables (no recursion needed)
407 for x in names:
408 try:
409 y = self.bld.get_tgen_by_name(x)
410 except Errors.WafError:
411 if not self.env['STLIB_' + x] and not x in self.uselib:
412 self.uselib.append(x)
413 else:
414 for k in self.to_list(getattr(y, 'use', [])):
415 if not self.env['STLIB_' + k] and not k in self.uselib:
416 self.uselib.append(k)
418 @taskgen_method
419 def accept_node_to_link(self, node):
421 PRIVATE INTERNAL USE ONLY
423 return not node.name.endswith('.pdb')
425 @taskgen_method
426 def add_objects_from_tgen(self, tg):
428 Add the objects from the depending compiled tasks as link task inputs.
430 Some objects are filtered: for instance, .pdb files are added
431 to the compiled tasks but not to the link tasks (to avoid errors)
432 PRIVATE INTERNAL USE ONLY
434 try:
435 link_task = self.link_task
436 except AttributeError:
437 pass
438 else:
439 for tsk in getattr(tg, 'compiled_tasks', []):
440 for x in tsk.outputs:
441 if self.accept_node_to_link(x):
442 link_task.inputs.append(x)
444 @taskgen_method
445 def get_uselib_vars(self):
447 :return: the *uselib* variables associated to the *features* attribute (see :py:attr:`waflib.Tools.ccroot.USELIB_VARS`)
448 :rtype: list of string
450 _vars = set()
451 for x in self.features:
452 if x in USELIB_VARS:
453 _vars |= USELIB_VARS[x]
454 return _vars
456 @feature('c', 'cxx', 'd', 'fc', 'javac', 'cs', 'uselib', 'asm')
457 @after_method('process_use')
458 def propagate_uselib_vars(self):
460 Process uselib variables for adding flags. For example, the following target::
462 def build(bld):
463 bld.env.AFLAGS_aaa = ['bar']
464 from waflib.Tools.ccroot import USELIB_VARS
465 USELIB_VARS['aaa'] = ['AFLAGS']
467 tg = bld(features='aaa', aflags='test')
469 The *aflags* attribute will be processed and this method will set::
471 tg.env.AFLAGS = ['bar', 'test']
473 _vars = self.get_uselib_vars()
474 env = self.env
475 app = env.append_value
476 feature_uselib = self.features + self.to_list(getattr(self, 'uselib', []))
477 for var in _vars:
478 y = var.lower()
479 val = getattr(self, y, [])
480 if val:
481 app(var, self.to_list(val))
483 for x in feature_uselib:
484 val = env['%s_%s' % (var, x)]
485 if val:
486 app(var, val)
488 # ============ the code above must not know anything about import libs ==========
490 @feature('cshlib', 'cxxshlib', 'fcshlib')
491 @after_method('apply_link')
492 def apply_implib(self):
494 Handle dlls and their import libs on Windows-like systems.
496 A ``.dll.a`` file called *import library* is generated.
497 It must be installed as it is required for linking the library.
499 if not self.env.DEST_BINFMT == 'pe':
500 return
502 dll = self.link_task.outputs[0]
503 if isinstance(self.target, Node.Node):
504 name = self.target.name
505 else:
506 name = os.path.split(self.target)[1]
507 implib = self.env.implib_PATTERN % name
508 implib = dll.parent.find_or_declare(implib)
509 self.env.append_value('LINKFLAGS', self.env.IMPLIB_ST % implib.bldpath())
510 self.link_task.outputs.append(implib)
512 if getattr(self, 'defs', None) and self.env.DEST_BINFMT == 'pe':
513 node = self.path.find_resource(self.defs)
514 if not node:
515 raise Errors.WafError('invalid def file %r' % self.defs)
516 if self.env.def_PATTERN:
517 self.env.append_value('LINKFLAGS', self.env.def_PATTERN % node.path_from(self.get_cwd()))
518 self.link_task.dep_nodes.append(node)
519 else:
520 # gcc for windows takes *.def file as input without any special flag
521 self.link_task.inputs.append(node)
523 # where to put the import library
524 if getattr(self, 'install_task', None):
525 try:
526 # user has given a specific installation path for the import library
527 inst_to = self.install_path_implib
528 except AttributeError:
529 try:
530 # user has given an installation path for the main library, put the import library in it
531 inst_to = self.install_path
532 except AttributeError:
533 # else, put the library in BINDIR and the import library in LIBDIR
534 inst_to = '${IMPLIBDIR}'
535 self.install_task.install_to = '${BINDIR}'
536 if not self.env.IMPLIBDIR:
537 self.env.IMPLIBDIR = self.env.LIBDIR
538 self.implib_install_task = self.add_install_files(install_to=inst_to, install_from=implib,
539 chmod=self.link_task.chmod, task=self.link_task)
541 # ============ the code above must not know anything about vnum processing on unix platforms =========
543 re_vnum = re.compile('^([1-9]\\d*|0)([.]([1-9]\\d*|0)){0,2}?$')
544 @feature('cshlib', 'cxxshlib', 'dshlib', 'fcshlib', 'vnum')
545 @after_method('apply_link', 'propagate_uselib_vars')
546 def apply_vnum(self):
548 Enforce version numbering on shared libraries. The valid version numbers must have either zero or two dots::
550 def build(bld):
551 bld.shlib(source='a.c', target='foo', vnum='14.15.16')
553 In this example on Linux platform, ``libfoo.so`` is installed as ``libfoo.so.14.15.16``, and the following symbolic links are created:
555 * ``libfoo.so → libfoo.so.14.15.16``
556 * ``libfoo.so.14 → libfoo.so.14.15.16``
558 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:
560 def build(bld):
561 bld.shlib(source='a.c', target='foo', vnum='14.15.16', cnum='14.15')
563 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.
565 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.
567 if not getattr(self, 'vnum', '') or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'):
568 return
570 link = self.link_task
571 if not re_vnum.match(self.vnum):
572 raise Errors.WafError('Invalid vnum %r for target %r' % (self.vnum, getattr(self, 'name', self)))
573 nums = self.vnum.split('.')
574 node = link.outputs[0]
576 cnum = getattr(self, 'cnum', str(nums[0]))
577 cnums = cnum.split('.')
579 libname = node.name
580 if libname.endswith('.dylib'):
581 name3 = name2 = libname.replace('.dylib', '.%s.dylib' % cnum)
582 else:
583 name3 = libname + '.' + self.vnum
584 name2 = libname + '.' + cnum
586 # add the so name for the ld linker - to disable, just unset env.SONAME_ST
587 if self.env.SONAME_ST:
588 v = self.env.SONAME_ST % name2
589 self.env.append_value('LINKFLAGS', v.split())
591 # the following task is just to enable execution from the build dir :-/
592 if self.env.DEST_OS != 'openbsd':
593 outs = [node.parent.make_node(name3)]
594 if name2 != name3:
595 outs.append(node.parent.make_node(name2))
596 self.create_task('vnum', node, outs)
598 if getattr(self, 'install_task', None):
599 self.install_task.hasrun = Task.SKIPPED
600 self.install_task.no_errcheck_out = True
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)
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.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)
628 class vnum(Task.Task):
630 Create the symbolic links for a versioned shared library. Instances are created by :py:func:`waflib.Tools.ccroot.apply_vnum`
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
644 try:
645 os.symlink(self.inputs[0].name, path)
646 except OSError:
647 return 1
649 class fake_shlib(link_task):
651 Task used for reading a system library and adding the dependency on it
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
659 class fake_stlib(stlink_task):
661 Task used for reading a system library and adding the dependency on it
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
669 @conf
670 def read_shlib(self, name, paths=[], export_includes=[], export_defines=[]):
672 Read a system shared library, enabling its use as a local library. Will trigger a rebuild if the file changes::
674 def build(bld):
675 bld.read_shlib('m')
676 bld.program(source='main.c', use='m')
678 return self(name=name, features='fake_lib', lib_paths=paths, lib_type='shlib', export_includes=export_includes, export_defines=export_defines)
680 @conf
681 def read_stlib(self, name, paths=[], export_includes=[], export_defines=[]):
683 Read a system static library, enabling a use as a local library. Will trigger a rebuild if the file changes.
685 return self(name=name, features='fake_lib', lib_paths=paths, lib_type='stlib', export_includes=export_includes, export_defines=export_defines)
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'],
692 @feature('fake_lib')
693 def process_lib(self):
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`.
697 node = None
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
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
723 class fake_o(Task.Task):
724 def runnable_status(self):
725 return Task.SKIP_ME
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]
735 @feature('fake_obj')
736 @before_method('process_source')
737 def process_objs(self):
739 Puts object files in the task generator outputs
741 for node in self.to_nodes(self.source):
742 self.add_those_o_files(node)
743 self.source = []
745 @conf
746 def read_object(self, obj):
748 Read an object file, enabling injection in libs/programs. Will trigger a rebuild if the file changes.
750 :param obj: object file path, as string or Node
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)
756 @feature('cxxprogram', 'cprogram')
757 @after_method('apply_link', 'process_use')
758 def set_full_paths_hpux(self):
760 On hp-ux, extend the libpaths and static library paths to absolute paths
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