Merge branch 'develop'
[jack2.git] / waflib / Tools / ccroot.py
blobcfef8bf51f67e132db1d6a5167d770199fb2d94a
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('.')
578 if len(cnums)>len(nums) or nums[0:len(cnums)] != cnums:
579 raise Errors.WafError('invalid compatibility version %s' % cnum)
581 libname = node.name
582 if libname.endswith('.dylib'):
583 name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum)
584 name2 = libname.replace('.dylib', '.%s.dylib' % cnum)
585 else:
586 name3 = libname + '.' + self.vnum
587 name2 = libname + '.' + cnum
589 # add the so name for the ld linker - to disable, just unset env.SONAME_ST
590 if self.env.SONAME_ST:
591 v = self.env.SONAME_ST % name2
592 self.env.append_value('LINKFLAGS', v.split())
594 # the following task is just to enable execution from the build dir :-/
595 if self.env.DEST_OS != 'openbsd':
596 outs = [node.parent.make_node(name3)]
597 if name2 != name3:
598 outs.append(node.parent.make_node(name2))
599 self.create_task('vnum', node, outs)
601 if getattr(self, 'install_task', None):
602 self.install_task.hasrun = Task.SKIPPED
603 self.install_task.no_errcheck_out = True
604 path = self.install_task.install_to
605 if self.env.DEST_OS == 'openbsd':
606 libname = self.link_task.outputs[0].name
607 t1 = self.add_install_as(install_to='%s/%s' % (path, libname), install_from=node, chmod=self.link_task.chmod)
608 self.vnum_install_task = (t1,)
609 else:
610 t1 = self.add_install_as(install_to=path + os.sep + name3, install_from=node, chmod=self.link_task.chmod)
611 t3 = self.add_symlink_as(install_to=path + os.sep + libname, install_from=name3)
612 if name2 != name3:
613 t2 = self.add_symlink_as(install_to=path + os.sep + name2, install_from=name3)
614 self.vnum_install_task = (t1, t2, t3)
615 else:
616 self.vnum_install_task = (t1, t3)
618 if '-dynamiclib' in self.env.LINKFLAGS:
619 # this requires after(propagate_uselib_vars)
620 try:
621 inst_to = self.install_path
622 except AttributeError:
623 inst_to = self.link_task.inst_to
624 if inst_to:
625 p = Utils.subst_vars(inst_to, self.env)
626 path = os.path.join(p, name2)
627 self.env.append_value('LINKFLAGS', ['-install_name', path])
628 self.env.append_value('LINKFLAGS', '-Wl,-compatibility_version,%s' % cnum)
629 self.env.append_value('LINKFLAGS', '-Wl,-current_version,%s' % self.vnum)
631 class vnum(Task.Task):
633 Create the symbolic links for a versioned shared library. Instances are created by :py:func:`waflib.Tools.ccroot.apply_vnum`
635 color = 'CYAN'
636 ext_in = ['.bin']
637 def keyword(self):
638 return 'Symlinking'
639 def run(self):
640 for x in self.outputs:
641 path = x.abspath()
642 try:
643 os.remove(path)
644 except OSError:
645 pass
647 try:
648 os.symlink(self.inputs[0].name, path)
649 except OSError:
650 return 1
652 class fake_shlib(link_task):
654 Task used for reading a system library and adding the dependency on it
656 def runnable_status(self):
657 for t in self.run_after:
658 if not t.hasrun:
659 return Task.ASK_LATER
660 return Task.SKIP_ME
662 class fake_stlib(stlink_task):
664 Task used for reading a system library and adding the dependency on it
666 def runnable_status(self):
667 for t in self.run_after:
668 if not t.hasrun:
669 return Task.ASK_LATER
670 return Task.SKIP_ME
672 @conf
673 def read_shlib(self, name, paths=[], export_includes=[], export_defines=[]):
675 Read a system shared library, enabling its use as a local library. Will trigger a rebuild if the file changes::
677 def build(bld):
678 bld.read_shlib('m')
679 bld.program(source='main.c', use='m')
681 return self(name=name, features='fake_lib', lib_paths=paths, lib_type='shlib', export_includes=export_includes, export_defines=export_defines)
683 @conf
684 def read_stlib(self, name, paths=[], export_includes=[], export_defines=[]):
686 Read a system static library, enabling a use as a local library. Will trigger a rebuild if the file changes.
688 return self(name=name, features='fake_lib', lib_paths=paths, lib_type='stlib', export_includes=export_includes, export_defines=export_defines)
690 lib_patterns = {
691 'shlib' : ['lib%s.so', '%s.so', 'lib%s.dylib', 'lib%s.dll', '%s.dll'],
692 'stlib' : ['lib%s.a', '%s.a', 'lib%s.dll', '%s.dll', 'lib%s.lib', '%s.lib'],
695 @feature('fake_lib')
696 def process_lib(self):
698 Find the location of a foreign library. Used by :py:class:`waflib.Tools.ccroot.read_shlib` and :py:class:`waflib.Tools.ccroot.read_stlib`.
700 node = None
702 names = [x % self.name for x in lib_patterns[self.lib_type]]
703 for x in self.lib_paths + [self.path] + SYSTEM_LIB_PATHS:
704 if not isinstance(x, Node.Node):
705 x = self.bld.root.find_node(x) or self.path.find_node(x)
706 if not x:
707 continue
709 for y in names:
710 node = x.find_node(y)
711 if node:
712 try:
713 Utils.h_file(node.abspath())
714 except EnvironmentError:
715 raise ValueError('Could not read %r' % y)
716 break
717 else:
718 continue
719 break
720 else:
721 raise Errors.WafError('could not find library %r' % self.name)
722 self.link_task = self.create_task('fake_%s' % self.lib_type, [], [node])
723 self.target = self.name
726 class fake_o(Task.Task):
727 def runnable_status(self):
728 return Task.SKIP_ME
730 @extension('.o', '.obj')
731 def add_those_o_files(self, node):
732 tsk = self.create_task('fake_o', [], node)
733 try:
734 self.compiled_tasks.append(tsk)
735 except AttributeError:
736 self.compiled_tasks = [tsk]
738 @feature('fake_obj')
739 @before_method('process_source')
740 def process_objs(self):
742 Puts object files in the task generator outputs
744 for node in self.to_nodes(self.source):
745 self.add_those_o_files(node)
746 self.source = []
748 @conf
749 def read_object(self, obj):
751 Read an object file, enabling injection in libs/programs. Will trigger a rebuild if the file changes.
753 :param obj: object file path, as string or Node
755 if not isinstance(obj, self.path.__class__):
756 obj = self.path.find_resource(obj)
757 return self(features='fake_obj', source=obj, name=obj.name)
759 @feature('cxxprogram', 'cprogram')
760 @after_method('apply_link', 'process_use')
761 def set_full_paths_hpux(self):
763 On hp-ux, extend the libpaths and static library paths to absolute paths
765 if self.env.DEST_OS != 'hp-ux':
766 return
767 base = self.bld.bldnode.abspath()
768 for var in ['LIBPATH', 'STLIBPATH']:
769 lst = []
770 for x in self.env[var]:
771 if x.startswith('/'):
772 lst.append(x)
773 else:
774 lst.append(os.path.normpath(os.path.join(base, x)))
775 self.env[var] = lst