3
# Thomas Nagy, 2005-2010 (ita)
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).
11
from waflib import TaskGen, Task, Utils, Logs, Build, Options, Node, Errors
12
from waflib.Logs import error, debug, warn
13
from waflib.TaskGen import after_method, before_method, feature, taskgen_method, extension
14
from waflib.Tools import c_aliases, c_preproc, c_config, c_osx, c_tests
15
from waflib.Configure import conf
17
USELIB_VARS = Utils.defaultdict(set)
19
Mapping for features to :py:class:`waflib.ConfigSet.ConfigSet` variables. See :py:func:`waflib.Tools.ccroot.propagate_uselib_vars`.
22
USELIB_VARS['c'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CCDEPS', 'CFLAGS', 'ARCH'])
23
USELIB_VARS['cxx'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CXXDEPS', 'CXXFLAGS', 'ARCH'])
24
USELIB_VARS['d'] = set(['INCLUDES', 'DFLAGS'])
26
USELIB_VARS['cprogram'] = USELIB_VARS['cxxprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH'])
27
USELIB_VARS['cshlib'] = USELIB_VARS['cxxshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH'])
28
USELIB_VARS['cstlib'] = USELIB_VARS['cxxstlib'] = set(['ARFLAGS', 'LINKDEPS'])
30
USELIB_VARS['dprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
31
USELIB_VARS['dshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
32
USELIB_VARS['dstlib'] = set(['ARFLAGS', 'LINKDEPS'])
34
USELIB_VARS['go'] = set(['GOCFLAGS'])
35
USELIB_VARS['goprogram'] = set(['GOLFLAGS'])
37
USELIB_VARS['asm'] = set(['ASFLAGS'])
39
# =================================================================================================
42
def create_compiled_task(self, name, node):
44
Create the compilation task: c, cxx, asm, etc. The output node is created automatically (object file with a typical **.o** extension).
45
The task is appended to the list *compiled_tasks* which is then used by :py:func:`waflib.Tools.ccroot.apply_link`
47
:param name: name of the task class
49
:param node: the file to compile
50
:type node: :py:class:`waflib.Node.Node`
51
:return: The task created
52
:rtype: :py:class:`waflib.Task.Task`
54
out = '%s.%d.o' % (node.name, self.idx)
55
task = self.create_task(name, node, node.parent.find_or_declare(out))
57
self.compiled_tasks.append(task)
58
except AttributeError:
59
self.compiled_tasks = [task]
63
def to_incnodes(self, inlst):
65
Task generator method provided to convert a list of string/nodes into a list of includes folders.
67
The paths are assumed to be relative to the task generator path, except if they begin by **#**
68
in which case they are searched from the top-level directory (``bld.srcnode``).
69
The folders are simply assumed to be existing.
71
The node objects in the list are returned in the output list. The strings are converted
72
into node objects if possible. The node is searched from the source directory, and if a match is found,
73
the equivalent build directory is created and added to the returned list too. When a folder cannot be found, it is ignored.
75
:param inlst: list of folders
76
:type inlst: space-delimited string or a list of string/nodes
77
:rtype: list of :py:class:`waflib.Node.Node`
78
:return: list of include folders as nodes
82
for x in self.to_list(inlst):
83
if x in seen or not x:
87
if isinstance(x, Node.Node):
91
lst.append(self.bld.root.make_node(x) or x)
94
p = self.bld.bldnode.make_node(x[1:])
95
v = self.bld.srcnode.make_node(x[1:])
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):
105
@feature('c', 'cxx', 'd', 'go', '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
self.env['INCPATHS'] = [x.abspath() for x in lst]
125
class link_task(Task.Task):
127
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
.. 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
134
"""Default installation path for the link task outputs, or None to disable"""
137
"""Default installation mode for the link task outputs"""
139
def add_target(self, target):
141
Process the *target* attribute to add the platform-specific prefix/suffix such as *.so* or *.exe*.
142
The settings are retrieved from ``env.clsname_PATTERN``
144
if isinstance(target, str):
145
pattern = self.env[self.__class__.__name__ + '_PATTERN']
148
folder, name = os.path.split(target)
150
if self.__class__.__name__.find('shlib') > 0:
151
if self.env.DEST_BINFMT == 'pe' and getattr(self.generator, 'vnum', None):
152
# include the version in the dll file name,
153
# the import lib file name stays unversionned.
154
name = name + '-' + self.generator.vnum.split('.')[0]
156
tmp = folder + os.sep + pattern % name
157
target = self.generator.path.find_or_declare(tmp)
158
self.set_outputs(target)
160
class stlink_task(link_task):
162
Base for static link tasks, which use *ar* most of the time.
163
The target is always removed before being written.
165
run_str = '${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}'
170
try: os.remove(self.outputs[0].abspath())
173
setattr(cls, 'run', wrap)
176
@feature('c', 'cxx', 'd', 'go', 'fc', 'asm')
177
@after_method('process_source')
178
def apply_link(self):
180
Collect the tasks stored in ``compiled_tasks`` (created by :py:func:`waflib.Tools.ccroot.create_compiled_task`), and
181
use the outputs for a new instance of :py:class:`waflib.Tools.ccroot.link_task`. The class to use is the first link task
182
matching a name from the attribute *features*, for example::
185
tg = bld(features='cxx cxxprogram cprogram', source='main.c', target='app')
187
will create the task ``tg.link_task`` as a new instance of :py:class:`waflib.Tools.cxx.cxxprogram`
190
for x in self.features:
191
if x == 'cprogram' and 'cxx' in self.features: # limited compat
193
elif x == 'cshlib' and 'cxx' in self.features:
196
if x in Task.classes:
197
if issubclass(Task.classes[x], link_task):
203
objs = [t.outputs[0] for t in getattr(self, 'compiled_tasks', [])]
204
self.link_task = self.create_task(link, objs)
205
self.link_task.add_target(self.target)
207
# remember that the install paths are given by the task generators
208
# we need to define install_task even during the build phase because others might need the installation path
210
inst_to = self.install_path
211
except AttributeError:
212
inst_to = self.link_task.__class__.inst_to
214
# install a copy of the node list we have at this moment (implib not added)
215
self.install_task = self.bld.install_files(inst_to, self.link_task.outputs[:], env=self.env, chmod=self.link_task.chmod)
218
def use_rec(self, name, **kw):
220
Processes the ``use`` keyword recursively. This method is kind of private and only meant to be used from ``process_use``
223
if name in self.tmp_use_not or name in self.tmp_use_seen:
227
y = self.bld.get_tgen_by_name(name)
228
except Errors.WafError:
229
self.uselib.append(name)
230
self.tmp_use_not.add(name)
233
self.tmp_use_seen.append(name)
236
# bind temporary attributes on the task generator
237
y.tmp_use_objects = objects = kw.get('objects', True)
238
y.tmp_use_stlib = stlib = kw.get('stlib', True)
240
link_task = y.link_task
241
except AttributeError:
245
if not isinstance(y.link_task, stlink_task):
247
y.tmp_use_var = 'LIB'
249
y.tmp_use_var = 'STLIB'
251
p = self.tmp_use_prec
252
for x in self.to_list(getattr(y, 'use', [])):
257
self.use_rec(x, objects=objects, stlib=stlib)
259
@feature('c', 'cxx', 'd', 'use', 'fc')
260
@before_method('apply_incpaths', 'propagate_uselib_vars')
261
@after_method('apply_link', 'process_source')
262
def process_use(self):
264
Process the ``use`` attribute which contains a list of task generator names::
267
bld.shlib(source='a.c', target='lib1')
268
bld.program(source='main.c', target='app', use='lib1')
270
See :py:func:`waflib.Tools.ccroot.use_rec`.
273
use_not = self.tmp_use_not = set([])
274
use_seen = self.tmp_use_seen = [] # we would like an ordered set
275
use_prec = self.tmp_use_prec = {}
276
self.uselib = self.to_list(getattr(self, 'uselib', []))
277
self.includes = self.to_list(getattr(self, 'includes', []))
278
names = self.to_list(getattr(self, 'use', []))
290
for x in self.tmp_use_seen:
291
for k in use_prec.values():
313
raise Errors.WafError('Cycle detected in the use processing %r' % use_prec)
316
link_task = getattr(self, 'link_task', None)
318
y = self.bld.get_tgen_by_name(x)
320
if var and link_task:
321
if var == 'LIB' or y.tmp_use_stlib:
322
self.env.append_value(var, [y.target[y.target.rfind(os.sep) + 1:]])
323
self.link_task.dep_nodes.extend(y.link_task.outputs)
324
tmp_path = y.link_task.outputs[0].parent.path_from(self.bld.bldnode)
325
self.env.append_value(var + 'PATH', [tmp_path])
327
if y.tmp_use_objects:
328
self.add_objects_from_tgen(y)
330
if getattr(y, 'export_includes', None):
331
self.includes.extend(y.to_incnodes(y.export_includes))
333
# and finally, add the uselib variables (no recursion needed)
336
y = self.bld.get_tgen_by_name(x)
338
if not self.env['STLIB_' + x] and not x in self.uselib:
339
self.uselib.append(x)
341
for k in self.to_list(getattr(y, 'uselib', [])):
342
if not self.env['STLIB_' + k] and not k in self.uselib:
343
self.uselib.append(k)
346
def add_objects_from_tgen(self, tg):
347
# Not public yet, wait for waf 1.6.7 at least - the purpose of this is to add pdb files to the compiled
348
# tasks but not to the link tasks (to avoid errors)
350
link_task = self.link_task
351
except AttributeError:
354
for tsk in getattr(tg, 'compiled_tasks', []):
355
for x in tsk.outputs:
356
if x.name.endswith('.o') or x.name.endswith('.obj'):
357
link_task.inputs.append(x)
360
def get_uselib_vars(self):
362
:return: the *uselib* variables associated to the *features* attribute (see :py:attr:`waflib.Tools.ccroot.USELIB_VARS`)
363
:rtype: list of string
366
for x in self.features:
368
_vars |= USELIB_VARS[x]
371
@feature('c', 'cxx', 'd', 'fc', 'javac', 'cs', 'uselib')
372
@after_method('process_use')
373
def propagate_uselib_vars(self):
375
Process uselib variables for adding flags. For example, the following target::
378
bld.env.AFLAGS_aaa = ['bar']
379
from waflib.Tools.ccroot import USELIB_VARS
380
USELIB_VARS['aaa'] = set('AFLAGS')
382
tg = bld(features='aaa', aflags='test')
384
The *aflags* attribute will be processed and this method will set::
386
tg.env.AFLAGS = ['bar', 'test']
388
_vars = self.get_uselib_vars()
393
env.append_unique(x, self.to_list(getattr(self, y, [])))
395
for x in self.features:
397
compvar = '%s_%s' % (var, x)
398
env.append_value(var, env[compvar])
400
for x in self.to_list(getattr(self, 'uselib', [])):
402
env.append_value(v, env[v + '_' + x])
404
# ============ the code above must not know anything about import libs ==========
406
@feature('cshlib', 'cxxshlib', 'fcshlib')
407
@after_method('apply_link')
408
def apply_implib(self):
410
Handle dlls and their import libs on Windows-like systems.
412
A ``.dll.a`` file called *import library* is generated.
413
It must be installed as it is required for linking the library.
415
if not self.env.DEST_BINFMT == 'pe':
418
dll = self.link_task.outputs[0]
419
if isinstance(self.target, Node.Node):
420
name = self.target.name
422
name = os.path.split(self.target)[1]
423
implib = self.env['implib_PATTERN'] % name
424
implib = dll.parent.find_or_declare(implib)
425
self.env.append_value('LINKFLAGS', self.env['IMPLIB_ST'] % implib.bldpath())
426
self.link_task.outputs.append(implib)
428
if getattr(self, 'defs', None) and self.env.DEST_BINFMT == 'pe':
429
node = self.path.find_resource(self.defs)
431
raise Errors.WafError('invalid def file %r' % self.defs)
432
if 'msvc' in (self.env.CC_NAME, self.env.CXX_NAME):
433
self.env.append_value('LINKFLAGS', '/def:%s' % node.path_from(self.bld.bldnode))
434
self.link_task.dep_nodes.append(node)
436
#gcc for windows takes *.def file a an input without any special flag
437
self.link_task.inputs.append(node)
440
inst_to = self.install_path
441
except AttributeError:
442
inst_to = self.link_task.__class__.inst_to
446
self.implib_install_task = self.bld.install_as('${PREFIX}/lib/%s' % implib.name, implib, self.env)
448
# ============ the code above must not know anything about vnum processing on unix platforms =========
450
@feature('cshlib', 'cxxshlib', 'dshlib', 'fcshlib', 'vnum')
451
@after_method('apply_link')
452
def apply_vnum(self):
454
Enforce version numbering on shared libraries. The valid version numbers must have at most two dots::
457
bld.shlib(source='a.c', target='foo', vnum='14.15.16')
459
In this example, ``libfoo.so`` is installed as ``libfoo.so.1.2.3``, and the following symbolic links are created:
461
* ``libfoo.so → libfoo.so.1.2.3``
462
* ``libfoo.so.1 → libfoo.so.1.2.3``
464
if not getattr(self, 'vnum', '') or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'):
467
link = self.link_task
468
nums = self.vnum.split('.')
469
node = link.outputs[0]
472
if libname.endswith('.dylib'):
473
name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum)
474
name2 = libname.replace('.dylib', '.%s.dylib' % nums[0])
476
name3 = libname + '.' + self.vnum
477
name2 = libname + '.' + nums[0]
479
# add the so name for the ld linker - to disable, just unset env.SONAME_ST
480
if self.env.SONAME_ST:
481
v = self.env.SONAME_ST % name2
482
self.env.append_value('LINKFLAGS', v.split())
484
# the following task is just to enable execution from the build dir :-/
485
tsk = self.create_task('vnum', node, [node.parent.find_or_declare(name2), node.parent.find_or_declare(name3)])
487
if getattr(self.bld, 'is_install', None):
488
self.install_task.hasrun = Task.SKIP_ME
490
path = self.install_task.dest
491
t1 = bld.install_as(path + os.sep + name3, node, env=self.env, chmod=self.link_task.chmod)
492
t2 = bld.symlink_as(path + os.sep + name2, name3)
493
t3 = bld.symlink_as(path + os.sep + libname, name3)
494
self.vnum_install_task = (t1, t2, t3)
496
if '-dynamiclib' in self.env['LINKFLAGS'] and getattr(self, 'install_task', None):
497
path = os.path.join(self.install_task.get_install_path(), self.link_task.outputs[0].name)
498
self.env.append_value('LINKFLAGS', ['-install_name', path])
500
class vnum(Task.Task):
502
Create the symbolic links for a versioned shared library. Instances are created by :py:func:`waflib.Tools.ccroot.apply_vnum`
508
for x in self.outputs:
516
os.symlink(self.inputs[0].name, path)
520
class fake_shlib(link_task):
522
Task used for reading a system library and adding the dependency on it
524
def runnable_status(self):
525
for t in self.run_after:
527
return Task.ASK_LATER
529
for x in self.outputs:
530
x.sig = Utils.h_file(x.abspath())
533
class fake_stlib(stlink_task):
535
Task used for reading a system library and adding the dependency on it
537
def runnable_status(self):
538
for t in self.run_after:
540
return Task.ASK_LATER
542
for x in self.outputs:
543
x.sig = Utils.h_file(x.abspath())
547
def read_shlib(self, name, paths=[]):
549
Read a system shared library, enabling its use as a local library. Will trigger a rebuild if the file changes::
553
bld.program(source='main.c', use='m')
555
return self(name=name, features='fake_lib', lib_paths=paths, lib_type='shlib')
558
def read_stlib(self, name, paths=[]):
560
Read a system static library, enabling a use as a local library. Will trigger a rebuild if the file changes.
562
return self(name=name, features='fake_lib', lib_paths=paths, lib_type='stlib')
565
'shlib' : ['lib%s.so', '%s.so', 'lib%s.dll', '%s.dll'],
566
'stlib' : ['lib%s.a', '%s.a', 'lib%s.dll', '%s.dll', 'lib%s.lib', '%s.lib'],
570
def process_lib(self):
572
Find the location of a foreign library. Used by :py:class:`waflib.Tools.ccroot.read_shlib` and :py:class:`waflib.Tools.ccroot.read_stlib`.
576
names = [x % self.name for x in lib_patterns[self.lib_type]]
577
for x in self.lib_paths + [self.path, '/usr/lib64', '/usr/lib', '/usr/local/lib64', '/usr/local/lib']:
578
if not isinstance(x, Node.Node):
579
x = self.bld.root.find_node(x) or self.path.find_node(x)
584
node = x.find_node(y)
586
node.sig = Utils.h_file(node.abspath())
592
raise Errors.WafError('could not find library %r' % self.name)
593
self.link_task = self.create_task('fake_%s' % self.lib_type, [], [node])
594
self.target = self.name
597
class fake_o(Task.Task):
598
def runnable_status(self):
601
@extension('.o', '.obj')
602
def add_those_o_files(self, node):
603
tsk = self.create_task('fake_o', [], node)
605
self.compiled_tasks.append(tsk)
606
except AttributeError:
607
self.compiled_tasks = [tsk]