3 # Thomas Nagy, 2005-2008 (ita)
6 c/c++ configuration routines
9 import os
, imp
, sys
, shlex
, shutil
11 import Build
, Utils
, Configure
, Task
, Options
, Logs
, TaskGen
12 from Constants
import *
13 from Configure
import conf
, conftest
16 'atleast-version': '>=',
17 'exact-version': '==',
31 if ((%(type_name)s *) 0) return 0;
32 if (sizeof (%(type_name)s)) return 0;
42 def parse_flags(line
, uselib
, env
):
43 """pkg-config still has bugs on some platforms, and there are many -config programs, parsing flags is necessary :-/"""
45 lst
= shlex
.split(line
)
50 app
= env
.append_value
51 if st
== '-I' or st
== '/I':
52 if not ot
: ot
= lst
.pop(0)
53 app('CPPPATH_' + uselib
, ot
)
55 if not ot
: ot
= lst
.pop(0)
56 app('CXXDEFINES_' + uselib
, ot
)
57 app('CCDEFINES_' + uselib
, ot
)
59 if not ot
: ot
= lst
.pop(0)
60 app('LIB_' + uselib
, ot
)
62 if not ot
: ot
= lst
.pop(0)
63 app('LIBPATH_' + uselib
, ot
)
64 elif x
== '-pthread' or x
.startswith('+'):
65 app('CCFLAGS_' + uselib
, x
)
66 app('CXXFLAGS_' + uselib
, x
)
67 app('LINKFLAGS_' + uselib
, x
)
68 elif x
== '-framework':
69 app('FRAMEWORK_' + uselib
, lst
.pop(0))
70 elif x
.startswith('-F'):
71 app('FRAMEWORKPATH_' + uselib
, x
[2:])
72 elif x
.startswith('-std'):
73 app('CCFLAGS_' + uselib
, x
)
74 app('CXXFLAGS_' + uselib
, x
)
75 app('LINKFLAGS_' + uselib
, x
)
76 elif x
.startswith('-Wl'):
77 app('LINKFLAGS_' + uselib
, x
)
78 elif x
.startswith('-m') or x
.startswith('-f'):
79 app('CCFLAGS_' + uselib
, x
)
80 app('CXXFLAGS_' + uselib
, x
)
83 def ret_msg(self
, f
, kw
):
84 """execute a function, when provided"""
85 if isinstance(f
, str):
90 def validate_cfg(self
, kw
):
92 kw
['path'] = 'pkg-config --errors-to-stdout --print-errors'
95 if 'atleast_pkgconfig_version' in kw
:
97 kw
['msg'] = 'Checking for pkg-config version >= %s' % kw
['atleast_pkgconfig_version']
100 # pkg-config --modversion
101 if 'modversion' in kw
:
104 if 'variables' in kw
:
106 kw
['msg'] = 'Checking for %s variables' % kw
['package']
109 # checking for the version of a module, for the moment, one thing at a time
110 for x
in cfg_ver
.keys():
111 y
= x
.replace('-', '_')
113 if not 'package' in kw
:
114 raise ValueError('%s requires a package' % x
)
117 kw
['msg'] = 'Checking for %s %s %s' % (kw
['package'], cfg_ver
[x
], kw
[y
])
121 kw
['msg'] = 'Checking for %s' % (kw
['package'] or kw
['path'])
122 if not 'okmsg' in kw
:
124 if not 'errmsg' in kw
:
125 kw
['errmsg'] = 'not found'
128 def cmd_and_log(self
, cmd
, kw
):
129 Logs
.debug('runner: %s\n' % cmd
)
131 self
.log
.write('%s\n' % cmd
)
134 p
= Utils
.pproc
.Popen(cmd
, stdout
=Utils
.pproc
.PIPE
, stderr
=Utils
.pproc
.PIPE
, shell
=True)
135 (out
, err
) = p
.communicate()
137 self
.log
.write('error %r' % e
)
140 # placeholder, don't touch
149 if not kw
.get('errmsg', ''):
150 if kw
.get('mandatory', False):
151 kw
['errmsg'] = out
.strip()
158 def exec_cfg(self
, kw
):
161 if 'atleast_pkgconfig_version' in kw
:
162 cmd
= '%s --atleast-pkgconfig-version=%s' % (kw
['path'], kw
['atleast_pkgconfig_version'])
163 self
.cmd_and_log(cmd
, kw
)
164 if not 'okmsg' in kw
:
168 # checking for the version of a module
170 y
= x
.replace('-', '_')
172 self
.cmd_and_log('%s --%s=%s %s' % (kw
['path'], x
, kw
[y
], kw
['package']), kw
)
173 if not 'okmsg' in kw
:
175 self
.define(self
.have_define(kw
.get('uselib_store', kw
['package'])), 1, 0)
178 # retrieving the version of a module
179 if 'modversion' in kw
:
180 version
= self
.cmd_and_log('%s --modversion %s' % (kw
['path'], kw
['modversion']), kw
).strip()
181 self
.define('%s_VERSION' % Utils
.quote_define_name(kw
.get('uselib_store', kw
['modversion'])), version
)
184 # retrieving variables of a module
185 if 'variables' in kw
:
186 env
= kw
.get('env', self
.env
)
187 uselib
= kw
.get('uselib_store', kw
['package'].upper())
188 vars = Utils
.to_list(kw
['variables'])
190 val
= self
.cmd_and_log('%s --variable=%s %s' % (kw
['path'], v
, kw
['package']), kw
).strip()
191 var
= '%s_%s' % (uselib
, v
)
193 if not 'okmsg' in kw
:
200 defi
= kw
.get('define_variable', None)
202 defi
= self
.env
.PKG_CONFIG_DEFINES
or {}
203 for key
, val
in defi
.iteritems():
204 lst
.append('--define-variable=%s=%s' % (key
, val
))
206 lst
.append(kw
.get('args', ''))
207 lst
.append(kw
['package'])
209 # so we assume the command-line will output flags to be parsed afterwards
211 ret
= self
.cmd_and_log(cmd
, kw
)
212 if not 'okmsg' in kw
:
215 self
.define(self
.have_define(kw
.get('uselib_store', kw
['package'])), 1, 0)
216 parse_flags(ret
, kw
.get('uselib_store', kw
['package'].upper()), kw
.get('env', self
.env
))
220 def check_cfg(self
, *k
, **kw
):
222 for pkg-config mostly, but also all the -config tools
223 conf.check_cfg(path='mpicc', args='--showme:compile --showme:link', package='', uselib_store='OPEN_MPI')
224 conf.check_cfg(package='dbus-1', variables='system_bus_default_address session_bus_services_dir')
227 self
.validate_cfg(kw
)
229 self
.check_message_1(kw
['msg'])
232 ret
= self
.exec_cfg(kw
)
233 except Configure
.ConfigurationError
, e
:
235 self
.check_message_2(kw
['errmsg'], 'YELLOW')
236 if 'mandatory' in kw
and kw
['mandatory']:
240 self
.fatal('the configuration failed (see %r)' % self
.log
.name
)
244 self
.check_message_2(self
.ret_msg(kw
['okmsg'], kw
))
248 # the idea is the following: now that we are certain
249 # that all the code here is only for c or c++, it is
250 # easy to put all the logic in one function
252 # this should prevent code duplication (ita)
254 # env: an optional environment (modified -> provide a copy)
255 # compiler: cc or cxx - it tries to guess what is best
256 # type: cprogram, cshlib, cstaticlib
257 # code: a c code to execute
258 # uselib_store: where to add the variables
259 # uselib: parameters to use for building
260 # define: define to set, like FOO in #define FOO, if not set, add /* #undef FOO */
261 # execute: True or False - will return the result of the execution
264 def validate_c(self
, kw
):
265 """validate the parameters for the test method"""
268 kw
['env'] = self
.env
.copy()
271 if not 'compiler' in kw
:
272 kw
['compiler'] = 'cc'
273 if env
['CXX_NAME'] and Task
.TaskBase
.classes
.get('cxx', None):
274 kw
['compiler'] = 'cxx'
275 if not self
.env
['CXX']:
276 self
.fatal('a c++ compiler is required')
278 if not self
.env
['CC']:
279 self
.fatal('a c compiler is required')
282 kw
['type'] = 'cprogram'
284 assert not(kw
['type'] != 'cprogram' and kw
.get('execute', 0)), 'can only execute programs'
287 #if kw['type'] != 'program' and kw.get('execute', 0):
288 # raise ValueError, 'can only execute programs'
291 if 'header_name' in dct
:
292 dct
= Utils
.to_list(dct
['header_name'])
293 return ''.join(['#include <%s>\n' % x
for x
in dct
])
297 if not 'compile_mode' in kw
:
298 kw
['compile_mode'] = (kw
['compiler'] == 'cxx') and 'cxx' or 'cc'
300 if not 'compile_filename' in kw
:
301 kw
['compile_filename'] = 'test.c' + ((kw
['compile_mode'] == 'cxx') and 'pp' or '')
304 if 'framework_name' in kw
:
305 try: TaskGen
.task_gen
.create_task_macapp
306 except AttributeError: self
.fatal('frameworks require the osx tool')
308 fwkname
= kw
['framework_name']
309 if not 'uselib_store' in kw
:
310 kw
['uselib_store'] = fwkname
.upper()
312 if not kw
.get('no_header', False):
313 if not 'header_name' in kw
:
314 kw
['header_name'] = []
315 fwk
= '%s/%s.h' % (fwkname
, fwkname
)
316 if kw
.get('remove_dot_h', None):
318 kw
['header_name'] = Utils
.to_list(kw
['header_name']) + [fwk
]
320 kw
['msg'] = 'Checking for framework %s' % fwkname
321 kw
['framework'] = fwkname
322 #kw['frameworkpath'] = set it yourself
324 if 'function_name' in kw
:
325 fu
= kw
['function_name']
327 kw
['msg'] = 'Checking for function %s' % fu
328 kw
['code'] = to_header(kw
) + SNIP1
% fu
329 if not 'uselib_store' in kw
:
330 kw
['uselib_store'] = fu
.upper()
331 if not 'define_name' in kw
:
332 kw
['define_name'] = self
.have_define(fu
)
334 elif 'type_name' in kw
:
337 kw
['msg'] = 'Checking for type %s' % tu
338 if not 'header_name' in kw
:
339 kw
['header_name'] = 'stdint.h'
340 kw
['code'] = to_header(kw
) + SNIP2
% {'type_name' : tu
}
341 if not 'define_name' in kw
:
342 kw
['define_name'] = self
.have_define(tu
.upper())
344 elif 'header_name' in kw
:
346 kw
['msg'] = 'Checking for header %s' % kw
['header_name']
348 l
= Utils
.to_list(kw
['header_name'])
349 assert len(l
)>0, 'list of headers in header_name is empty'
351 kw
['code'] = to_header(kw
) + SNIP3
353 if not 'uselib_store' in kw
:
354 kw
['uselib_store'] = l
[0].upper()
356 if not 'define_name' in kw
:
357 kw
['define_name'] = self
.have_define(l
[0])
361 kw
['msg'] = 'Checking for library %s' % kw
['lib']
362 if not 'uselib_store' in kw
:
363 kw
['uselib_store'] = kw
['lib'].upper()
365 if 'staticlib' in kw
:
367 kw
['msg'] = 'Checking for static library %s' % kw
['staticlib']
368 if not 'uselib_store' in kw
:
369 kw
['uselib_store'] = kw
['staticlib'].upper()
372 # an additional code fragment may be provided to replace the predefined code
374 kw
['code'] = kw
['fragment']
376 kw
['msg'] = 'Checking for custom code'
377 if not 'errmsg' in kw
:
380 for (flagsname
,flagstype
) in [('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')]:
383 kw
['msg'] = 'Checking for %s flags %s' % (flagstype
, kw
[flagsname
])
384 if not 'errmsg' in kw
:
387 if not 'execute' in kw
:
388 kw
['execute'] = False
390 if not 'errmsg' in kw
:
391 kw
['errmsg'] = 'not found'
393 if not 'okmsg' in kw
:
399 if not kw
.get('success'): kw
['success'] = None
401 assert 'msg' in kw
, 'invalid parameters, read http://freehackers.org/~tnagy/wafbook/single.html#config_helpers_c'
404 def post_check(self
, *k
, **kw
):
405 "set the variables after a test was run successfully"
409 if kw
['success'] is not None:
412 is_success
= (kw
['success'] == 0)
414 if 'define_name' in kw
:
415 if 'header_name' in kw
or 'function_name' in kw
or 'type_name' in kw
or 'fragment' in kw
:
418 if isinstance(key
, str):
420 self
.define(kw
['define_name'], key
, quote
=kw
.get('quote', 1))
422 self
.define_cond(kw
['define_name'], True)
424 self
.define_cond(kw
['define_name'], False)
426 self
.define_cond(kw
['define_name'], is_success
)
428 if is_success
and 'uselib_store' in kw
:
430 for k
in set(cc
.g_cc_flag_vars
).union(cxx
.g_cxx_flag_vars
):
432 # inconsistency: includes -> CPPPATH
433 if k
== 'CPPPATH': lk
= 'includes'
434 if k
== 'CXXDEFINES': lk
= 'defines'
435 if k
== 'CCDEFINES': lk
= 'defines'
438 # remove trailing slash
439 if isinstance(val
, str):
440 val
= val
.rstrip(os
.path
.sep
)
441 self
.env
.append_unique(k
+ '_' + kw
['uselib_store'], val
)
444 def check(self
, *k
, **kw
):
445 # so this will be the generic function
446 # it will be safer to use check_cxx or check_cc
448 self
.check_message_1(kw
['msg'])
451 ret
= self
.run_c_code(*k
, **kw
)
452 except Configure
.ConfigurationError
, e
:
453 self
.check_message_2(kw
['errmsg'], 'YELLOW')
454 if 'mandatory' in kw
and kw
['mandatory']:
458 self
.fatal('the configuration failed (see %r)' % self
.log
.name
)
461 self
.check_message_2(self
.ret_msg(kw
['okmsg'], kw
))
463 self
.post_check(*k
, **kw
)
464 if not kw
.get('execute', False):
469 def run_c_code(self
, *k
, **kw
):
470 test_f_name
= kw
['compile_filename']
474 # make certain to use a fresh folder - necessary for win32
475 dir = os
.path
.join(self
.blddir
, '.conf_check_%d' % k
)
477 # if the folder already exists, remove it
493 self
.fatal('cannot create a configuration test folder %r' % dir)
498 self
.fatal('cannot use the configuration test folder %r' % dir)
500 bdir
= os
.path
.join(dir, 'testbuild')
502 if not os
.path
.exists(bdir
):
507 dest
= open(os
.path
.join(dir, test_f_name
), 'w')
508 dest
.write(kw
['code'])
511 back
= os
.path
.abspath('.')
513 bld
= Build
.BuildContext()
515 bld
.all_envs
.update(self
.all_envs
)
516 bld
.all_envs
['default'] = env
517 bld
.lst_variants
= bld
.all_envs
.keys()
518 bld
.load_dirs(dir, bdir
)
522 bld
.rescan(bld
.srcnode
)
524 if not 'features' in kw
:
525 # conf.check(features='cc cprogram pyext', ...)
526 kw
['features'] = [kw
['compile_mode'], kw
['type']] # "cprogram cc"
528 o
= bld(features
=kw
['features'], source
=test_f_name
, target
='testprog')
530 for k
, v
in kw
.iteritems():
533 self
.log
.write("==>\n%s\n<==\n" % kw
['code'])
535 # compile the program
538 except Utils
.WafError
:
539 ret
= Utils
.ex_stack()
543 # chdir before returning
547 self
.log
.write('command returned %r' % ret
)
550 # if we need to run the program, try to get its result
551 # keep the name of the program to execute
553 lastprog
= o
.link_task
.outputs
[0].abspath(env
)
555 args
= Utils
.to_list(kw
.get('exec_args', []))
556 proc
= Utils
.pproc
.Popen([lastprog
] + args
, stdout
=Utils
.pproc
.PIPE
, stderr
=Utils
.pproc
.PIPE
)
557 (out
, err
) = proc
.communicate()
563 w('returncode %r' % proc
.returncode
)
566 self
.fatal(Utils
.ex_stack())
572 def check_cxx(self
, *k
, **kw
):
573 kw
['compiler'] = 'cxx'
574 return self
.check(*k
, **kw
)
577 def check_cc(self
, *k
, **kw
):
578 kw
['compiler'] = 'cc'
579 return self
.check(*k
, **kw
)
582 def define(self
, define
, value
, quote
=1):
583 """store a single define and its state into an internal list for later
584 writing to a config header file. Value can only be
585 a string or int; other types not supported. String
586 values will appear properly quoted in the generated
588 assert define
and isinstance(define
, str)
590 # ordered_dict is for writing the configuration header in order
591 tbl
= self
.env
[DEFINES
] or Utils
.ordered_dict()
593 # the user forgot to tell if the value is quoted or not
594 if isinstance(value
, str):
596 tbl
[define
] = '"%s"' % repr('"'+value
)[2:-1].replace('"', '\\"')
599 elif isinstance(value
, int):
602 raise TypeError('define %r -> %r must be a string or an int' % (define
, value
))
604 # add later to make reconfiguring faster
605 self
.env
[DEFINES
] = tbl
606 self
.env
[define
] = value
# <- not certain this is necessary
609 def undefine(self
, define
):
610 """store a single define and its state into an internal list
611 for later writing to a config header file"""
612 assert define
and isinstance(define
, str)
614 tbl
= self
.env
[DEFINES
] or Utils
.ordered_dict()
619 # add later to make reconfiguring faster
620 self
.env
[DEFINES
] = tbl
621 self
.env
[define
] = value
624 def define_cond(self
, name
, value
):
625 """Conditionally define a name.
626 Formally equivalent to: if value: define(name, 1) else: undefine(name)"""
633 def is_defined(self
, key
):
634 defines
= self
.env
[DEFINES
]
642 return value
!= UNDEFINED
645 def get_define(self
, define
):
646 "get the value of a previously stored define"
647 try: return self
.env
[DEFINES
][define
]
648 except KeyError: return None
651 def have_define(self
, name
):
652 "prefix the define with 'HAVE_' and make sure it has valid characters."
653 return self
.__dict
__.get('HAVE_PAT', 'HAVE_%s') % Utils
.quote_define_name(name
)
656 def write_config_header(self
, configfile
='', env
='', guard
='', top
=False):
657 "save the defines into a file"
658 if not configfile
: configfile
= WAF_CONFIG_H
659 waf_guard
= guard
or '_%s_WAF' % Utils
.quote_define_name(configfile
)
661 # configfile -> absolute path
662 # there is a good reason to concatenate first and to split afterwards
663 if not env
: env
= self
.env
667 diff
= Utils
.diff_path(self
.srcdir
, self
.curdir
)
668 full
= os
.sep
.join([self
.blddir
, env
.variant(), diff
, configfile
])
669 full
= os
.path
.normpath(full
)
670 (dir, base
) = os
.path
.split(full
)
672 try: os
.makedirs(dir)
675 dest
= open(full
, 'w')
676 dest
.write('/* Configuration header created by Waf - do not edit */\n')
677 dest
.write('#ifndef %s\n#define %s\n\n' % (waf_guard
, waf_guard
))
679 dest
.write(self
.get_config_header())
681 # config files are not removed on "waf clean"
682 env
.append_unique(CFG_FILES
, os
.path
.join(diff
, configfile
))
684 dest
.write('\n#endif /* %s */\n' % waf_guard
)
688 def get_config_header(self
):
689 """Fill-in the contents of the config header. Override when you need to write your own config header."""
692 tbl
= self
.env
[DEFINES
] or Utils
.ordered_dict()
693 for key
in tbl
.allkeys
:
696 config_header
.append('#define %s' % key
)
697 elif value
is UNDEFINED
:
698 config_header
.append('/* #undef %s */' % key
)
700 config_header
.append('#define %s %s' % (key
, value
))
701 return "\n".join(config_header
)
707 if v
['CPP']: cpp
= v
['CPP']
708 elif 'CPP' in conf
.environ
: cpp
= conf
.environ
['CPP']
709 if not cpp
: cpp
= conf
.find_program('cpp', var
='CPP')
710 #if not cpp: cpp = v['CC']
711 #if not cpp: cpp = v['CXX']
715 def cc_add_flags(conf
):
716 conf
.add_os_flags('CFLAGS', 'CCFLAGS')
717 conf
.add_os_flags('CPPFLAGS')
720 def cxx_add_flags(conf
):
721 conf
.add_os_flags('CXXFLAGS')
722 conf
.add_os_flags('CPPFLAGS')
725 def link_add_flags(conf
):
726 conf
.add_os_flags('LINKFLAGS')
727 conf
.add_os_flags('LDFLAGS', 'LINKFLAGS')
730 def cc_load_tools(conf
):
731 conf
.check_tool('cc')
734 def cxx_load_tools(conf
):
735 conf
.check_tool('cxx')