Clarification about jack_port_get_latency_range().
[jack2.git] / waflib / Configure.py
blobd0a4793a88329ed0f8f861abc497f48169f016f2
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 conf.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)
184 if not (self.env.NO_LOCK_IN_RUN or env.environ.get('NO_LOCK_IN_RUN') or getattr(Options.options, 'no_lock_in_run')):
185 env.store(os.path.join(Context.run_dir, Options.lockfile))
186 if not (self.env.NO_LOCK_IN_TOP or env.environ.get('NO_LOCK_IN_TOP') or getattr(Options.options, 'no_lock_in_top')):
187 env.store(os.path.join(Context.top_dir, Options.lockfile))
188 if not (self.env.NO_LOCK_IN_OUT or env.environ.get('NO_LOCK_IN_OUT') or getattr(Options.options, 'no_lock_in_out')):
189 env.store(os.path.join(Context.out_dir, Options.lockfile))
191 def prepare_env(self, env):
193 Insert *PREFIX*, *BINDIR* and *LIBDIR* values into ``env``
195 :type env: :py:class:`waflib.ConfigSet.ConfigSet`
196 :param env: a ConfigSet, usually ``conf.env``
198 if not env.PREFIX:
199 if Options.options.prefix or Utils.is_win32:
200 env.PREFIX = Options.options.prefix
201 else:
202 env.PREFIX = '/'
203 if not env.BINDIR:
204 if Options.options.bindir:
205 env.BINDIR = Options.options.bindir
206 else:
207 env.BINDIR = Utils.subst_vars('${PREFIX}/bin', env)
208 if not env.LIBDIR:
209 if Options.options.libdir:
210 env.LIBDIR = Options.options.libdir
211 else:
212 env.LIBDIR = Utils.subst_vars('${PREFIX}/lib%s' % Utils.lib64(), env)
214 def store(self):
215 """Save the config results into the cache file"""
216 n = self.cachedir.make_node('build.config.py')
217 n.write('version = 0x%x\ntools = %r\n' % (Context.HEXVERSION, self.tools))
219 if not self.all_envs:
220 self.fatal('nothing to store in the configuration context!')
222 for key in self.all_envs:
223 tmpenv = self.all_envs[key]
224 tmpenv.store(os.path.join(self.cachedir.abspath(), key + Build.CACHE_SUFFIX))
226 def load(self, tool_list, tooldir=None, funs=None, with_sys_path=True, cache=False):
228 Load Waf tools, which will be imported whenever a build is started.
230 :param tool_list: waf tools to import
231 :type tool_list: list of string
232 :param tooldir: paths for the imports
233 :type tooldir: list of string
234 :param funs: functions to execute from the waf tools
235 :type funs: list of string
236 :param cache: whether to prevent the tool from running twice
237 :type cache: bool
240 tools = Utils.to_list(tool_list)
241 if tooldir:
242 tooldir = Utils.to_list(tooldir)
243 for tool in tools:
244 # avoid loading the same tool more than once with the same functions
245 # used by composite projects
247 if cache:
248 mag = (tool, id(self.env), tooldir, funs)
249 if mag in self.tool_cache:
250 self.to_log('(tool %s is already loaded, skipping)' % tool)
251 continue
252 self.tool_cache.append(mag)
254 module = None
255 try:
256 module = Context.load_tool(tool, tooldir, ctx=self, with_sys_path=with_sys_path)
257 except ImportError as e:
258 self.fatal('Could not load the Waf tool %r from %r\n%s' % (tool, getattr(e, 'waf_sys_path', sys.path), e))
259 except Exception as e:
260 self.to_log('imp %r (%r & %r)' % (tool, tooldir, funs))
261 self.to_log(traceback.format_exc())
262 raise
264 if funs is not None:
265 self.eval_rules(funs)
266 else:
267 func = getattr(module, 'configure', None)
268 if func:
269 if type(func) is type(Utils.readf):
270 func(self)
271 else:
272 self.eval_rules(func)
274 self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs})
276 def post_recurse(self, node):
278 Records the path and a hash of the scripts visited, see :py:meth:`waflib.Context.Context.post_recurse`
280 :param node: script
281 :type node: :py:class:`waflib.Node.Node`
283 super(ConfigurationContext, self).post_recurse(node)
284 self.hash = Utils.h_list((self.hash, node.read('rb')))
285 self.files.append(node.abspath())
287 def eval_rules(self, rules):
289 Execute configuration tests provided as list of functions to run
291 :param rules: list of configuration method names
292 :type rules: list of string
294 self.rules = Utils.to_list(rules)
295 for x in self.rules:
296 f = getattr(self, x)
297 if not f:
298 self.fatal('No such configuration function %r' % x)
301 def conf(f):
303 Decorator: attach new configuration functions to :py:class:`waflib.Build.BuildContext` and
304 :py:class:`waflib.Configure.ConfigurationContext`. The methods bound will accept a parameter
305 named 'mandatory' to disable the configuration errors::
307 def configure(conf):
308 conf.find_program('abc', mandatory=False)
310 :param f: method to bind
311 :type f: function
313 def fun(*k, **kw):
314 mandatory = kw.pop('mandatory', True)
315 try:
316 return f(*k, **kw)
317 except Errors.ConfigurationError:
318 if mandatory:
319 raise
321 fun.__name__ = f.__name__
322 setattr(ConfigurationContext, f.__name__, fun)
323 setattr(Build.BuildContext, f.__name__, fun)
324 return f
326 @conf
327 def add_os_flags(self, var, dest=None, dup=False):
329 Import operating system environment values into ``conf.env`` dict::
331 def configure(conf):
332 conf.add_os_flags('CFLAGS')
334 :param var: variable to use
335 :type var: string
336 :param dest: destination variable, by default the same as var
337 :type dest: string
338 :param dup: add the same set of flags again
339 :type dup: bool
341 try:
342 flags = shlex.split(self.environ[var])
343 except KeyError:
344 return
345 if dup or ''.join(flags) not in ''.join(Utils.to_list(self.env[dest or var])):
346 self.env.append_value(dest or var, flags)
348 @conf
349 def cmd_to_list(self, cmd):
351 Detect if a command is written in pseudo shell like ``ccache g++`` and return a list.
353 :param cmd: command
354 :type cmd: a string or a list of string
356 if isinstance(cmd, str):
357 if os.path.isfile(cmd):
358 # do not take any risk
359 return [cmd]
360 if os.sep == '/':
361 return shlex.split(cmd)
362 else:
363 try:
364 return shlex.split(cmd, posix=False)
365 except TypeError:
366 # Python 2.5 on windows?
367 return shlex.split(cmd)
368 return cmd
370 @conf
371 def check_waf_version(self, mini='1.9.99', maxi='2.1.0', **kw):
373 Raise a Configuration error if the Waf version does not strictly match the given bounds::
375 conf.check_waf_version(mini='1.9.99', maxi='2.1.0')
377 :type mini: number, tuple or string
378 :param mini: Minimum required version
379 :type maxi: number, tuple or string
380 :param maxi: Maximum allowed version
382 self.start_msg('Checking for waf version in %s-%s' % (str(mini), str(maxi)), **kw)
383 ver = Context.HEXVERSION
384 if Utils.num2ver(mini) > ver:
385 self.fatal('waf version should be at least %r (%r found)' % (Utils.num2ver(mini), ver))
386 if Utils.num2ver(maxi) < ver:
387 self.fatal('waf version should be at most %r (%r found)' % (Utils.num2ver(maxi), ver))
388 self.end_msg('ok', **kw)
390 @conf
391 def find_file(self, filename, path_list=[]):
393 Find a file in a list of paths
395 :param filename: name of the file to search for
396 :param path_list: list of directories to search
397 :return: the first matching filename; else a configuration exception is raised
399 for n in Utils.to_list(filename):
400 for d in Utils.to_list(path_list):
401 p = os.path.expanduser(os.path.join(d, n))
402 if os.path.exists(p):
403 return p
404 self.fatal('Could not find %r' % filename)
406 @conf
407 def find_program(self, filename, **kw):
409 Search for a program on the operating system
411 When var is used, you may set os.environ[var] to help find a specific program version, for example::
413 $ CC='ccache gcc' waf configure
415 :param path_list: paths to use for searching
416 :type param_list: list of string
417 :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
418 :type var: string
419 :param value: obtain the program from the value passed exclusively
420 :type value: list or string (list is preferred)
421 :param exts: list of extensions for the binary (do not add an extension for portability)
422 :type exts: list of string
423 :param msg: name to display in the log, by default filename is used
424 :type msg: string
425 :param interpreter: interpreter for the program
426 :type interpreter: ConfigSet variable key
427 :raises: :py:class:`waflib.Errors.ConfigurationError`
430 exts = kw.get('exts', Utils.is_win32 and '.exe,.com,.bat,.cmd' or ',.sh,.pl,.py')
432 environ = kw.get('environ', getattr(self, 'environ', os.environ))
434 ret = ''
436 filename = Utils.to_list(filename)
437 msg = kw.get('msg', ', '.join(filename))
439 var = kw.get('var', '')
440 if not var:
441 var = re.sub(r'[-.]', '_', filename[0].upper())
443 path_list = kw.get('path_list', '')
444 if path_list:
445 path_list = Utils.to_list(path_list)
446 else:
447 path_list = environ.get('PATH', '').split(os.pathsep)
449 if kw.get('value'):
450 # user-provided in command-line options and passed to find_program
451 ret = self.cmd_to_list(kw['value'])
452 elif environ.get(var):
453 # user-provided in the os environment
454 ret = self.cmd_to_list(environ[var])
455 elif self.env[var]:
456 # a default option in the wscript file
457 ret = self.cmd_to_list(self.env[var])
458 else:
459 if not ret:
460 ret = self.find_binary(filename, exts.split(','), path_list)
461 if not ret and Utils.winreg:
462 ret = Utils.get_registry_app_path(Utils.winreg.HKEY_CURRENT_USER, filename)
463 if not ret and Utils.winreg:
464 ret = Utils.get_registry_app_path(Utils.winreg.HKEY_LOCAL_MACHINE, filename)
465 ret = self.cmd_to_list(ret)
467 if ret:
468 if len(ret) == 1:
469 retmsg = ret[0]
470 else:
471 retmsg = ret
472 else:
473 retmsg = False
475 self.msg('Checking for program %r' % msg, retmsg, **kw)
476 if not kw.get('quiet'):
477 self.to_log('find program=%r paths=%r var=%r -> %r' % (filename, path_list, var, ret))
479 if not ret:
480 self.fatal(kw.get('errmsg', '') or 'Could not find the program %r' % filename)
482 interpreter = kw.get('interpreter')
483 if interpreter is None:
484 if not Utils.check_exe(ret[0], env=environ):
485 self.fatal('Program %r is not executable' % ret)
486 self.env[var] = ret
487 else:
488 self.env[var] = self.env[interpreter] + ret
490 return ret
492 @conf
493 def find_binary(self, filenames, exts, paths):
494 for f in filenames:
495 for ext in exts:
496 exe_name = f + ext
497 if os.path.isabs(exe_name):
498 if os.path.isfile(exe_name):
499 return exe_name
500 else:
501 for path in paths:
502 x = os.path.expanduser(os.path.join(path, exe_name))
503 if os.path.isfile(x):
504 return x
505 return None
507 @conf
508 def run_build(self, *k, **kw):
510 Create a temporary build context to execute a build. A reference to that build
511 context is kept on self.test_bld for debugging purposes, and you should not rely
512 on it too much (read the note on the cache below).
513 The parameters given in the arguments to this function are passed as arguments for
514 a single task generator created in the build. Only three parameters are obligatory:
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: code to write in the filename to compile
521 :type code: string
523 Though this function returns *0* by default, the build may set an 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 This function also provides a limited cache. To use it, provide the following option::
528 def options(opt):
529 opt.add_option('--confcache', dest='confcache', default=0,
530 action='count', help='Use a configuration cache')
532 And execute the configuration with the following command-line::
534 $ waf configure --confcache
537 lst = [str(v) for (p, v) in kw.items() if p != 'env']
538 h = Utils.h_list(lst)
539 dir = self.bldnode.abspath() + os.sep + (not Utils.is_win32 and '.' or '') + 'conf_check_' + Utils.to_hex(h)
541 try:
542 os.makedirs(dir)
543 except OSError:
544 pass
546 try:
547 os.stat(dir)
548 except OSError:
549 self.fatal('cannot use the configuration test folder %r' % dir)
551 cachemode = getattr(Options.options, 'confcache', None)
552 if cachemode == 1:
553 try:
554 proj = ConfigSet.ConfigSet(os.path.join(dir, 'cache_run_build'))
555 except EnvironmentError:
556 pass
557 else:
558 ret = proj['cache_run_build']
559 if isinstance(ret, str) and ret.startswith('Test does not build'):
560 self.fatal(ret)
561 return ret
563 bdir = os.path.join(dir, 'testbuild')
565 if not os.path.exists(bdir):
566 os.makedirs(bdir)
568 cls_name = kw.get('run_build_cls') or getattr(self, 'run_build_cls', 'build')
569 self.test_bld = bld = Context.create_context(cls_name, top_dir=dir, out_dir=bdir)
570 bld.init_dirs()
571 bld.progress_bar = 0
572 bld.targets = '*'
574 bld.logger = self.logger
575 bld.all_envs.update(self.all_envs) # not really necessary
576 bld.env = kw['env']
578 bld.kw = kw
579 bld.conf = self
580 kw['build_fun'](bld)
581 ret = -1
582 try:
583 try:
584 bld.compile()
585 except Errors.WafError:
586 ret = 'Test does not build: %s' % traceback.format_exc()
587 self.fatal(ret)
588 else:
589 ret = getattr(bld, 'retval', 0)
590 finally:
591 if cachemode == 1:
592 # cache the results each time
593 proj = ConfigSet.ConfigSet()
594 proj['cache_run_build'] = ret
595 proj.store(os.path.join(dir, 'cache_run_build'))
596 else:
597 shutil.rmtree(dir)
598 return ret
600 @conf
601 def ret_msg(self, msg, args):
602 if isinstance(msg, str):
603 return msg
604 return msg(args)
606 @conf
607 def test(self, *k, **kw):
609 if not 'env' in kw:
610 kw['env'] = self.env.derive()
612 # validate_c for example
613 if kw.get('validate'):
614 kw['validate'](kw)
616 self.start_msg(kw['msg'], **kw)
617 ret = None
618 try:
619 ret = self.run_build(*k, **kw)
620 except self.errors.ConfigurationError:
621 self.end_msg(kw['errmsg'], 'YELLOW', **kw)
622 if Logs.verbose > 1:
623 raise
624 else:
625 self.fatal('The configuration failed')
626 else:
627 kw['success'] = ret
629 if kw.get('post_check'):
630 ret = kw['post_check'](kw)
632 if ret:
633 self.end_msg(kw['errmsg'], 'YELLOW', **kw)
634 self.fatal('The configuration failed %r' % ret)
635 else:
636 self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
637 return ret