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',
80 '__POWERPC__' : 'powerpc',
85 conf
.env
.DEST_OS
= mp1
[i
]
88 if isD('__APPLE__') and isD('__MACH__'):
89 conf
.env
.DEST_OS
= 'darwin'
90 elif isD('__unix__'): # unix must be tested last as it's a generic fallback
91 conf
.env
.DEST_OS
= 'generic'
94 conf
.env
.DEST_BINFMT
= 'elf'
95 elif isD('__WINNT__') or isD('__CYGWIN__'):
96 conf
.env
.DEST_BINFMT
= 'pe'
97 elif isD('__APPLE__'):
98 conf
.env
.DEST_BINFMT
= 'mac-o'
101 '__x86_64__' : 'x86_64',
105 '__sparc__' : 'sparc',
106 '__alpha__' : 'alpha',
109 '__powerpc__' : 'powerpc',
113 conf
.env
.DEST_CPU
= mp2
[i
]
116 debug('ccroot: dest platform: ' + ' '.join([conf
.env
[x
] or '?' for x
in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
117 conf
.env
['CC_VERSION'] = (k
['__GNUC__'], k
['__GNUC_MINOR__'], k
['__GNUC_PATCHLEVEL__'])
121 """Will disappear in waf 1.6"""
122 ULTRADEBUG
= "ultradebug"
125 OPTIMIZED
= "optimized"
128 ALL
= [ULTRADEBUG
, DEBUG
, RELEASE
, OPTIMIZED
, CUSTOM
]
131 "look for .h the .cpp need"
132 debug('ccroot: _scan_preprocessor(self, node, env, path_lst)')
134 # TODO waf 1.6 - assume the default input has exactly one file
136 if len(self
.inputs
) == 1:
137 node
= self
.inputs
[0]
138 (nodes
, names
) = preproc
.get_deps(node
, self
.env
, nodepaths
= self
.env
['INC_PATHS'])
140 debug('deps: deps for %s: %r; unresolved %r', str(node
), nodes
, names
)
141 return (nodes
, names
)
146 for node
in self
.inputs
:
147 (nodes
, names
) = preproc
.get_deps(node
, self
.env
, nodepaths
= self
.env
['INC_PATHS'])
149 debug('deps: deps for %s: %r; unresolved %r', str(node
), nodes
, names
)
151 if id(x
) in seen
: continue
155 if not x
in all_names
:
157 return (all_nodes
, all_names
)
159 class ccroot_abstract(TaskGen
.task_gen
):
160 "Parent class for programs and libraries in languages c, c++ and moc (Qt)"
161 def __init__(self
, *k
, **kw
):
162 # COMPAT remove in waf 1.6 TODO
167 TaskGen
.task_gen
.__init
__(self
, *k
, **kw
)
169 def get_target_name(self
):
171 for x
in self
.features
:
172 if x
in ['cshlib', 'cstaticlib']:
175 pattern
= self
.env
[tp
+ '_PATTERN']
176 if not pattern
: pattern
= '%s'
178 dir, name
= os
.path
.split(self
.target
)
180 if self
.env
.DEST_BINFMT
== 'pe' and getattr(self
, 'vnum', None) and 'cshlib' in self
.features
:
181 # include the version in the dll file name,
182 # the import lib file name stays unversionned.
183 name
= name
+ '-' + self
.vnum
.split('.')[0]
185 return os
.path
.join(dir, pattern
% name
)
187 @feature('cc', 'cxx')
188 @before('apply_core')
189 def default_cc(self
):
190 """compiled_tasks attribute must be set before the '.c->.o' tasks can be created"""
191 Utils
.def_attrs(self
,
203 # The only thing we need for cross-compilation is DEST_BINFMT.
204 # At some point, we may reach a case where DEST_BINFMT is not enough, but for now it's sufficient.
205 # Currently, cross-compilation is auto-detected only for the gnu and intel compilers.
206 if not self
.env
.DEST_BINFMT
:
207 # Infer the binary format from the os name.
208 self
.env
.DEST_BINFMT
= Utils
.unversioned_sys_platform_to_binary_format(
209 self
.env
.DEST_OS
or Utils
.unversioned_sys_platform())
211 if not self
.env
.BINDIR
: self
.env
.BINDIR
= Utils
.subst_vars('${PREFIX}/bin', self
.env
)
212 if not self
.env
.LIBDIR
: self
.env
.LIBDIR
= Utils
.subst_vars('${PREFIX}/lib${LIB_EXT}', self
.env
)
214 @feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
215 def apply_verif(self
):
216 """no particular order, used for diagnostic"""
217 if not (self
.source
or getattr(self
, 'add_objects', None) or getattr(self
, 'uselib_local', None) or getattr(self
, 'obj_files', None)):
218 raise Utils
.WafError('no source files specified for %s' % self
)
220 raise Utils
.WafError('no target for %s' % self
)
222 # TODO reference the d programs, shlibs in d.py, not here
224 @feature('cprogram', 'dprogram')
226 @before('apply_core')
227 def vars_target_cprogram(self
):
228 self
.default_install_path
= self
.env
.BINDIR
229 self
.default_chmod
= O755
232 @feature('cshlib', 'dshlib')
233 @before('apply_core')
234 def vars_target_cshlib(self
):
235 if self
.env
.DEST_BINFMT
== 'pe':
236 # set execute bit on libs to avoid 'permission denied' (issue 283)
237 self
.default_chmod
= O755
238 self
.default_install_path
= self
.env
.BINDIR
240 self
.default_install_path
= self
.env
.LIBDIR
242 @feature('cprogram', 'dprogram', 'cstaticlib', 'dstaticlib', 'cshlib', 'dshlib')
243 @after('apply_link', 'vars_target_cprogram', 'vars_target_cshlib')
244 def default_link_install(self
):
245 """you may kill this method to inject your own installation for the first element
246 any other install should only process its own nodes and not those from the others"""
247 if self
.install_path
:
248 self
.bld
.install_files(self
.install_path
, self
.link_task
.outputs
[0], env
=self
.env
, chmod
=self
.chmod
)
250 @feature('cc', 'cxx')
251 @after('apply_type_vars', 'apply_lib_vars', 'apply_core')
252 def apply_incpaths(self
):
253 """used by the scanner
254 after processing the uselib for CPPPATH
255 after apply_core because some processing may add include paths
258 # TODO move the uselib processing out of here
259 for lib
in self
.to_list(self
.uselib
):
260 for path
in self
.env
['CPPPATH_' + lib
]:
263 if preproc
.go_absolute
:
264 for path
in preproc
.standard_includes
:
268 for path
in self
.to_list(self
.includes
):
270 if preproc
.go_absolute
or not os
.path
.isabs(path
):
273 self
.env
.prepend_value('CPPPATH', path
)
277 if os
.path
.isabs(path
):
278 if preproc
.go_absolute
:
279 node
= self
.bld
.root
.find_dir(path
)
281 node
= self
.bld
.srcnode
283 node
= node
.find_dir(path
[1:])
285 node
= self
.path
.find_dir(path
)
288 self
.env
.append_value('INC_PATHS', node
)
292 self
.env
.append_value('INC_PATHS', self
.bld
.srcnode
)
294 @feature('cc', 'cxx')
295 @after('init_cc', 'init_cxx')
296 @before('apply_lib_vars')
297 def apply_type_vars(self
):
298 """before apply_lib_vars because we modify uselib
299 after init_cc and init_cxx because web need p_type_vars
301 for x
in self
.features
:
302 if not x
in ['cprogram', 'cstaticlib', 'cshlib']:
306 # if the type defines uselib to add, add them
307 st
= self
.env
[x
+ '_USELIB']
308 if st
: self
.uselib
= self
.uselib
+ ' ' + st
310 # each compiler defines variables like 'shlib_CXXFLAGS', 'shlib_LINKFLAGS', etc
311 # so when we make a task generator of the type shlib, CXXFLAGS are modified accordingly
312 for var
in self
.p_type_vars
:
313 compvar
= '%s_%s' % (x
, var
)
315 value
= self
.env
[compvar
]
316 if value
: self
.env
.append_value(var
, value
)
318 @feature('cprogram', 'cshlib', 'cstaticlib')
320 def apply_link(self
):
321 """executes after apply_core for collecting 'compiled_tasks'
322 use a custom linker if specified (self.link='name-of-custom-link-task')"""
323 link
= getattr(self
, 'link', None)
325 if 'cstaticlib' in self
.features
: link
= 'static_link'
326 elif 'cxx' in self
.features
: link
= 'cxx_link'
327 else: link
= 'cc_link'
329 tsk
= self
.create_task(link
)
330 outputs
= [t
.outputs
[0] for t
in self
.compiled_tasks
]
331 tsk
.set_inputs(outputs
)
332 tsk
.set_outputs(self
.path
.find_or_declare(get_target_name(self
)))
336 @feature('cc', 'cxx')
337 @after('apply_link', 'init_cc', 'init_cxx', 'apply_core')
338 def apply_lib_vars(self
):
339 """after apply_link because of 'link_task'
340 after default_cc because of the attribute 'uselib'"""
342 # after 'apply_core' in case if 'cc' if there is no link
346 # 1. the case of the libs defined in the project (visit ancestors first)
347 # the ancestors external libraries (uselib) will be prepended
348 self
.uselib
= self
.to_list(self
.uselib
)
349 names
= self
.to_list(self
.uselib_local
)
352 tmp
= Utils
.deque(names
) # consume a copy of the list of names
354 lib_name
= tmp
.popleft()
355 # visit dependencies only once
359 y
= self
.name_to_obj(lib_name
)
361 raise Utils
.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name
, self
.name
))
365 # object has ancestors to process (shared libraries): add them to the end of the list
366 if getattr(y
, 'uselib_local', None):
367 lst
= y
.to_list(y
.uselib_local
)
368 if 'cshlib' in y
.features
or 'cprogram' in y
.features
:
369 lst
= [x
for x
in lst
if not 'cstaticlib' in self
.name_to_obj(x
).features
]
372 # link task and flags
373 if getattr(y
, 'link_task', None):
375 link_name
= y
.target
[y
.target
.rfind(os
.sep
) + 1:]
376 if 'cstaticlib' in y
.features
:
377 env
.append_value('STATICLIB', link_name
)
378 elif 'cshlib' in y
.features
or 'cprogram' in y
.features
:
379 # WARNING some linkers can link against programs
380 env
.append_value('LIB', link_name
)
383 self
.link_task
.set_run_after(y
.link_task
)
385 # for the recompilation
386 dep_nodes
= getattr(self
.link_task
, 'dep_nodes', [])
387 self
.link_task
.dep_nodes
= dep_nodes
+ y
.link_task
.outputs
389 # add the link path too
390 tmp_path
= y
.link_task
.outputs
[0].parent
.bldpath(self
.env
)
391 if not tmp_path
in env
['LIBPATH']: env
.prepend_value('LIBPATH', tmp_path
)
393 # add ancestors uselib too - but only propagate those that have no staticlib
394 for v
in self
.to_list(y
.uselib
):
395 if not env
['STATICLIB_' + v
]:
396 if not v
in self
.uselib
:
397 self
.uselib
.insert(0, v
)
399 # if the library task generator provides 'export_incdirs', add to the include path
400 # the export_incdirs must be a list of paths relative to the other library
401 if getattr(y
, 'export_incdirs', None):
402 for x
in self
.to_list(y
.export_incdirs
):
403 node
= y
.path
.find_dir(x
)
405 raise Utils
.WafError('object %r: invalid folder %r in export_incdirs' % (y
.target
, x
))
406 self
.env
.append_unique('INC_PATHS', node
)
408 # 2. the case of the libs defined outside
409 for x
in self
.uselib
:
410 for v
in self
.p_flag_vars
:
411 val
= self
.env
[v
+ '_' + x
]
412 if val
: self
.env
.append_value(v
, val
)
414 @feature('cprogram', 'cstaticlib', 'cshlib')
415 @after('init_cc', 'init_cxx', 'apply_link')
416 def apply_objdeps(self
):
417 "add the .o files produced by some other object files in the same manner as uselib_local"
418 if not getattr(self
, 'add_objects', None): return
421 names
= self
.to_list(self
.add_objects
)
425 # visit dependencies only once
430 # object does not exist ?
431 y
= self
.name_to_obj(x
)
433 raise Utils
.WafError('object %r was not found in uselib_local (required by add_objects %r)' % (x
, self
.name
))
435 # object has ancestors to process first ? update the list of names
436 if getattr(y
, 'add_objects', None):
438 lst
= y
.to_list(y
.add_objects
)
441 if u
in seen
: continue
444 if added
: continue # list of names modified, loop
446 # safe to process the current object
450 for t
in y
.compiled_tasks
:
451 self
.link_task
.inputs
.extend(t
.outputs
)
453 @feature('cprogram', 'cshlib', 'cstaticlib')
454 @after('apply_lib_vars')
455 def apply_obj_vars(self
):
456 """after apply_lib_vars for uselib"""
459 staticlib_st
= v
['STATICLIB_ST']
460 libpath_st
= v
['LIBPATH_ST']
461 staticlibpath_st
= v
['STATICLIBPATH_ST']
462 rpath_st
= v
['RPATH_ST']
464 app
= v
.append_unique
467 v
.append_value('LINKFLAGS', v
['FULLSTATIC_MARKER'])
471 app('LINKFLAGS', rpath_st
% i
)
473 for i
in v
['LIBPATH']:
474 app('LINKFLAGS', libpath_st
% i
)
475 app('LINKFLAGS', staticlibpath_st
% i
)
478 v
.append_value('LINKFLAGS', v
['STATICLIB_MARKER'])
479 k
= [(staticlib_st
% i
) for i
in v
['STATICLIB']]
482 # fully static binaries ?
483 if not v
['FULLSTATIC']:
484 if v
['STATICLIB'] or v
['LIB']:
485 v
.append_value('LINKFLAGS', v
['SHLIB_MARKER'])
487 app('LINKFLAGS', [lib_st
% i
for i
in v
['LIB']])
490 def process_obj_files(self
):
491 if not hasattr(self
, 'obj_files'): return
492 for x
in self
.obj_files
:
493 node
= self
.path
.find_resource(x
)
494 self
.link_task
.inputs
.append(node
)
497 def add_obj_file(self
, file):
498 """Small example on how to link object files as if they were source
499 obj = bld.create_obj('cc')
500 obj.add_obj_file('foo.o')"""
501 if not hasattr(self
, 'obj_files'): self
.obj_files
= []
502 if not 'process_obj_files' in self
.meths
: self
.meths
.append('process_obj_files')
503 self
.obj_files
.append(file)
506 'cxxflag' : 'CXXFLAGS',
508 'ccflag' : 'CCFLAGS',
509 'linkflag' : 'LINKFLAGS',
510 'ldflag' : 'LINKFLAGS',
512 'libpath' : 'LIBPATH',
513 'staticlib': 'STATICLIB',
514 'staticlibpath': 'STATICLIBPATH',
516 'framework' : 'FRAMEWORK',
517 'frameworkpath' : 'FRAMEWORKPATH'
520 @feature('cc', 'cxx')
521 @before('init_cxx', 'init_cc')
522 @before('apply_lib_vars', 'apply_obj_vars', 'apply_incpaths', 'init_cc')
523 def add_extra_flags(self
):
524 """case and plural insensitive
525 before apply_obj_vars for processing the library attributes
527 for x
in self
.__dict
__.keys():
531 if c_attrs
.get(y
, None):
532 self
.env
.append_unique(c_attrs
[y
], getattr(self
, x
))
534 # ============ the code above must not know anything about import libs ==========
537 @after('apply_link', 'default_cc')
538 @before('apply_lib_vars', 'apply_objdeps', 'default_link_install')
539 def apply_implib(self
):
540 """On mswindows, handle dlls and their import libs
541 the .dll.a is the import lib and it is required for linking so it is installed too
543 if not self
.env
.DEST_BINFMT
== 'pe':
546 self
.meths
.remove('default_link_install')
548 bindir
= self
.install_path
549 if not bindir
: return
551 # install the dll in the bin dir
552 dll
= self
.link_task
.outputs
[0]
553 self
.bld
.install_files(bindir
, dll
, self
.env
, self
.chmod
)
555 # add linker flags to generate the import lib
556 implib
= self
.env
['implib_PATTERN'] % os
.path
.split(self
.target
)[1]
558 implib
= dll
.parent
.find_or_declare(implib
)
559 self
.link_task
.outputs
.append(implib
)
560 self
.bld
.install_as('${LIBDIR}/%s' % implib
.name
, implib
, self
.env
)
562 self
.env
.append_value('LINKFLAGS', (self
.env
['IMPLIB_ST'] % implib
.bldpath(self
.env
)).split())
564 # ============ the code above must not know anything about vnum processing on unix platforms =========
568 @before('apply_lib_vars', 'default_link_install')
569 def apply_vnum(self
):
571 libfoo.so is installed as libfoo.so.1.2.3
573 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'):
576 self
.meths
.remove('default_link_install')
578 link
= self
.link_task
579 nums
= self
.vnum
.split('.')
580 node
= link
.outputs
[0]
583 if libname
.endswith('.dylib'):
584 name3
= libname
.replace('.dylib', '.%s.dylib' % self
.vnum
)
585 name2
= libname
.replace('.dylib', '.%s.dylib' % nums
[0])
587 name3
= libname
+ '.' + self
.vnum
588 name2
= libname
+ '.' + nums
[0]
590 if self
.env
.SONAME_ST
:
591 v
= self
.env
.SONAME_ST
% name2
592 self
.env
.append_value('LINKFLAGS', v
.split())
595 nums
= self
.vnum
.split('.')
597 path
= self
.install_path
600 bld
.install_as(path
+ os
.sep
+ name3
, node
, env
=self
.env
)
601 bld
.symlink_as(path
+ os
.sep
+ name2
, name3
)
602 bld
.symlink_as(path
+ os
.sep
+ libname
, name3
)
604 # the following task is just to enable execution from the build dir :-/
605 self
.create_task('vnum', node
, [node
.parent
.find_or_declare(name2
), node
.parent
.find_or_declare(name3
)])
607 def exec_vnum_link(self
):
608 for x
in self
.outputs
:
609 path
= x
.abspath(self
.env
)
616 os
.symlink(self
.inputs
[0].name
, path
)
620 cls
= Task
.task_type_from_func('vnum', func
=exec_vnum_link
, ext_in
='.bin', color
='CYAN')
623 # ============ the --as-needed flag should added during the configuration, not at runtime =========
626 def add_as_needed(conf
):
627 if conf
.env
.DEST_BINFMT
== 'elf' and 'gcc' in (conf
.env
.CXX_NAME
, conf
.env
.CC_NAME
):
628 conf
.env
.append_unique('LINKFLAGS', '--as-needed')