ctdb-daemon: Use ctdb_parse_node_address() in ctdbd
[samba4-gss.git] / third_party / waf / waflib / Configure.py
blobf6fdc4e94a79d724daefab889c250b2f2eb7c54f
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2018 (ita)
5 """
6 Configuration system
8 A :py:class:`waflib.Configure.ConfigurationContext` instance is created when ``waf configure`` is called, it is used to:
10 * create data dictionaries (ConfigSet instances)
11 * store the list of modules to import
12 * hold configuration routines such as ``find_program``, etc
13 """
15 import os, re, shlex, shutil, sys, time, traceback
16 from waflib import ConfigSet, Utils, Options, Logs, Context, Build, Errors
18 WAF_CONFIG_LOG = 'config.log'
19 """Name of the configuration log file"""
21 autoconfig = False
22 """Execute the configuration automatically"""
24 conf_template = '''# project %(app)s configured on %(now)s by
25 # waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s)
26 # using %(args)s
27 #'''
29 class ConfigurationContext(Context.Context):
30 '''configures the project'''
32 cmd = 'configure'
34 error_handlers = []
35 """
36 Additional functions to handle configuration errors
37 """
39 def __init__(self, **kw):
40 super(ConfigurationContext, self).__init__(**kw)
41 self.environ = dict(os.environ)
42 self.all_envs = {}
44 self.top_dir = None
45 self.out_dir = None
47 self.tools = [] # tools loaded in the configuration, and that will be loaded when building
49 self.hash = 0
50 self.files = []
52 self.tool_cache = []
54 self.setenv('')
56 def setenv(self, name, env=None):
57 """
58 Set a new config set for conf.env. If a config set of that name already exists,
59 recall it without modification.
61 The name is the filename prefix to save to ``c4che/NAME_cache.py``, and it
62 is also used as *variants* by the build commands.
63 Though related to variants, whatever kind of data may be stored in the config set::
65 def configure(cfg):
66 cfg.env.ONE = 1
67 cfg.setenv('foo')
68 cfg.env.ONE = 2
70 def build(bld):
71 2 == bld.env_of_name('foo').ONE
73 :param name: name of the configuration set
74 :type name: string
75 :param env: ConfigSet to copy, or an empty ConfigSet is created
76 :type env: :py:class:`waflib.ConfigSet.ConfigSet`
77 """
78 if name not in self.all_envs or env:
79 if not env:
80 env = ConfigSet.ConfigSet()
81 self.prepare_env(env)
82 else:
83 env = env.derive()
84 self.all_envs[name] = env
85 self.variant = name
87 def get_env(self):
88 """Getter for the env property"""
89 return self.all_envs[self.variant]
90 def set_env(self, val):
91 """Setter for the env property"""
92 self.all_envs[self.variant] = val
94 env = property(get_env, set_env)
96 def init_dirs(self):
97 """
98 Initialize the project directory and the build directory
99 """
101 top = self.top_dir
102 if not top:
103 top = Options.options.top
104 if not top:
105 top = getattr(Context.g_module, Context.TOP, None)
106 if not top:
107 top = self.path.abspath()
108 top = os.path.abspath(top)
110 self.srcnode = (os.path.isabs(top) and self.root or self.path).find_dir(top)
111 assert(self.srcnode)
113 out = self.out_dir
114 if not out:
115 out = Options.options.out
116 if not out:
117 out = getattr(Context.g_module, Context.OUT, None)
118 if not out:
119 out = Options.lockfile.replace('.lock-waf_%s_' % sys.platform, '').replace('.lock-waf', '')
121 # someone can be messing with symlinks
122 out = os.path.realpath(out)
124 self.bldnode = (os.path.isabs(out) and self.root or self.path).make_node(out)
125 self.bldnode.mkdir()
127 if not os.path.isdir(self.bldnode.abspath()):
128 self.fatal('Could not create the build directory %s' % self.bldnode.abspath())
130 def execute(self):
132 See :py:func:`waflib.Context.Context.execute`
134 self.init_dirs()
136 self.cachedir = self.bldnode.make_node(Build.CACHE_DIR)
137 self.cachedir.mkdir()
139 path = os.path.join(self.bldnode.abspath(), WAF_CONFIG_LOG)
140 self.logger = Logs.make_logger(path, 'cfg')
142 app = getattr(Context.g_module, 'APPNAME', '')
143 if app:
144 ver = getattr(Context.g_module, 'VERSION', '')
145 if ver:
146 app = "%s (%s)" % (app, ver)
148 params = {'now': time.ctime(), 'pyver': sys.hexversion, 'systype': sys.platform, 'args': " ".join(sys.argv), 'wafver': Context.WAFVERSION, 'abi': Context.ABI, 'app': app}
149 self.to_log(conf_template % params)
150 self.msg('Setting top to', self.srcnode.abspath())
151 self.msg('Setting out to', self.bldnode.abspath())
153 if id(self.srcnode) == id(self.bldnode):
154 Logs.warn('Setting top == out')
155 elif id(self.path) != id(self.srcnode):
156 if self.srcnode.is_child_of(self.path):
157 Logs.warn('Are you certain that you do not want to set top="." ?')
159 super(ConfigurationContext, self).execute()
161 self.store()
163 Context.top_dir = self.srcnode.abspath()
164 Context.out_dir = self.bldnode.abspath()
166 # this will write a configure lock so that subsequent builds will
167 # consider the current path as the root directory (see prepare_impl).
168 # to remove: use 'waf distclean'
169 env = ConfigSet.ConfigSet()
170 env.argv = sys.argv
171 env.options = Options.options.__dict__
172 env.config_cmd = self.cmd
174 env.run_dir = Context.run_dir
175 env.top_dir = Context.top_dir
176 env.out_dir = Context.out_dir
178 # conf.hash & conf.files hold wscript files paths and hash
179 # (used only by Configure.autoconfig)
180 env.hash = self.hash
181 env.files = self.files
182 env.environ = dict(self.environ)
183 env.launch_dir = Context.launch_dir
185 if not (self.env.NO_LOCK_IN_RUN or env.environ.get('NO_LOCK_IN_RUN') or getattr(Options.options, 'no_lock_in_run')):
186 env.store(os.path.join(Context.run_dir, Options.lockfile))
187 if not (self.env.NO_LOCK_IN_TOP or env.environ.get('NO_LOCK_IN_TOP') or getattr(Options.options, 'no_lock_in_top')):
188 env.store(os.path.join(Context.top_dir, Options.lockfile))
189 if not (self.env.NO_LOCK_IN_OUT or env.environ.get('NO_LOCK_IN_OUT') or getattr(Options.options, 'no_lock_in_out')):
190 env.store(os.path.join(Context.out_dir, Options.lockfile))
192 def prepare_env(self, env):
194 Insert *PREFIX*, *BINDIR* and *LIBDIR* values into ``env``
196 :type env: :py:class:`waflib.ConfigSet.ConfigSet`
197 :param env: a ConfigSet, usually ``conf.env``
199 if not env.PREFIX:
200 if Options.options.prefix or Utils.is_win32:
201 env.PREFIX = Options.options.prefix
202 else:
203 env.PREFIX = '/'
204 if not env.BINDIR:
205 if Options.options.bindir:
206 env.BINDIR = Options.options.bindir
207 else:
208 env.BINDIR = Utils.subst_vars('${PREFIX}/bin', env)
209 if not env.LIBDIR:
210 if Options.options.libdir:
211 env.LIBDIR = Options.options.libdir
212 else:
213 env.LIBDIR = Utils.subst_vars('${PREFIX}/lib%s' % Utils.lib64(), env)
215 def store(self):
216 """Save the config results into the cache file"""
217 n = self.cachedir.make_node('build.config.py')
218 n.write('version = 0x%x\ntools = %r\n' % (Context.HEXVERSION, self.tools))
220 if not self.all_envs:
221 self.fatal('nothing to store in the configuration context!')
223 for key in self.all_envs:
224 tmpenv = self.all_envs[key]
225 tmpenv.store(os.path.join(self.cachedir.abspath(), key + Build.CACHE_SUFFIX))
227 def load(self, tool_list, tooldir=None, funs=None, with_sys_path=True, cache=False):
229 Load Waf tools, which will be imported whenever a build is started.
231 :param tool_list: waf tools to import
232 :type tool_list: list of string
233 :param tooldir: paths for the imports
234 :type tooldir: list of string
235 :param funs: functions to execute from the waf tools
236 :type funs: list of string
237 :param cache: whether to prevent the tool from running twice
238 :type cache: bool
241 tools = Utils.to_list(tool_list)
242 if tooldir:
243 tooldir = Utils.to_list(tooldir)
244 for tool in tools:
245 # avoid loading the same tool more than once with the same functions
246 # used by composite projects
248 if cache:
249 mag = (tool, id(self.env), tooldir, funs)
250 if mag in self.tool_cache:
251 self.to_log('(tool %s is already loaded, skipping)' % tool)
252 continue
253 self.tool_cache.append(mag)
255 module = None
256 try:
257 module = Context.load_tool(tool, tooldir, ctx=self, with_sys_path=with_sys_path)
258 except ImportError as e:
259 self.fatal('Could not load the Waf tool %r from %r\n%s' % (tool, getattr(e, 'waf_sys_path', sys.path), e))
260 except Exception as e:
261 self.to_log('imp %r (%r & %r)' % (tool, tooldir, funs))
262 self.to_log(traceback.format_exc())
263 raise
265 if funs is not None:
266 self.eval_rules(funs)
267 else:
268 func = getattr(module, 'configure', None)
269 if func:
270 if type(func) is type(Utils.readf):
271 func(self)
272 else:
273 self.eval_rules(func)
275 self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs})
277 def post_recurse(self, node):
279 Records the path and a hash of the scripts visited, see :py:meth:`waflib.Context.Context.post_recurse`
281 :param node: script
282 :type node: :py:class:`waflib.Node.Node`
284 super(ConfigurationContext, self).post_recurse(node)
285 self.hash = Utils.h_list((self.hash, node.read('rb')))
286 self.files.append(node.abspath())
288 def eval_rules(self, rules):
290 Execute configuration tests provided as list of functions to run
292 :param rules: list of configuration method names
293 :type rules: list of string
295 self.rules = Utils.to_list(rules)
296 for x in self.rules:
297 f = getattr(self, x)
298 if not f:
299 self.fatal('No such configuration function %r' % x)
302 def conf(f):
304 Decorator: attach new configuration functions to :py:class:`waflib.Build.BuildContext` and
305 :py:class:`waflib.Configure.ConfigurationContext`. The methods bound will accept a parameter
306 named 'mandatory' to disable the configuration errors::
308 def configure(conf):
309 conf.find_program('abc', mandatory=False)
311 :param f: method to bind
312 :type f: function
314 def fun(*k, **kw):
315 mandatory = kw.pop('mandatory', True)
316 try:
317 return f(*k, **kw)
318 except Errors.ConfigurationError:
319 if mandatory:
320 raise
322 fun.__name__ = f.__name__
323 setattr(ConfigurationContext, f.__name__, fun)
324 setattr(Build.BuildContext, f.__name__, fun)
325 return f
327 @conf
328 def add_os_flags(self, var, dest=None, dup=False):
330 Import operating system environment values into ``conf.env`` dict::
332 def configure(conf):
333 conf.add_os_flags('CFLAGS')
335 :param var: variable to use
336 :type var: string
337 :param dest: destination variable, by default the same as var
338 :type dest: string
339 :param dup: add the same set of flags again
340 :type dup: bool
342 try:
343 flags = shlex.split(self.environ[var])
344 except KeyError:
345 return
346 if dup or ''.join(flags) not in ''.join(Utils.to_list(self.env[dest or var])):
347 self.env.append_value(dest or var, flags)
349 @conf
350 def cmd_to_list(self, cmd):
352 Detect if a command is written in pseudo shell like ``ccache g++`` and return a list.
354 :param cmd: command
355 :type cmd: a string or a list of string
357 if isinstance(cmd, str):
358 if os.path.isfile(cmd):
359 # do not take any risk
360 return [cmd]
361 if os.sep == '/':
362 return shlex.split(cmd)
363 else:
364 try:
365 return shlex.split(cmd, posix=False)
366 except TypeError:
367 # Python 2.5 on windows?
368 return shlex.split(cmd)
369 return cmd
371 @conf
372 def check_waf_version(self, mini='1.9.99', maxi='2.1.0', **kw):
374 Raise a Configuration error if the Waf version does not strictly match the given bounds::
376 conf.check_waf_version(mini='1.9.99', maxi='2.1.0')
378 :type mini: number, tuple or string
379 :param mini: Minimum required version
380 :type maxi: number, tuple or string
381 :param maxi: Maximum allowed version
383 self.start_msg('Checking for waf version in %s-%s' % (str(mini), str(maxi)), **kw)
384 ver = Context.HEXVERSION
385 if Utils.num2ver(mini) > ver:
386 self.fatal('waf version should be at least %r (%r found)' % (Utils.num2ver(mini), ver))
387 if Utils.num2ver(maxi) < ver:
388 self.fatal('waf version should be at most %r (%r found)' % (Utils.num2ver(maxi), ver))
389 self.end_msg('ok', **kw)
391 @conf
392 def find_file(self, filename, path_list=[]):
394 Find a file in a list of paths
396 :param filename: name of the file to search for
397 :param path_list: list of directories to search
398 :return: the first matching filename; else a configuration exception is raised
400 for n in Utils.to_list(filename):
401 for d in Utils.to_list(path_list):
402 p = os.path.expanduser(os.path.join(d, n))
403 if os.path.exists(p):
404 return p
405 self.fatal('Could not find %r' % filename)
407 @conf
408 def find_program(self, filename, **kw):
410 Search for a program on the operating system
412 When var is used, you may set os.environ[var] to help find a specific program version, for example::
414 $ CC='ccache gcc' waf configure
416 :param path_list: paths to use for searching
417 :type param_list: list of string
418 :param var: store the result to conf.env[var] where var defaults to filename.upper() if not provided; the result is stored as a list of strings
419 :type var: string
420 :param value: obtain the program from the value passed exclusively
421 :type value: list or string (list is preferred)
422 :param exts: list of extensions for the binary (do not add an extension for portability)
423 :type exts: list of string
424 :param msg: name to display in the log, by default filename is used
425 :type msg: string
426 :param interpreter: interpreter for the program
427 :type interpreter: ConfigSet variable key
428 :raises: :py:class:`waflib.Errors.ConfigurationError`
431 exts = kw.get('exts', Utils.is_win32 and '.exe,.com,.bat,.cmd' or ',.sh,.pl,.py')
433 environ = kw.get('environ', getattr(self, 'environ', os.environ))
435 ret = ''
437 filename = Utils.to_list(filename)
438 msg = kw.get('msg', ', '.join(filename))
440 var = kw.get('var', '')
441 if not var:
442 var = re.sub(r'\W', '_', filename[0].upper())
444 path_list = kw.get('path_list', '')
445 if path_list:
446 path_list = Utils.to_list(path_list)
447 else:
448 path_list = environ.get('PATH', '').split(os.pathsep)
450 if kw.get('value'):
451 # user-provided in command-line options and passed to find_program
452 ret = self.cmd_to_list(kw['value'])
453 elif environ.get(var):
454 # user-provided in the os environment
455 ret = self.cmd_to_list(environ[var])
456 elif self.env[var]:
457 # a default option in the wscript file
458 ret = self.cmd_to_list(self.env[var])
459 else:
460 if not ret:
461 ret = self.find_binary(filename, exts.split(','), path_list)
462 if not ret and Utils.winreg:
463 ret = Utils.get_registry_app_path(Utils.winreg.HKEY_CURRENT_USER, filename)
464 if not ret and Utils.winreg:
465 ret = Utils.get_registry_app_path(Utils.winreg.HKEY_LOCAL_MACHINE, filename)
466 ret = self.cmd_to_list(ret)
468 if ret:
469 if len(ret) == 1:
470 retmsg = ret[0]
471 else:
472 retmsg = ret
473 else:
474 retmsg = False
476 self.msg('Checking for program %r' % msg, retmsg, **kw)
477 if not kw.get('quiet'):
478 self.to_log('find program=%r paths=%r var=%r -> %r' % (filename, path_list, var, ret))
480 if not ret:
481 self.fatal(kw.get('errmsg', '') or 'Could not find the program %r' % filename)
483 interpreter = kw.get('interpreter')
484 if interpreter is None:
485 if not Utils.check_exe(ret[0], env=environ):
486 self.fatal('Program %r is not executable' % ret)
487 self.env[var] = ret
488 else:
489 self.env[var] = self.env[interpreter] + ret
491 return ret
493 @conf
494 def find_binary(self, filenames, exts, paths):
495 for f in filenames:
496 for ext in exts:
497 exe_name = f + ext
498 if os.path.isabs(exe_name):
499 if os.path.isfile(exe_name):
500 return exe_name
501 else:
502 for path in paths:
503 x = os.path.expanduser(os.path.join(path, exe_name))
504 if os.path.isfile(x):
505 return x
506 return None
508 @conf
509 def run_build(self, *k, **kw):
511 Create a temporary build context to execute a build. A temporary reference to that build
512 context is kept on self.test_bld for debugging purposes.
513 The arguments to this function are passed to a single task generator for that build.
514 Only three parameters are mandatory:
516 :param features: features to pass to a task generator created in the build
517 :type features: list of string
518 :param compile_filename: file to create for the compilation (default: *test.c*)
519 :type compile_filename: string
520 :param code: input file contents
521 :type code: string
523 Though this function returns *0* by default, the build may bind attribute named *retval* on the
524 build context object to return a particular value. See :py:func:`waflib.Tools.c_config.test_exec_fun` for example.
526 The temporary builds creates a temporary folder; the name of that folder is calculated
527 by hashing input arguments to this function, with the exception of :py:class:`waflib.ConfigSet.ConfigSet`
528 objects which are used for both reading and writing values.
530 This function also features a cache which is disabled by default; that cache relies
531 on the hash value calculated as indicated above::
533 def options(opt):
534 opt.add_option('--confcache', dest='confcache', default=0,
535 action='count', help='Use a configuration cache')
537 And execute the configuration with the following command-line::
539 $ waf configure --confcache
542 buf = []
543 for key in sorted(kw.keys()):
544 v = kw[key]
545 if isinstance(v, ConfigSet.ConfigSet):
546 # values are being written to, so they are excluded from contributing to the hash
547 continue
548 elif hasattr(v, '__call__'):
549 buf.append(Utils.h_fun(v))
550 else:
551 buf.append(str(v))
552 h = Utils.h_list(buf)
553 dir = self.bldnode.abspath() + os.sep + (not Utils.is_win32 and '.' or '') + 'conf_check_' + Utils.to_hex(h)
555 cachemode = kw.get('confcache', getattr(Options.options, 'confcache', None))
557 if not cachemode and os.path.exists(dir):
558 shutil.rmtree(dir)
560 try:
561 os.makedirs(dir)
562 except OSError:
563 pass
565 try:
566 os.stat(dir)
567 except OSError:
568 self.fatal('cannot use the configuration test folder %r' % dir)
570 if cachemode == 1:
571 try:
572 proj = ConfigSet.ConfigSet(os.path.join(dir, 'cache_run_build'))
573 except EnvironmentError:
574 pass
575 else:
576 ret = proj['cache_run_build']
577 if isinstance(ret, str) and ret.startswith('Test does not build'):
578 self.fatal(ret)
579 return ret
581 bdir = os.path.join(dir, 'testbuild')
583 if not os.path.exists(bdir):
584 os.makedirs(bdir)
586 cls_name = kw.get('run_build_cls') or getattr(self, 'run_build_cls', 'build')
587 self.test_bld = bld = Context.create_context(cls_name, top_dir=dir, out_dir=bdir)
588 bld.init_dirs()
589 bld.progress_bar = 0
590 bld.targets = '*'
592 bld.logger = self.logger
593 bld.all_envs.update(self.all_envs) # not really necessary
594 bld.env = kw['env']
596 bld.kw = kw
597 bld.conf = self
598 kw['build_fun'](bld)
599 ret = -1
600 try:
601 try:
602 bld.compile()
603 except Errors.WafError:
604 ret = 'Test does not build: %s' % traceback.format_exc()
605 self.fatal(ret)
606 else:
607 ret = getattr(bld, 'retval', 0)
608 finally:
609 if cachemode:
610 # cache the results each time
611 proj = ConfigSet.ConfigSet()
612 proj['cache_run_build'] = ret
613 proj.store(os.path.join(dir, 'cache_run_build'))
614 else:
615 shutil.rmtree(dir)
616 return ret
618 @conf
619 def ret_msg(self, msg, args):
620 if isinstance(msg, str):
621 return msg
622 return msg(args)
624 @conf
625 def test(self, *k, **kw):
627 if not 'env' in kw:
628 kw['env'] = self.env.derive()
630 # validate_c for example
631 if kw.get('validate'):
632 kw['validate'](kw)
634 self.start_msg(kw['msg'], **kw)
635 ret = None
636 try:
637 ret = self.run_build(*k, **kw)
638 except self.errors.ConfigurationError:
639 self.end_msg(kw['errmsg'], 'YELLOW', **kw)
640 if Logs.verbose > 1:
641 raise
642 else:
643 self.fatal('The configuration failed')
644 else:
645 kw['success'] = ret
647 if kw.get('post_check'):
648 ret = kw['post_check'](kw)
650 if ret:
651 self.end_msg(kw['errmsg'], 'YELLOW', **kw)
652 self.fatal('The configuration failed %r' % ret)
653 else:
654 self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
655 return ret