build(autoconf): check if we have ctdb_protocol.h in the cluster checks
[Samba/gebeck_regimport.git] / buildtools / wafadmin / Tools / config_c.py
bloba32d8aaf1ae956f59f91a9709a3d0c4bef32d593
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2008 (ita)
5 """
6 c/c++ configuration routines
7 """
9 import os, imp, sys, shlex, shutil
10 from Utils import md5
11 import Build, Utils, Configure, Task, Options, Logs, TaskGen
12 from Constants import *
13 from Configure import conf, conftest
15 cfg_ver = {
16 'atleast-version': '>=',
17 'exact-version': '==',
18 'max-version': '<=',
21 SNIP1 = '''
22 int main() {
23 void *p;
24 p=(void*)(%s);
25 return 0;
27 '''
29 SNIP2 = '''
30 int main() {
31 if ((%(type_name)s *) 0) return 0;
32 if (sizeof (%(type_name)s)) return 0;
34 '''
36 SNIP3 = '''
37 int main() {
38 return 0;
40 '''
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)
46 while lst:
47 x = lst.pop(0)
48 st = x[:2]
49 ot = x[2:]
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)
54 elif st == '-D':
55 if not ot: ot = lst.pop(0)
56 app('CXXDEFINES_' + uselib, ot)
57 app('CCDEFINES_' + uselib, ot)
58 elif st == '-l':
59 if not ot: ot = lst.pop(0)
60 app('LIB_' + uselib, ot)
61 elif st == '-L':
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)
82 @conf
83 def ret_msg(self, f, kw):
84 """execute a function, when provided"""
85 if isinstance(f, str):
86 return f
87 return f(kw)
89 @conf
90 def validate_cfg(self, kw):
91 if not 'path' in kw:
92 kw['path'] = 'pkg-config --errors-to-stdout --print-errors'
94 # pkg-config version
95 if 'atleast_pkgconfig_version' in kw:
96 if not 'msg' in kw:
97 kw['msg'] = 'Checking for pkg-config version >= %s' % kw['atleast_pkgconfig_version']
98 return
100 # pkg-config --modversion
101 if 'modversion' in kw:
102 return
104 if 'variables' in kw:
105 if not 'msg' in kw:
106 kw['msg'] = 'Checking for %s variables' % kw['package']
107 return
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('-', '_')
112 if y in kw:
113 if not 'package' in kw:
114 raise ValueError('%s requires a package' % x)
116 if not 'msg' in kw:
117 kw['msg'] = 'Checking for %s %s %s' % (kw['package'], cfg_ver[x], kw[y])
118 return
120 if not 'msg' in kw:
121 kw['msg'] = 'Checking for %s' % (kw['package'] or kw['path'])
122 if not 'okmsg' in kw:
123 kw['okmsg'] = 'yes'
124 if not 'errmsg' in kw:
125 kw['errmsg'] = 'not found'
127 @conf
128 def cmd_and_log(self, cmd, kw):
129 Logs.debug('runner: %s\n' % cmd)
130 if self.log:
131 self.log.write('%s\n' % cmd)
133 try:
134 p = Utils.pproc.Popen(cmd, stdout=Utils.pproc.PIPE, stderr=Utils.pproc.PIPE, shell=True)
135 (out, err) = p.communicate()
136 except OSError, e:
137 self.log.write('error %r' % e)
138 self.fatal(str(e))
140 # placeholder, don't touch
141 out = str(out)
142 err = str(err)
144 if self.log:
145 self.log.write(out)
146 self.log.write(err)
148 if p.returncode:
149 if not kw.get('errmsg', ''):
150 if kw.get('mandatory', False):
151 kw['errmsg'] = out.strip()
152 else:
153 kw['errmsg'] = 'no'
154 self.fatal('fail')
155 return out
157 @conf
158 def exec_cfg(self, kw):
160 # pkg-config version
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:
165 kw['okmsg'] = 'yes'
166 return
168 # checking for the version of a module
169 for x in cfg_ver:
170 y = x.replace('-', '_')
171 if y in kw:
172 self.cmd_and_log('%s --%s=%s %s' % (kw['path'], x, kw[y], kw['package']), kw)
173 if not 'okmsg' in kw:
174 kw['okmsg'] = 'yes'
175 self.define(self.have_define(kw.get('uselib_store', kw['package'])), 1, 0)
176 break
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)
182 return 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'])
189 for v in vars:
190 val = self.cmd_and_log('%s --variable=%s %s' % (kw['path'], v, kw['package']), kw).strip()
191 var = '%s_%s' % (uselib, v)
192 env[var] = val
193 if not 'okmsg' in kw:
194 kw['okmsg'] = 'yes'
195 return
197 lst = [kw['path']]
200 defi = kw.get('define_variable', None)
201 if not defi:
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
210 cmd = ' '.join(lst)
211 ret = self.cmd_and_log(cmd, kw)
212 if not 'okmsg' in kw:
213 kw['okmsg'] = 'yes'
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))
217 return ret
219 @conf
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)
228 if 'msg' in kw:
229 self.check_message_1(kw['msg'])
230 ret = None
231 try:
232 ret = self.exec_cfg(kw)
233 except Configure.ConfigurationError, e:
234 if 'errmsg' in kw:
235 self.check_message_2(kw['errmsg'], 'YELLOW')
236 if 'mandatory' in kw and kw['mandatory']:
237 if Logs.verbose > 1:
238 raise
239 else:
240 self.fatal('the configuration failed (see %r)' % self.log.name)
241 else:
242 kw['success'] = ret
243 if 'okmsg' in kw:
244 self.check_message_2(self.ret_msg(kw['okmsg'], kw))
246 return ret
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
263 @conf
264 def validate_c(self, kw):
265 """validate the parameters for the test method"""
267 if not 'env' in kw:
268 kw['env'] = self.env.copy()
270 env = kw['env']
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')
277 else:
278 if not self.env['CC']:
279 self.fatal('a c compiler is required')
281 if not 'type' in kw:
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'
290 def to_header(dct):
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])
294 return ''
296 # set the file name
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 '')
303 #OSX
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):
317 fwk = fwk[:-2]
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']
326 if not 'msg' in kw:
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:
335 tu = kw['type_name']
336 if not 'msg' 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:
345 if not 'msg' 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])
359 if 'lib' in kw:
360 if not 'msg' in kw:
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:
366 if not 'msg' 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()
371 if 'fragment' in kw:
372 # an additional code fragment may be provided to replace the predefined code
373 # in custom headers
374 kw['code'] = kw['fragment']
375 if not 'msg' in kw:
376 kw['msg'] = 'Checking for custom code'
377 if not 'errmsg' in kw:
378 kw['errmsg'] = 'no'
380 for (flagsname,flagstype) in [('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')]:
381 if flagsname in kw:
382 if not 'msg' in kw:
383 kw['msg'] = 'Checking for %s flags %s' % (flagstype, kw[flagsname])
384 if not 'errmsg' in kw:
385 kw['errmsg'] = 'no'
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:
394 kw['okmsg'] = 'yes'
396 if not 'code' in kw:
397 kw['code'] = SNIP3
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'
403 @conf
404 def post_check(self, *k, **kw):
405 "set the variables after a test was run successfully"
407 is_success = False
408 if kw['execute']:
409 if kw['success'] is not None:
410 is_success = True
411 else:
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:
416 if kw['execute']:
417 key = kw['success']
418 if isinstance(key, str):
419 if key:
420 self.define(kw['define_name'], key, quote=kw.get('quote', 1))
421 else:
422 self.define_cond(kw['define_name'], True)
423 else:
424 self.define_cond(kw['define_name'], False)
425 else:
426 self.define_cond(kw['define_name'], is_success)
428 if is_success and 'uselib_store' in kw:
429 import cc, cxx
430 for k in set(cc.g_cc_flag_vars).union(cxx.g_cxx_flag_vars):
431 lk = k.lower()
432 # inconsistency: includes -> CPPPATH
433 if k == 'CPPPATH': lk = 'includes'
434 if k == 'CXXDEFINES': lk = 'defines'
435 if k == 'CCDEFINES': lk = 'defines'
436 if lk in kw:
437 val = kw[lk]
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)
443 @conf
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
447 self.validate_c(kw)
448 self.check_message_1(kw['msg'])
449 ret = None
450 try:
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']:
455 if Logs.verbose > 1:
456 raise
457 else:
458 self.fatal('the configuration failed (see %r)' % self.log.name)
459 else:
460 kw['success'] = ret
461 self.check_message_2(self.ret_msg(kw['okmsg'], kw))
463 self.post_check(*k, **kw)
464 if not kw.get('execute', False):
465 return ret == 0
466 return ret
468 @conf
469 def run_c_code(self, *k, **kw):
470 test_f_name = kw['compile_filename']
472 k = 0
473 while k < 10000:
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
478 try:
479 shutil.rmtree(dir)
480 except OSError:
481 pass
483 try:
484 os.stat(dir)
485 except OSError:
486 break
488 k += 1
490 try:
491 os.makedirs(dir)
492 except:
493 self.fatal('cannot create a configuration test folder %r' % dir)
495 try:
496 os.stat(dir)
497 except:
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):
503 os.makedirs(bdir)
505 env = kw['env']
507 dest = open(os.path.join(dir, test_f_name), 'w')
508 dest.write(kw['code'])
509 dest.close()
511 back = os.path.abspath('.')
513 bld = Build.BuildContext()
514 bld.log = self.log
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)
520 os.chdir(dir)
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():
531 setattr(o, k, v)
533 self.log.write("==>\n%s\n<==\n" % kw['code'])
535 # compile the program
536 try:
537 bld.compile()
538 except Utils.WafError:
539 ret = Utils.ex_stack()
540 else:
541 ret = 0
543 # chdir before returning
544 os.chdir(back)
546 if ret:
547 self.log.write('command returned %r' % ret)
548 self.fatal(str(ret))
550 # if we need to run the program, try to get its result
551 # keep the name of the program to execute
552 if kw['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()
558 w = self.log.write
559 w(str(out))
560 w('\n')
561 w(str(err))
562 w('\n')
563 w('returncode %r' % proc.returncode)
564 w('\n')
565 if proc.returncode:
566 self.fatal(Utils.ex_stack())
567 ret = out
569 return ret
571 @conf
572 def check_cxx(self, *k, **kw):
573 kw['compiler'] = 'cxx'
574 return self.check(*k, **kw)
576 @conf
577 def check_cc(self, *k, **kw):
578 kw['compiler'] = 'cc'
579 return self.check(*k, **kw)
581 @conf
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
587 header file."""
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):
595 if quote:
596 tbl[define] = '"%s"' % repr('"'+value)[2:-1].replace('"', '\\"')
597 else:
598 tbl[define] = value
599 elif isinstance(value, int):
600 tbl[define] = value
601 else:
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
608 @conf
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()
616 value = UNDEFINED
617 tbl[define] = value
619 # add later to make reconfiguring faster
620 self.env[DEFINES] = tbl
621 self.env[define] = value
623 @conf
624 def define_cond(self, name, value):
625 """Conditionally define a name.
626 Formally equivalent to: if value: define(name, 1) else: undefine(name)"""
627 if value:
628 self.define(name, 1)
629 else:
630 self.undefine(name)
632 @conf
633 def is_defined(self, key):
634 defines = self.env[DEFINES]
635 if not defines:
636 return False
637 try:
638 value = defines[key]
639 except KeyError:
640 return False
641 else:
642 return value != UNDEFINED
644 @conf
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
650 @conf
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)
655 @conf
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
664 if top:
665 diff = ''
666 else:
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)
673 except: pass
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)
685 dest.close()
687 @conf
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."""
690 config_header = []
692 tbl = self.env[DEFINES] or Utils.ordered_dict()
693 for key in tbl.allkeys:
694 value = tbl[key]
695 if value is None:
696 config_header.append('#define %s' % key)
697 elif value is UNDEFINED:
698 config_header.append('/* #undef %s */' % key)
699 else:
700 config_header.append('#define %s %s' % (key, value))
701 return "\n".join(config_header)
703 @conftest
704 def find_cpp(conf):
705 v = conf.env
706 cpp = []
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']
712 v['CPP'] = cpp
714 @conftest
715 def cc_add_flags(conf):
716 conf.add_os_flags('CFLAGS', 'CCFLAGS')
717 conf.add_os_flags('CPPFLAGS')
719 @conftest
720 def cxx_add_flags(conf):
721 conf.add_os_flags('CXXFLAGS')
722 conf.add_os_flags('CPPFLAGS')
724 @conftest
725 def link_add_flags(conf):
726 conf.add_os_flags('LINKFLAGS')
727 conf.add_os_flags('LDFLAGS', 'LINKFLAGS')
729 @conftest
730 def cc_load_tools(conf):
731 conf.check_tool('cc')
733 @conftest
734 def cxx_load_tools(conf):
735 conf.check_tool('cxx')