1# a set of config tests that use the samba_autoconf functions 2# to test for commonly needed configuration options 3 4import os, shutil, re 5from waflib import Build, Configure, Utils, Options, Logs, Errors 6from waflib.Configure import conf 7from samba_utils import TO_LIST, ADD_LD_LIBRARY_PATH, get_string 8 9 10def add_option(self, *k, **kw): 11 '''syntax help: provide the "match" attribute to opt.add_option() so that folders can be added to specific config tests''' 12 Options.OptionsContext.parser = self 13 match = kw.get('match', []) 14 if match: 15 del kw['match'] 16 opt = self.parser.add_option(*k, **kw) 17 opt.match = match 18 return opt 19Options.OptionsContext.add_option = add_option 20 21@conf 22def check(self, *k, **kw): 23 '''Override the waf defaults to inject --with-directory options''' 24 25 if not 'env' in kw: 26 kw['env'] = self.env.derive() 27 28 # match the configuration test with specific options, for example: 29 # --with-libiconv -> Options.options.iconv_open -> "Checking for library iconv" 30 additional_dirs = [] 31 if 'msg' in kw: 32 msg = kw['msg'] 33 for x in Options.OptionsContext.parser.parser.option_list: 34 if getattr(x, 'match', None) and msg in x.match: 35 d = getattr(Options.options, x.dest, '') 36 if d: 37 additional_dirs.append(d) 38 39 # we add the additional dirs twice: once for the test data, and again if the compilation test suceeds below 40 def add_options_dir(dirs, env): 41 for x in dirs: 42 if not x in env.CPPPATH: 43 env.CPPPATH = [os.path.join(x, 'include')] + env.CPPPATH 44 if not x in env.LIBPATH: 45 env.LIBPATH = [os.path.join(x, 'lib')] + env.LIBPATH 46 47 add_options_dir(additional_dirs, kw['env']) 48 49 self.validate_c(kw) 50 self.start_msg(kw['msg']) 51 ret = None 52 try: 53 ret = self.run_c_code(*k, **kw) 54 except Configure.ConfigurationError as e: 55 self.end_msg(kw['errmsg'], 'YELLOW') 56 if 'mandatory' in kw and kw['mandatory']: 57 if Logs.verbose > 1: 58 raise 59 else: 60 self.fatal('the configuration failed (see %r)' % self.log.name) 61 else: 62 kw['success'] = ret 63 self.end_msg(self.ret_msg(kw['okmsg'], kw)) 64 65 # success! keep the CPPPATH/LIBPATH 66 add_options_dir(additional_dirs, self.env) 67 68 self.post_check(*k, **kw) 69 if not kw.get('execute', False): 70 return ret == 0 71 return ret 72 73 74@conf 75def CHECK_ICONV(conf, define='HAVE_NATIVE_ICONV'): 76 '''check if the iconv library is installed 77 optionally pass a define''' 78 if conf.CHECK_FUNCS_IN('iconv_open', 'iconv', checklibc=True, headers='iconv.h'): 79 conf.DEFINE(define, 1) 80 return True 81 return False 82 83 84@conf 85def CHECK_LARGEFILE(conf, define='HAVE_LARGEFILE'): 86 '''see what we need for largefile support''' 87 getconf_cflags = conf.CHECK_COMMAND(['getconf', 'LFS_CFLAGS']); 88 if getconf_cflags is not False: 89 if (conf.CHECK_CODE('return !(sizeof(off_t) >= 8)', 90 define='WORKING_GETCONF_LFS_CFLAGS', 91 execute=True, 92 cflags=getconf_cflags, 93 msg='Checking getconf large file support flags work')): 94 conf.ADD_CFLAGS(getconf_cflags) 95 getconf_cflags_list=TO_LIST(getconf_cflags) 96 for flag in getconf_cflags_list: 97 if flag[:2] == "-D": 98 flag_split = flag[2:].split('=') 99 if len(flag_split) == 1: 100 conf.DEFINE(flag_split[0], '1') 101 else: 102 conf.DEFINE(flag_split[0], flag_split[1]) 103 104 if conf.CHECK_CODE('return !(sizeof(off_t) >= 8)', 105 define, 106 execute=True, 107 msg='Checking for large file support without additional flags'): 108 return True 109 110 if conf.CHECK_CODE('return !(sizeof(off_t) >= 8)', 111 define, 112 execute=True, 113 cflags='-D_FILE_OFFSET_BITS=64', 114 msg='Checking for -D_FILE_OFFSET_BITS=64'): 115 conf.DEFINE('_FILE_OFFSET_BITS', 64) 116 return True 117 118 if conf.CHECK_CODE('return !(sizeof(off_t) >= 8)', 119 define, 120 execute=True, 121 cflags='-D_LARGE_FILES', 122 msg='Checking for -D_LARGE_FILES'): 123 conf.DEFINE('_LARGE_FILES', 1) 124 return True 125 return False 126 127 128@conf 129def CHECK_C_PROTOTYPE(conf, function, prototype, define, headers=None, msg=None): 130 '''verify that a C prototype matches the one on the current system''' 131 if not conf.CHECK_DECLS(function, headers=headers): 132 return False 133 if not msg: 134 msg = 'Checking C prototype for %s' % function 135 return conf.CHECK_CODE('%s; void *_x = (void *)%s' % (prototype, function), 136 define=define, 137 local_include=False, 138 headers=headers, 139 link=False, 140 execute=False, 141 msg=msg) 142 143 144@conf 145def CHECK_CHARSET_EXISTS(conf, charset, outcharset='UCS-2LE', headers=None, define=None): 146 '''check that a named charset is able to be used with iconv_open() for conversion 147 to a target charset 148 ''' 149 msg = 'Checking if can we convert from %s to %s' % (charset, outcharset) 150 if define is None: 151 define = 'HAVE_CHARSET_%s' % charset.upper().replace('-','_') 152 return conf.CHECK_CODE(''' 153 iconv_t cd = iconv_open("%s", "%s"); 154 if (cd == 0 || cd == (iconv_t)-1) return -1; 155 ''' % (charset, outcharset), 156 define=define, 157 execute=True, 158 msg=msg, 159 lib='iconv', 160 headers=headers) 161 162def find_config_dir(conf): 163 '''find a directory to run tests in''' 164 k = 0 165 while k < 10000: 166 dir = os.path.join(conf.bldnode.abspath(), '.conf_check_%d' % k) 167 try: 168 shutil.rmtree(dir) 169 except OSError: 170 pass 171 try: 172 os.stat(dir) 173 except: 174 break 175 k += 1 176 177 try: 178 os.makedirs(dir) 179 except: 180 conf.fatal('cannot create a configuration test folder %r' % dir) 181 182 try: 183 os.stat(dir) 184 except: 185 conf.fatal('cannot use the configuration test folder %r' % dir) 186 return dir 187 188@conf 189def CHECK_SHLIB_INTRASINC_NAME_FLAGS(conf, msg): 190 ''' 191 check if the waf default flags for setting the name of lib 192 are ok 193 ''' 194 195 snip = ''' 196int foo(int v) { 197 return v * 2; 198} 199''' 200 return conf.check(features='c cshlib',vnum="1",fragment=snip,msg=msg, mandatory=False) 201 202@conf 203def CHECK_NEED_LC(conf, msg): 204 '''check if we need -lc''' 205 206 dir = find_config_dir(conf) 207 208 env = conf.env 209 210 bdir = os.path.join(dir, 'testbuild2') 211 if not os.path.exists(bdir): 212 os.makedirs(bdir) 213 214 215 subdir = os.path.join(dir, "liblctest") 216 217 os.makedirs(subdir) 218 219 Utils.writef(os.path.join(subdir, 'liblc1.c'), '#include <stdio.h>\nint lib_func(void) { FILE *f = fopen("foo", "r");}\n') 220 221 bld = Build.BuildContext() 222 bld.log = conf.log 223 bld.all_envs.update(conf.all_envs) 224 bld.all_envs['default'] = env 225 bld.lst_variants = bld.all_envs.keys() 226 bld.load_dirs(dir, bdir) 227 228 bld.rescan(bld.srcnode) 229 230 bld(features='c cshlib', 231 source='liblctest/liblc1.c', 232 ldflags=conf.env['EXTRA_LDFLAGS'], 233 target='liblc', 234 name='liblc') 235 236 try: 237 bld.compile() 238 conf.check_message(msg, '', True) 239 return True 240 except: 241 conf.check_message(msg, '', False) 242 return False 243 244 245@conf 246def CHECK_SHLIB_W_PYTHON(conf, msg): 247 '''check if we need -undefined dynamic_lookup''' 248 249 dir = find_config_dir(conf) 250 snip = ''' 251#include <Python.h> 252#include <crt_externs.h> 253#define environ (*_NSGetEnviron()) 254 255static PyObject *ldb_module = NULL; 256int foo(int v) { 257 extern char **environ; 258 environ[0] = 1; 259 ldb_module = PyImport_ImportModule("ldb"); 260 return v * 2; 261}''' 262 return conf.check(features='c cshlib',uselib='PYEMBED',fragment=snip,msg=msg, mandatory=False) 263 264# this one is quite complex, and should probably be broken up 265# into several parts. I'd quite like to create a set of CHECK_COMPOUND() 266# functions that make writing complex compound tests like this much easier 267@conf 268def CHECK_LIBRARY_SUPPORT(conf, rpath=False, version_script=False, msg=None): 269 '''see if the platform supports building libraries''' 270 271 if msg is None: 272 if rpath: 273 msg = "rpath library support" 274 else: 275 msg = "building library support" 276 277 dir = find_config_dir(conf) 278 279 bdir = os.path.join(dir, 'testbuild') 280 if not os.path.exists(bdir): 281 os.makedirs(bdir) 282 283 env = conf.env 284 285 subdir = os.path.join(dir, "libdir") 286 287 os.makedirs(subdir) 288 289 Utils.writef(os.path.join(subdir, 'lib1.c'), 'int lib_func(void) { return 42; }\n') 290 Utils.writef(os.path.join(dir, 'main.c'), 291 'int lib_func(void);\n' 292 'int main(void) {return !(lib_func() == 42);}\n') 293 294 bld = Build.BuildContext() 295 bld.log = conf.log 296 bld.all_envs.update(conf.all_envs) 297 bld.all_envs['default'] = env 298 bld.lst_variants = bld.all_envs.keys() 299 bld.load_dirs(dir, bdir) 300 301 bld.rescan(bld.srcnode) 302 303 ldflags = [] 304 if version_script: 305 ldflags.append("-Wl,--version-script=%s/vscript" % bld.path.abspath()) 306 Utils.writef(os.path.join(dir,'vscript'), 'TEST_1.0A2 { global: *; };\n') 307 308 bld(features='c cshlib', 309 source='libdir/lib1.c', 310 target='libdir/lib1', 311 ldflags=ldflags, 312 name='lib1') 313 314 o = bld(features='c cprogram', 315 source='main.c', 316 target='prog1', 317 uselib_local='lib1') 318 319 if rpath: 320 o.rpath=os.path.join(bdir, 'default/libdir') 321 322 # compile the program 323 try: 324 bld.compile() 325 except: 326 conf.check_message(msg, '', False) 327 return False 328 329 # path for execution 330 lastprog = o.link_task.outputs[0].abspath(env) 331 332 if not rpath: 333 if 'LD_LIBRARY_PATH' in os.environ: 334 old_ld_library_path = os.environ['LD_LIBRARY_PATH'] 335 else: 336 old_ld_library_path = None 337 ADD_LD_LIBRARY_PATH(os.path.join(bdir, 'default/libdir')) 338 339 # we need to run the program, try to get its result 340 args = conf.SAMBA_CROSS_ARGS(msg=msg) 341 proc = Utils.subprocess.Popen([lastprog] + args, 342 stdout=Utils.subprocess.PIPE, stderr=Utils.subprocess.PIPE) 343 (out, err) = proc.communicate() 344 w = conf.log.write 345 w(str(out)) 346 w('\n') 347 w(str(err)) 348 w('\nreturncode %r\n' % proc.returncode) 349 ret = (proc.returncode == 0) 350 351 if not rpath: 352 os.environ['LD_LIBRARY_PATH'] = old_ld_library_path or '' 353 354 conf.check_message(msg, '', ret) 355 return ret 356 357 358 359@conf 360def CHECK_PERL_MANPAGE(conf, msg=None, section=None): 361 '''work out what extension perl uses for manpages''' 362 363 if msg is None: 364 if section: 365 msg = "perl man%s extension" % section 366 else: 367 msg = "perl manpage generation" 368 369 conf.start_msg(msg) 370 371 dir = find_config_dir(conf) 372 373 bdir = os.path.join(dir, 'testbuild') 374 if not os.path.exists(bdir): 375 os.makedirs(bdir) 376 377 Utils.writef(os.path.join(bdir, 'Makefile.PL'), """ 378use ExtUtils::MakeMaker; 379WriteMakefile( 380 'NAME' => 'WafTest', 381 'EXE_FILES' => [ 'WafTest' ] 382); 383""") 384 back = os.path.abspath('.') 385 os.chdir(bdir) 386 proc = Utils.subprocess.Popen(['perl', 'Makefile.PL'], 387 stdout=Utils.subprocess.PIPE, 388 stderr=Utils.subprocess.PIPE) 389 (out, err) = proc.communicate() 390 os.chdir(back) 391 392 ret = (proc.returncode == 0) 393 if not ret: 394 conf.end_msg('not found', color='YELLOW') 395 return 396 397 if section: 398 man = Utils.readf(os.path.join(bdir,'Makefile')) 399 m = re.search('MAN%sEXT\s+=\s+(\w+)' % section, man) 400 if not m: 401 conf.end_msg('not found', color='YELLOW') 402 return 403 ext = m.group(1) 404 conf.end_msg(ext) 405 return ext 406 407 conf.end_msg('ok') 408 return True 409 410 411@conf 412def CHECK_COMMAND(conf, cmd, msg=None, define=None, on_target=True, boolean=False): 413 '''run a command and return result''' 414 if msg is None: 415 msg = 'Checking %s' % ' '.join(cmd) 416 conf.COMPOUND_START(msg) 417 cmd = cmd[:] 418 if on_target: 419 cmd.extend(conf.SAMBA_CROSS_ARGS(msg=msg)) 420 try: 421 ret = get_string(Utils.cmd_output(cmd)) 422 except: 423 conf.COMPOUND_END(False) 424 return False 425 if boolean: 426 conf.COMPOUND_END('ok') 427 if define: 428 conf.DEFINE(define, '1') 429 else: 430 ret = ret.strip() 431 conf.COMPOUND_END(ret) 432 if define: 433 conf.DEFINE(define, ret, quote=True) 434 return ret 435 436 437@conf 438def CHECK_UNAME(conf): 439 '''setup SYSTEM_UNAME_* defines''' 440 ret = True 441 for v in "sysname machine release version".split(): 442 if not conf.CHECK_CODE(''' 443 int printf(const char *format, ...); 444 struct utsname n; 445 if (uname(&n) == -1) return -1; 446 printf("%%s", n.%s); 447 ''' % v, 448 define='SYSTEM_UNAME_%s' % v.upper(), 449 execute=True, 450 define_ret=True, 451 quote=True, 452 headers='sys/utsname.h', 453 local_include=False, 454 msg="Checking uname %s type" % v): 455 ret = False 456 return ret 457 458@conf 459def CHECK_INLINE(conf): 460 '''check for the right value for inline''' 461 conf.COMPOUND_START('Checking for inline') 462 for i in ['inline', '__inline__', '__inline']: 463 ret = conf.CHECK_CODE(''' 464 typedef int foo_t; 465 static %s foo_t static_foo () {return 0; } 466 %s foo_t foo () {return 0; }''' % (i, i), 467 define='INLINE_MACRO', 468 addmain=False, 469 link=False) 470 if ret: 471 if i != 'inline': 472 conf.DEFINE('inline', i, quote=False) 473 break 474 if not ret: 475 conf.COMPOUND_END(ret) 476 else: 477 conf.COMPOUND_END(i) 478 return ret 479 480@conf 481def CHECK_XSLTPROC_MANPAGES(conf): 482 '''check if xsltproc can run with the given stylesheets''' 483 484 485 if not conf.CONFIG_SET('XSLTPROC'): 486 conf.find_program('xsltproc', var='XSLTPROC') 487 if not conf.CONFIG_SET('XSLTPROC'): 488 return False 489 490 s='http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl' 491 conf.CHECK_COMMAND('%s --nonet %s 2> /dev/null' % (conf.env.get_flat('XSLTPROC'), s), 492 msg='Checking for stylesheet %s' % s, 493 define='XSLTPROC_MANPAGES', on_target=False, 494 boolean=True) 495 if not conf.CONFIG_SET('XSLTPROC_MANPAGES'): 496 print("A local copy of the docbook.xsl wasn't found on your system" \ 497 " consider installing package like docbook-xsl") 498 499# 500# Determine the standard libpath for the used compiler, 501# so we can later use that to filter out these standard 502# library paths when some tools like cups-config or 503# python-config report standard lib paths with their 504# ldflags (-L...) 505# 506@conf 507def CHECK_STANDARD_LIBPATH(conf): 508 # at least gcc and clang support this: 509 try: 510 cmd = conf.env.CC + ['-print-search-dirs'] 511 out = get_string(Utils.cmd_output(cmd)).split('\n') 512 except ValueError: 513 # option not supported by compiler - use a standard list of directories 514 dirlist = [ '/usr/lib', '/usr/lib64' ] 515 except: 516 raise Errors.WafError('Unexpected error running "%s"' % (cmd)) 517 else: 518 dirlist = [] 519 for line in out: 520 line = line.strip() 521 if line.startswith("libraries: ="): 522 dirliststr = line[len("libraries: ="):] 523 dirlist = [ os.path.normpath(x) for x in dirliststr.split(':') ] 524 break 525 526 conf.env.STANDARD_LIBPATH = dirlist 527 528