3
# Thomas Nagy, 2005-2008 (ita)
5
"base for all c/c++ programs and libraries"
8
import TaskGen, Task, Utils, preproc, Logs, Build, Options
9
from Logs import error, debug, warn
11
from TaskGen import taskgen, after, before, feature
12
from Constants import *
13
from Configure import conftest
15
from cStringIO import StringIO
17
from io import StringIO
19
import config_c # <- necessary for the configuration, do not touch
23
def get_cc_version(conf, cc, gcc=False, icc=False):
25
cmd = cc + ['-dM', '-E', '-']
27
p = Utils.pproc.Popen(cmd, stdin=Utils.pproc.PIPE, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE)
29
out = p.communicate()[0]
31
conf.fatal('could not determine the compiler version %r' % cmd)
37
if out.find('__INTEL_COMPILER') >= 0:
38
conf.fatal('The intel compiler pretends to be gcc')
39
if out.find('__GNUC__') < 0:
40
conf.fatal('Could not determine the compiler type')
42
if icc and out.find('__INTEL_COMPILER') < 0:
43
conf.fatal('Not icc/icpc')
51
lst = shlex.split(line)
61
return var in k and k[var] != '0'
63
# Some documentation is available at http://predef.sourceforge.net
64
# The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns.
66
'__linux__' : 'linux',
68
'__FreeBSD__' : 'freebsd',
69
'__NetBSD__' : 'netbsd',
70
'__OpenBSD__' : 'openbsd',
75
'__CYGWIN__' : 'cygwin',
84
conf.env.DEST_OS = mp1[i]
87
if isD('__APPLE__') and isD('__MACH__'):
88
conf.env.DEST_OS = 'darwin'
89
elif isD('__unix__'): # unix must be tested last as it's a generic fallback
90
conf.env.DEST_OS = 'generic'
93
conf.env.DEST_BINFMT = 'elf'
96
'__x86_64__' : 'x86_64',
100
'__sparc__' : 'sparc',
101
'__alpha__' : 'alpha',
104
'__powerpc__' : 'powerpc',
108
conf.env.DEST_CPU = mp2[i]
111
debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
112
conf.env['CC_VERSION'] = (k['__GNUC__'], k['__GNUC_MINOR__'], k['__GNUC_PATCHLEVEL__'])
116
"""Will disappear in waf 1.6"""
117
ULTRADEBUG = "ultradebug"
120
OPTIMIZED = "optimized"
123
ALL = [ULTRADEBUG, DEBUG, RELEASE, OPTIMIZED, CUSTOM]
126
"look for .h the .cpp need"
127
debug('ccroot: _scan_preprocessor(self, node, env, path_lst)')
129
# TODO waf 1.6 - assume the default input has exactly one file
131
if len(self.inputs) == 1:
132
node = self.inputs[0]
133
(nodes, names) = preproc.get_deps(node, self.env, nodepaths = self.env['INC_PATHS'])
135
debug('deps: deps for %s: %r; unresolved %r' % (str(node), nodes, names))
136
return (nodes, names)
141
for node in self.inputs:
142
(nodes, names) = preproc.get_deps(node, self.env, nodepaths = self.env['INC_PATHS'])
144
debug('deps: deps for %s: %r; unresolved %r' % (str(node), nodes, names))
146
if id(x) in seen: continue
150
if not x in all_names:
152
return (all_nodes, all_names)
154
class ccroot_abstract(TaskGen.task_gen):
155
"Parent class for programs and libraries in languages c, c++ and moc (Qt)"
156
def __init__(self, *k, **kw):
157
# COMPAT remove in waf 1.6 TODO
162
TaskGen.task_gen.__init__(self, *k, **kw)
164
def get_target_name(self):
166
for x in self.features:
167
if x in ['cshlib', 'cstaticlib']:
170
pattern = self.env[tp + '_PATTERN']
171
if not pattern: pattern = '%s'
173
dir, name = os.path.split(self.target)
175
if self.env.DEST_BINFMT == 'pe' and getattr(self, 'vnum', None) and 'cshlib' in self.features:
176
# include the version in the dll file name,
177
# the import lib file name stays unversionned.
178
name = name + '-' + self.vnum.split('.')[0]
180
return os.path.join(dir, pattern % name)
182
@feature('cc', 'cxx')
183
@before('apply_core')
184
def default_cc(self):
185
"""compiled_tasks attribute must be set before the '.c->.o' tasks can be created"""
186
Utils.def_attrs(self,
198
# The only thing we need for cross-compilation is DEST_BINFMT.
199
# At some point, we may reach a case where DEST_BINFMT is not enough, but for now it's sufficient.
200
# Currently, cross-compilation is auto-detected only for the gnu and intel compilers.
201
if not self.env.DEST_BINFMT:
202
# Infer the binary format from the os name.
203
self.env.DEST_BINFMT = Utils.unversioned_sys_platform_to_binary_format(
204
self.env.DEST_OS or Utils.unversioned_sys_platform())
206
if not self.env.BINDIR: self.env.BINDIR = Utils.subst_vars('${PREFIX}/bin', self.env)
207
if not self.env.LIBDIR: self.env.LIBDIR = Utils.subst_vars('${PREFIX}/lib${LIB_EXT}', self.env)
209
@feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
210
def apply_verif(self):
211
"""no particular order, used for diagnostic"""
212
if not (self.source or getattr(self, 'add_objects', None)):
213
raise Utils.WafError('no source files specified for %s' % self)
215
raise Utils.WafError('no target for %s' % self)
217
# TODO reference the d programs, shlibs in d.py, not here
219
@feature('cprogram', 'dprogram')
221
@before('apply_core')
222
def vars_target_cprogram(self):
223
self.default_install_path = self.env.BINDIR
224
self.default_chmod = O755
227
@feature('cshlib', 'dshlib')
228
@before('apply_core')
229
def vars_target_cshlib(self):
230
if self.env.DEST_BINFMT == 'pe':
231
# set execute bit on libs to avoid 'permission denied' (issue 283)
232
self.default_chmod = O755
233
self.default_install_path = self.env.BINDIR
235
self.default_install_path = self.env.LIBDIR
237
@feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
238
@after('apply_link', 'vars_target_cprogram', 'vars_target_cshlib')
239
def default_link_install(self):
240
"""you may kill this method to inject your own installation for the first element
241
any other install should only process its own nodes and not those from the others"""
242
if self.install_path:
243
self.bld.install_files(self.install_path, self.link_task.outputs[0], env=self.env, chmod=self.chmod)
245
@feature('cc', 'cxx')
246
@after('apply_type_vars', 'apply_lib_vars', 'apply_core')
247
def apply_incpaths(self):
248
"""used by the scanner
249
after processing the uselib for CPPPATH
250
after apply_core because some processing may add include paths
253
# TODO move the uselib processing out of here
254
for lib in self.to_list(self.uselib):
255
for path in self.env['CPPPATH_' + lib]:
258
if preproc.go_absolute:
259
for path in preproc.standard_includes:
263
for path in self.to_list(self.includes):
265
if preproc.go_absolute or not os.path.isabs(path):
268
self.env.prepend_value('CPPPATH', path)
272
if os.path.isabs(path):
273
if preproc.go_absolute:
274
node = self.bld.root.find_dir(path)
276
node = self.bld.srcnode
278
node = node.find_dir(path[1:])
280
node = self.path.find_dir(path)
283
self.env.append_value('INC_PATHS', node)
287
self.env.append_value('INC_PATHS', self.bld.srcnode)
289
@feature('cc', 'cxx')
290
@after('init_cc', 'init_cxx')
291
@before('apply_lib_vars')
292
def apply_type_vars(self):
293
"""before apply_lib_vars because we modify uselib
294
after init_cc and init_cxx because web need p_type_vars
296
for x in self.features:
297
if not x in ['cprogram', 'cstaticlib', 'cshlib']:
301
# if the type defines uselib to add, add them
302
st = self.env[x + '_USELIB']
303
if st: self.uselib = self.uselib + ' ' + st
305
# each compiler defines variables like 'shlib_CXXFLAGS', 'shlib_LINKFLAGS', etc
306
# so when we make a task generator of the type shlib, CXXFLAGS are modified accordingly
307
for var in self.p_type_vars:
308
compvar = '%s_%s' % (x, var)
310
value = self.env[compvar]
311
if value: self.env.append_value(var, value)
313
@feature('cprogram', 'cshlib', 'cstaticlib')
315
def apply_link(self):
316
"""executes after apply_core for collecting 'compiled_tasks'
317
use a custom linker if specified (self.link='name-of-custom-link-task')"""
318
link = getattr(self, 'link', None)
320
if 'cstaticlib' in self.features: link = 'static_link'
321
elif 'cxx' in self.features: link = 'cxx_link'
322
else: link = 'cc_link'
324
tsk = self.create_task(link)
325
outputs = [t.outputs[0] for t in self.compiled_tasks]
326
tsk.set_inputs(outputs)
327
tsk.set_outputs(self.path.find_or_declare(get_target_name(self)))
331
@feature('cc', 'cxx')
332
@after('apply_link', 'init_cc', 'init_cxx')
333
def apply_lib_vars(self):
334
"""after apply_link because of 'link_task'
335
after default_cc because of the attribute 'uselib'"""
338
# 1. the case of the libs defined in the project (visit ancestors first)
339
# the ancestors external libraries (uselib) will be prepended
340
self.uselib = self.to_list(self.uselib)
341
names = self.to_list(self.uselib_local)
344
tmp = Utils.deque(names) # consume a copy of the list of names
346
lib_name = tmp.popleft()
347
# visit dependencies only once
351
y = self.name_to_obj(lib_name)
353
raise Utils.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name, self.name))
357
# object has ancestors to process (shared libraries): add them to the end of the list
358
if getattr(y, 'uselib_local', None):
359
lst = y.to_list(y.uselib_local)
360
if 'cshlib' in y.features or 'cprogram' in y.features:
361
lst = [x for x in lst if not 'cstaticlib' in self.name_to_obj(x).features]
364
# link task and flags
365
if getattr(y, 'link_task', None):
367
link_name = y.target[y.target.rfind(os.sep) + 1:]
368
if 'cstaticlib' in y.features:
369
env.append_value('STATICLIB', link_name)
370
elif 'cshlib' in y.features or 'cprogram' in y.features:
371
# WARNING some linkers can link against programs
372
env.append_value('LIB', link_name)
375
self.link_task.set_run_after(y.link_task)
377
# for the recompilation
378
dep_nodes = getattr(self.link_task, 'dep_nodes', [])
379
self.link_task.dep_nodes = dep_nodes + y.link_task.outputs
381
# add the link path too
382
tmp_path = y.link_task.outputs[0].parent.bldpath(self.env)
383
if not tmp_path in env['LIBPATH']: env.prepend_value('LIBPATH', tmp_path)
385
# add ancestors uselib too - but only propagate those that have no staticlib
386
for v in self.to_list(y.uselib):
387
if not env['STATICLIB_' + v]:
388
if not v in self.uselib:
389
self.uselib.insert(0, v)
391
# if the library task generator provides 'export_incdirs', add to the include path
392
# the export_incdirs must be a list of paths relative to the other library
393
if getattr(y, 'export_incdirs', None):
394
for x in self.to_list(y.export_incdirs):
395
node = y.path.find_dir(x)
397
raise Utils.WafError('object %r: invalid folder %r in export_incdirs' % (y.target, x))
398
self.env.append_unique('INC_PATHS', node)
400
# 2. the case of the libs defined outside
401
for x in self.uselib:
402
for v in self.p_flag_vars:
403
val = self.env[v + '_' + x]
404
if val: self.env.append_value(v, val)
406
@feature('cprogram', 'cstaticlib', 'cshlib')
407
@after('init_cc', 'init_cxx', 'apply_link')
408
def apply_objdeps(self):
409
"add the .o files produced by some other object files in the same manner as uselib_local"
410
if not getattr(self, 'add_objects', None): return
413
names = self.to_list(self.add_objects)
417
# visit dependencies only once
422
# object does not exist ?
423
y = self.name_to_obj(x)
425
raise Utils.WafError('object %r was not found in uselib_local (required by add_objects %r)' % (x, self.name))
427
# object has ancestors to process first ? update the list of names
428
if getattr(y, 'add_objects', None):
430
lst = y.to_list(y.add_objects)
433
if u in seen: continue
436
if added: continue # list of names modified, loop
438
# safe to process the current object
442
for t in y.compiled_tasks:
443
self.link_task.inputs.extend(t.outputs)
445
@feature('cprogram', 'cshlib', 'cstaticlib')
446
@after('apply_lib_vars')
447
def apply_obj_vars(self):
448
"""after apply_lib_vars for uselib"""
451
staticlib_st = v['STATICLIB_ST']
452
libpath_st = v['LIBPATH_ST']
453
staticlibpath_st = v['STATICLIBPATH_ST']
454
rpath_st = v['RPATH_ST']
456
app = v.append_unique
459
v.append_value('LINKFLAGS', v['FULLSTATIC_MARKER'])
463
app('LINKFLAGS', rpath_st % i)
465
for i in v['LIBPATH']:
466
app('LINKFLAGS', libpath_st % i)
467
app('LINKFLAGS', staticlibpath_st % i)
470
v.append_value('LINKFLAGS', v['STATICLIB_MARKER'])
471
k = [(staticlib_st % i) for i in v['STATICLIB']]
474
# fully static binaries ?
475
if not v['FULLSTATIC']:
476
if v['STATICLIB'] or v['LIB']:
477
v.append_value('LINKFLAGS', v['SHLIB_MARKER'])
479
app('LINKFLAGS', [lib_st % i for i in v['LIB']])
482
def process_obj_files(self):
483
if not hasattr(self, 'obj_files'): return
484
for x in self.obj_files:
485
node = self.path.find_resource(x)
486
self.link_task.inputs.append(node)
489
def add_obj_file(self, file):
490
"""Small example on how to link object files as if they were source
491
obj = bld.create_obj('cc')
492
obj.add_obj_file('foo.o')"""
493
if not hasattr(self, 'obj_files'): self.obj_files = []
494
if not 'process_obj_files' in self.meths: self.meths.append('process_obj_files')
495
self.obj_files.append(file)
498
'cxxflag' : 'CXXFLAGS',
500
'ccflag' : 'CCFLAGS',
501
'linkflag' : 'LINKFLAGS',
502
'ldflag' : 'LINKFLAGS',
504
'libpath' : 'LIBPATH',
505
'staticlib': 'STATICLIB',
506
'staticlibpath': 'STATICLIBPATH',
508
'framework' : 'FRAMEWORK',
509
'frameworkpath' : 'FRAMEWORKPATH'
512
@feature('cc', 'cxx')
513
@before('init_cxx', 'init_cc')
514
@before('apply_lib_vars', 'apply_obj_vars', 'apply_incpaths', 'init_cc')
515
def add_extra_flags(self):
516
"""case and plural insensitive
517
before apply_obj_vars for processing the library attributes
519
for x in self.__dict__.keys():
523
if c_attrs.get(y, None):
524
self.env.append_unique(c_attrs[y], getattr(self, x))
526
# ============ the code above must not know anything about import libs ==========
529
@after('apply_link', 'default_cc')
530
@before('apply_lib_vars', 'apply_objdeps', 'default_link_install')
531
def apply_implib(self):
532
"""On mswindows, handle dlls and their import libs
533
the .dll.a is the import lib and it is required for linking so it is installed too
535
if not self.env.DEST_BINFMT == 'pe':
538
self.meths.remove('default_link_install')
540
bindir = self.install_path
541
if not bindir: return
543
# install the dll in the bin dir
544
dll = self.link_task.outputs[0]
545
self.bld.install_files(bindir, dll, self.env, self.chmod)
547
# add linker flags to generate the import lib
548
implib = self.env['implib_PATTERN'] % os.path.split(self.target)[1]
550
implib = dll.parent.find_or_declare(implib)
551
self.link_task.outputs.append(implib)
552
self.bld.install_as('${LIBDIR}/%s' % implib.name, implib, self.env)
554
self.env.append_value('LINKFLAGS', (self.env['IMPLIB_ST'] % implib.bldpath(self.env)).split())
556
# ============ the code above must not know anything about vnum processing on unix platforms =========
560
@before('apply_lib_vars', 'default_link_install')
561
def apply_vnum(self):
563
libfoo.so is installed as libfoo.so.1.2.3
565
if not getattr(self, 'vnum', '') or not 'cshlib' in self.features or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'):
568
self.meths.remove('default_link_install')
570
link = self.link_task
571
nums = self.vnum.split('.')
572
node = link.outputs[0]
575
if libname.endswith('.dylib'):
576
name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum)
577
name2 = libname.replace('.dylib', '.%s.dylib' % nums[0])
579
name3 = libname + '.' + self.vnum
580
name2 = libname + '.' + nums[0]
582
if self.env.SONAME_ST:
583
v = self.env.SONAME_ST % name2
584
self.env.append_value('LINKFLAGS', v.split())
587
nums = self.vnum.split('.')
589
path = self.install_path
592
bld.install_as(path + os.sep + name3, node, env=self.env)
593
bld.symlink_as(path + os.sep + name2, name3)
594
bld.symlink_as(path + os.sep + libname, name3)
596
# the following task is just to enable execution from the build dir :-/
597
tsk = self.create_task('vnum')
598
tsk.set_inputs([node])
599
tsk.set_outputs(node.parent.find_or_declare(name2))
601
def exec_vnum_link(self):
602
path = self.outputs[0].abspath(self.env)
609
os.symlink(self.inputs[0].name, path)
613
cls = Task.task_type_from_func('vnum', func=exec_vnum_link, ext_in='.bin', color='CYAN')
616
# ============ the --as-needed flag should added during the configuration, not at runtime =========
619
def add_as_needed(conf):
620
if conf.env.DEST_BINFMT == 'elf' and 'gcc' in (conf.env.CXX_NAME, conf.env.CC_NAME):
621
conf.env.append_unique('LINKFLAGS', '--as-needed')