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