2011-09-10 11:13:51 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# encoding: utf-8
|
2018-01-01 20:53:49 +01:00
|
|
|
# Thomas Nagy, 2005-2018 (ita)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
"""
|
|
|
|
Configuration system
|
|
|
|
|
|
|
|
A :py:class:`waflib.Configure.ConfigurationContext` instance is created when ``waf configure`` is called, it is used to:
|
|
|
|
|
|
|
|
* create data dictionaries (ConfigSet instances)
|
|
|
|
* store the list of modules to import
|
|
|
|
* hold configuration routines such as ``find_program``, etc
|
|
|
|
"""
|
|
|
|
|
2017-08-19 11:54:58 +02:00
|
|
|
import os, re, shlex, shutil, sys, time, traceback
|
2011-09-10 11:13:51 +02:00
|
|
|
from waflib import ConfigSet, Utils, Options, Logs, Context, Build, Errors
|
|
|
|
|
|
|
|
WAF_CONFIG_LOG = 'config.log'
|
|
|
|
"""Name of the configuration log file"""
|
|
|
|
|
|
|
|
autoconfig = False
|
|
|
|
"""Execute the configuration automatically"""
|
|
|
|
|
|
|
|
conf_template = '''# project %(app)s configured on %(now)s by
|
|
|
|
# waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s)
|
|
|
|
# using %(args)s
|
|
|
|
#'''
|
|
|
|
|
|
|
|
class ConfigurationContext(Context.Context):
|
|
|
|
'''configures the project'''
|
|
|
|
|
|
|
|
cmd = 'configure'
|
|
|
|
|
|
|
|
def __init__(self, **kw):
|
|
|
|
super(ConfigurationContext, self).__init__(**kw)
|
|
|
|
self.environ = dict(os.environ)
|
|
|
|
self.all_envs = {}
|
|
|
|
|
|
|
|
self.top_dir = None
|
|
|
|
self.out_dir = None
|
|
|
|
|
|
|
|
self.tools = [] # tools loaded in the configuration, and that will be loaded when building
|
|
|
|
|
|
|
|
self.hash = 0
|
|
|
|
self.files = []
|
|
|
|
|
|
|
|
self.tool_cache = []
|
|
|
|
|
|
|
|
self.setenv('')
|
|
|
|
|
|
|
|
def setenv(self, name, env=None):
|
|
|
|
"""
|
2011-10-26 21:57:58 +02:00
|
|
|
Set a new config set for conf.env. If a config set of that name already exists,
|
|
|
|
recall it without modification.
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
The name is the filename prefix to save to ``c4che/NAME_cache.py``, and it
|
|
|
|
is also used as *variants* by the build commands.
|
|
|
|
Though related to variants, whatever kind of data may be stored in the config set::
|
|
|
|
|
|
|
|
def configure(cfg):
|
|
|
|
cfg.env.ONE = 1
|
|
|
|
cfg.setenv('foo')
|
|
|
|
cfg.env.ONE = 2
|
|
|
|
|
|
|
|
def build(bld):
|
|
|
|
2 == bld.env_of_name('foo').ONE
|
|
|
|
|
|
|
|
:param name: name of the configuration set
|
|
|
|
:type name: string
|
|
|
|
:param env: ConfigSet to copy, or an empty ConfigSet is created
|
|
|
|
:type env: :py:class:`waflib.ConfigSet.ConfigSet`
|
|
|
|
"""
|
2011-10-26 21:57:58 +02:00
|
|
|
if name not in self.all_envs or env:
|
|
|
|
if not env:
|
|
|
|
env = ConfigSet.ConfigSet()
|
|
|
|
self.prepare_env(env)
|
|
|
|
else:
|
|
|
|
env = env.derive()
|
|
|
|
self.all_envs[name] = env
|
2011-09-10 11:13:51 +02:00
|
|
|
self.variant = name
|
|
|
|
|
|
|
|
def get_env(self):
|
|
|
|
"""Getter for the env property"""
|
|
|
|
return self.all_envs[self.variant]
|
|
|
|
def set_env(self, val):
|
|
|
|
"""Setter for the env property"""
|
|
|
|
self.all_envs[self.variant] = val
|
|
|
|
|
|
|
|
env = property(get_env, set_env)
|
|
|
|
|
|
|
|
def init_dirs(self):
|
|
|
|
"""
|
|
|
|
Initialize the project directory and the build directory
|
|
|
|
"""
|
|
|
|
|
|
|
|
top = self.top_dir
|
|
|
|
if not top:
|
|
|
|
top = Options.options.top
|
|
|
|
if not top:
|
|
|
|
top = getattr(Context.g_module, Context.TOP, None)
|
|
|
|
if not top:
|
|
|
|
top = self.path.abspath()
|
|
|
|
top = os.path.abspath(top)
|
|
|
|
|
|
|
|
self.srcnode = (os.path.isabs(top) and self.root or self.path).find_dir(top)
|
|
|
|
assert(self.srcnode)
|
|
|
|
|
|
|
|
out = self.out_dir
|
|
|
|
if not out:
|
|
|
|
out = Options.options.out
|
|
|
|
if not out:
|
|
|
|
out = getattr(Context.g_module, Context.OUT, None)
|
|
|
|
if not out:
|
2011-10-01 19:04:26 +02:00
|
|
|
out = Options.lockfile.replace('.lock-waf_%s_' % sys.platform, '').replace('.lock-waf', '')
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2014-09-20 13:46:11 +02:00
|
|
|
# someone can be messing with symlinks
|
|
|
|
out = os.path.realpath(out)
|
|
|
|
|
2011-09-10 11:13:51 +02:00
|
|
|
self.bldnode = (os.path.isabs(out) and self.root or self.path).make_node(out)
|
|
|
|
self.bldnode.mkdir()
|
|
|
|
|
|
|
|
if not os.path.isdir(self.bldnode.abspath()):
|
2018-11-17 15:24:07 +01:00
|
|
|
self.fatal('Could not create the build directory %s' % self.bldnode.abspath())
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
def execute(self):
|
|
|
|
"""
|
|
|
|
See :py:func:`waflib.Context.Context.execute`
|
|
|
|
"""
|
|
|
|
self.init_dirs()
|
|
|
|
|
|
|
|
self.cachedir = self.bldnode.make_node(Build.CACHE_DIR)
|
2020-09-19 10:38:05 +02:00
|
|
|
if os.path.exists(self.cachedir.abspath()):
|
|
|
|
shutil.rmtree(self.cachedir.abspath())
|
2011-09-10 11:13:51 +02:00
|
|
|
self.cachedir.mkdir()
|
|
|
|
|
|
|
|
path = os.path.join(self.bldnode.abspath(), WAF_CONFIG_LOG)
|
|
|
|
self.logger = Logs.make_logger(path, 'cfg')
|
|
|
|
|
|
|
|
app = getattr(Context.g_module, 'APPNAME', '')
|
|
|
|
if app:
|
|
|
|
ver = getattr(Context.g_module, 'VERSION', '')
|
|
|
|
if ver:
|
|
|
|
app = "%s (%s)" % (app, ver)
|
|
|
|
|
2015-10-11 11:32:27 +02:00
|
|
|
params = {'now': time.ctime(), 'pyver': sys.hexversion, 'systype': sys.platform, 'args': " ".join(sys.argv), 'wafver': Context.WAFVERSION, 'abi': Context.ABI, 'app': app}
|
|
|
|
self.to_log(conf_template % params)
|
2011-09-10 11:13:51 +02:00
|
|
|
self.msg('Setting top to', self.srcnode.abspath())
|
|
|
|
self.msg('Setting out to', self.bldnode.abspath())
|
|
|
|
|
|
|
|
if id(self.srcnode) == id(self.bldnode):
|
2016-06-25 22:48:47 +02:00
|
|
|
Logs.warn('Setting top == out')
|
2011-09-10 11:13:51 +02:00
|
|
|
elif id(self.path) != id(self.srcnode):
|
|
|
|
if self.srcnode.is_child_of(self.path):
|
|
|
|
Logs.warn('Are you certain that you do not want to set top="." ?')
|
|
|
|
|
|
|
|
super(ConfigurationContext, self).execute()
|
|
|
|
|
|
|
|
self.store()
|
|
|
|
|
|
|
|
Context.top_dir = self.srcnode.abspath()
|
|
|
|
Context.out_dir = self.bldnode.abspath()
|
|
|
|
|
|
|
|
# this will write a configure lock so that subsequent builds will
|
|
|
|
# consider the current path as the root directory (see prepare_impl).
|
|
|
|
# to remove: use 'waf distclean'
|
|
|
|
env = ConfigSet.ConfigSet()
|
2016-06-25 23:54:12 +02:00
|
|
|
env.argv = sys.argv
|
|
|
|
env.options = Options.options.__dict__
|
|
|
|
env.config_cmd = self.cmd
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
env.run_dir = Context.run_dir
|
|
|
|
env.top_dir = Context.top_dir
|
|
|
|
env.out_dir = Context.out_dir
|
|
|
|
|
|
|
|
# conf.hash & conf.files hold wscript files paths and hash
|
|
|
|
# (used only by Configure.autoconfig)
|
2016-06-25 23:54:12 +02:00
|
|
|
env.hash = self.hash
|
|
|
|
env.files = self.files
|
|
|
|
env.environ = dict(self.environ)
|
2018-11-06 18:16:18 +01:00
|
|
|
env.launch_dir = Context.launch_dir
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2017-04-14 13:04:51 +02:00
|
|
|
if not (self.env.NO_LOCK_IN_RUN or env.environ.get('NO_LOCK_IN_RUN') or getattr(Options.options, 'no_lock_in_run')):
|
2014-08-30 13:05:46 +02:00
|
|
|
env.store(os.path.join(Context.run_dir, Options.lockfile))
|
2017-04-14 13:04:51 +02:00
|
|
|
if not (self.env.NO_LOCK_IN_TOP or env.environ.get('NO_LOCK_IN_TOP') or getattr(Options.options, 'no_lock_in_top')):
|
2014-08-30 13:05:46 +02:00
|
|
|
env.store(os.path.join(Context.top_dir, Options.lockfile))
|
2017-04-14 13:04:51 +02:00
|
|
|
if not (self.env.NO_LOCK_IN_OUT or env.environ.get('NO_LOCK_IN_OUT') or getattr(Options.options, 'no_lock_in_out')):
|
2014-08-30 13:05:46 +02:00
|
|
|
env.store(os.path.join(Context.out_dir, Options.lockfile))
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
def prepare_env(self, env):
|
|
|
|
"""
|
|
|
|
Insert *PREFIX*, *BINDIR* and *LIBDIR* values into ``env``
|
|
|
|
|
|
|
|
:type env: :py:class:`waflib.ConfigSet.ConfigSet`
|
|
|
|
:param env: a ConfigSet, usually ``conf.env``
|
|
|
|
"""
|
|
|
|
if not env.PREFIX:
|
2012-09-29 17:32:46 +02:00
|
|
|
if Options.options.prefix or Utils.is_win32:
|
2017-09-10 19:16:02 +02:00
|
|
|
env.PREFIX = Options.options.prefix
|
2012-09-29 17:32:46 +02:00
|
|
|
else:
|
2017-09-10 19:16:02 +02:00
|
|
|
env.PREFIX = '/'
|
2011-09-10 11:13:51 +02:00
|
|
|
if not env.BINDIR:
|
2015-02-23 17:03:48 +01:00
|
|
|
if Options.options.bindir:
|
2017-09-10 19:16:02 +02:00
|
|
|
env.BINDIR = Options.options.bindir
|
2015-02-19 13:46:18 +01:00
|
|
|
else:
|
|
|
|
env.BINDIR = Utils.subst_vars('${PREFIX}/bin', env)
|
2011-09-10 11:13:51 +02:00
|
|
|
if not env.LIBDIR:
|
2015-02-23 17:03:48 +01:00
|
|
|
if Options.options.libdir:
|
2017-09-10 19:16:02 +02:00
|
|
|
env.LIBDIR = Options.options.libdir
|
2015-02-19 13:46:18 +01:00
|
|
|
else:
|
|
|
|
env.LIBDIR = Utils.subst_vars('${PREFIX}/lib%s' % Utils.lib64(), env)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
def store(self):
|
|
|
|
"""Save the config results into the cache file"""
|
|
|
|
n = self.cachedir.make_node('build.config.py')
|
|
|
|
n.write('version = 0x%x\ntools = %r\n' % (Context.HEXVERSION, self.tools))
|
|
|
|
|
|
|
|
if not self.all_envs:
|
|
|
|
self.fatal('nothing to store in the configuration context!')
|
|
|
|
|
|
|
|
for key in self.all_envs:
|
|
|
|
tmpenv = self.all_envs[key]
|
|
|
|
tmpenv.store(os.path.join(self.cachedir.abspath(), key + Build.CACHE_SUFFIX))
|
|
|
|
|
2021-05-09 10:29:11 +02:00
|
|
|
def load(self, tool_list, **kw):
|
2011-09-10 11:13:51 +02:00
|
|
|
"""
|
|
|
|
Load Waf tools, which will be imported whenever a build is started.
|
|
|
|
|
2017-10-20 20:07:29 +02:00
|
|
|
:param tool_list: waf tools to import
|
|
|
|
:type tool_list: list of string
|
2011-09-10 11:13:51 +02:00
|
|
|
:param tooldir: paths for the imports
|
|
|
|
:type tooldir: list of string
|
|
|
|
:param funs: functions to execute from the waf tools
|
|
|
|
:type funs: list of string
|
2021-05-09 10:29:11 +02:00
|
|
|
:param cache: whether to prevent the tool from running twice (false by default)
|
2016-03-07 23:38:14 +01:00
|
|
|
:type cache: bool
|
2011-09-10 11:13:51 +02:00
|
|
|
"""
|
|
|
|
|
2017-10-20 20:07:29 +02:00
|
|
|
tools = Utils.to_list(tool_list)
|
2021-05-13 17:02:31 +02:00
|
|
|
tooldir = Utils.to_list(kw.get('tooldir', ''))
|
|
|
|
with_sys_path = kw.get('with_sys_path', True)
|
|
|
|
funs = kw.get('funs')
|
2011-09-10 11:13:51 +02:00
|
|
|
for tool in tools:
|
|
|
|
# avoid loading the same tool more than once with the same functions
|
|
|
|
# used by composite projects
|
|
|
|
|
2021-05-13 17:02:31 +02:00
|
|
|
if kw.get('cache'):
|
|
|
|
mag = (tool, id(self.env), tuple(tooldir), funs)
|
2016-03-07 23:38:14 +01:00
|
|
|
if mag in self.tool_cache:
|
|
|
|
self.to_log('(tool %s is already loaded, skipping)' % tool)
|
|
|
|
continue
|
|
|
|
self.tool_cache.append(mag)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
module = None
|
|
|
|
try:
|
2015-08-28 22:43:38 +02:00
|
|
|
module = Context.load_tool(tool, tooldir, ctx=self, with_sys_path=with_sys_path)
|
2017-11-09 07:15:44 +01:00
|
|
|
except ImportError as e:
|
2017-11-09 07:55:44 +01:00
|
|
|
self.fatal('Could not load the Waf tool %r from %r\n%s' % (tool, getattr(e, 'waf_sys_path', sys.path), e))
|
2011-09-10 11:13:51 +02:00
|
|
|
except Exception as e:
|
|
|
|
self.to_log('imp %r (%r & %r)' % (tool, tooldir, funs))
|
2017-08-19 11:54:58 +02:00
|
|
|
self.to_log(traceback.format_exc())
|
2011-09-10 11:13:51 +02:00
|
|
|
raise
|
|
|
|
|
|
|
|
if funs is not None:
|
|
|
|
self.eval_rules(funs)
|
|
|
|
else:
|
|
|
|
func = getattr(module, 'configure', None)
|
|
|
|
if func:
|
2017-04-17 12:26:47 +02:00
|
|
|
if type(func) is type(Utils.readf):
|
|
|
|
func(self)
|
|
|
|
else:
|
|
|
|
self.eval_rules(func)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs})
|
|
|
|
|
|
|
|
def post_recurse(self, node):
|
|
|
|
"""
|
|
|
|
Records the path and a hash of the scripts visited, see :py:meth:`waflib.Context.Context.post_recurse`
|
|
|
|
|
|
|
|
:param node: script
|
|
|
|
:type node: :py:class:`waflib.Node.Node`
|
|
|
|
"""
|
|
|
|
super(ConfigurationContext, self).post_recurse(node)
|
2013-11-28 20:00:50 +01:00
|
|
|
self.hash = Utils.h_list((self.hash, node.read('rb')))
|
2011-09-10 11:13:51 +02:00
|
|
|
self.files.append(node.abspath())
|
|
|
|
|
|
|
|
def eval_rules(self, rules):
|
|
|
|
"""
|
2018-07-29 01:46:58 +02:00
|
|
|
Execute configuration tests provided as list of functions to run
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
:param rules: list of configuration method names
|
|
|
|
:type rules: list of string
|
|
|
|
"""
|
|
|
|
self.rules = Utils.to_list(rules)
|
|
|
|
for x in self.rules:
|
|
|
|
f = getattr(self, x)
|
2016-03-18 19:54:31 +01:00
|
|
|
if not f:
|
2016-06-05 00:23:57 +02:00
|
|
|
self.fatal('No such configuration function %r' % x)
|
2016-03-18 19:54:31 +01:00
|
|
|
f()
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
def conf(f):
|
|
|
|
"""
|
|
|
|
Decorator: attach new configuration functions to :py:class:`waflib.Build.BuildContext` and
|
|
|
|
:py:class:`waflib.Configure.ConfigurationContext`. The methods bound will accept a parameter
|
|
|
|
named 'mandatory' to disable the configuration errors::
|
|
|
|
|
|
|
|
def configure(conf):
|
|
|
|
conf.find_program('abc', mandatory=False)
|
|
|
|
|
|
|
|
:param f: method to bind
|
|
|
|
:type f: function
|
|
|
|
"""
|
|
|
|
def fun(*k, **kw):
|
2018-05-19 11:30:05 +02:00
|
|
|
mandatory = kw.pop('mandatory', True)
|
2011-09-10 11:13:51 +02:00
|
|
|
try:
|
|
|
|
return f(*k, **kw)
|
2012-04-26 23:47:04 +02:00
|
|
|
except Errors.ConfigurationError:
|
2011-09-10 11:13:51 +02:00
|
|
|
if mandatory:
|
2012-04-26 23:47:04 +02:00
|
|
|
raise
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2015-11-05 02:01:32 +01:00
|
|
|
fun.__name__ = f.__name__
|
2011-09-10 11:13:51 +02:00
|
|
|
setattr(ConfigurationContext, f.__name__, fun)
|
|
|
|
setattr(Build.BuildContext, f.__name__, fun)
|
|
|
|
return f
|
|
|
|
|
|
|
|
@conf
|
2015-12-24 22:52:19 +01:00
|
|
|
def add_os_flags(self, var, dest=None, dup=False):
|
2011-09-10 11:13:51 +02:00
|
|
|
"""
|
|
|
|
Import operating system environment values into ``conf.env`` dict::
|
|
|
|
|
|
|
|
def configure(conf):
|
|
|
|
conf.add_os_flags('CFLAGS')
|
|
|
|
|
|
|
|
:param var: variable to use
|
|
|
|
:type var: string
|
|
|
|
:param dest: destination variable, by default the same as var
|
|
|
|
:type dest: string
|
2015-05-16 14:26:43 +02:00
|
|
|
:param dup: add the same set of flags again
|
|
|
|
:type dup: bool
|
2011-09-10 11:13:51 +02:00
|
|
|
"""
|
2015-05-16 14:26:43 +02:00
|
|
|
try:
|
|
|
|
flags = shlex.split(self.environ[var])
|
|
|
|
except KeyError:
|
|
|
|
return
|
|
|
|
if dup or ''.join(flags) not in ''.join(Utils.to_list(self.env[dest or var])):
|
|
|
|
self.env.append_value(dest or var, flags)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
@conf
|
|
|
|
def cmd_to_list(self, cmd):
|
|
|
|
"""
|
|
|
|
Detect if a command is written in pseudo shell like ``ccache g++`` and return a list.
|
|
|
|
|
|
|
|
:param cmd: command
|
|
|
|
:type cmd: a string or a list of string
|
|
|
|
"""
|
2016-02-23 22:59:29 +01:00
|
|
|
if isinstance(cmd, str):
|
|
|
|
if os.path.isfile(cmd):
|
|
|
|
# do not take any risk
|
|
|
|
return [cmd]
|
|
|
|
if os.sep == '/':
|
2011-09-10 11:13:51 +02:00
|
|
|
return shlex.split(cmd)
|
|
|
|
else:
|
2016-02-23 22:59:29 +01:00
|
|
|
try:
|
|
|
|
return shlex.split(cmd, posix=False)
|
|
|
|
except TypeError:
|
|
|
|
# Python 2.5 on windows?
|
|
|
|
return shlex.split(cmd)
|
2011-09-10 11:13:51 +02:00
|
|
|
return cmd
|
|
|
|
|
|
|
|
@conf
|
2017-10-11 21:59:29 +02:00
|
|
|
def check_waf_version(self, mini='1.9.99', maxi='2.1.0', **kw):
|
2011-09-10 11:13:51 +02:00
|
|
|
"""
|
2012-03-30 20:25:11 +02:00
|
|
|
Raise a Configuration error if the Waf version does not strictly match the given bounds::
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2017-10-11 21:59:29 +02:00
|
|
|
conf.check_waf_version(mini='1.9.99', maxi='2.1.0')
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
:type mini: number, tuple or string
|
|
|
|
:param mini: Minimum required version
|
|
|
|
:type maxi: number, tuple or string
|
|
|
|
:param maxi: Maximum allowed version
|
|
|
|
"""
|
2014-02-02 04:04:21 +01:00
|
|
|
self.start_msg('Checking for waf version in %s-%s' % (str(mini), str(maxi)), **kw)
|
2011-09-10 11:13:51 +02:00
|
|
|
ver = Context.HEXVERSION
|
|
|
|
if Utils.num2ver(mini) > ver:
|
|
|
|
self.fatal('waf version should be at least %r (%r found)' % (Utils.num2ver(mini), ver))
|
|
|
|
if Utils.num2ver(maxi) < ver:
|
|
|
|
self.fatal('waf version should be at most %r (%r found)' % (Utils.num2ver(maxi), ver))
|
2014-02-02 04:04:21 +01:00
|
|
|
self.end_msg('ok', **kw)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
@conf
|
|
|
|
def find_file(self, filename, path_list=[]):
|
|
|
|
"""
|
|
|
|
Find a file in a list of paths
|
|
|
|
|
|
|
|
:param filename: name of the file to search for
|
|
|
|
:param path_list: list of directories to search
|
2017-06-07 19:21:27 +02:00
|
|
|
:return: the first matching filename; else a configuration exception is raised
|
2011-09-10 11:13:51 +02:00
|
|
|
"""
|
|
|
|
for n in Utils.to_list(filename):
|
|
|
|
for d in Utils.to_list(path_list):
|
2015-11-07 22:01:37 +01:00
|
|
|
p = os.path.expanduser(os.path.join(d, n))
|
2011-09-10 11:13:51 +02:00
|
|
|
if os.path.exists(p):
|
|
|
|
return p
|
|
|
|
self.fatal('Could not find %r' % filename)
|
|
|
|
|
|
|
|
@conf
|
|
|
|
def find_program(self, filename, **kw):
|
|
|
|
"""
|
|
|
|
Search for a program on the operating system
|
|
|
|
|
|
|
|
When var is used, you may set os.environ[var] to help find a specific program version, for example::
|
|
|
|
|
2014-04-07 22:10:56 +02:00
|
|
|
$ CC='ccache gcc' waf configure
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
:param path_list: paths to use for searching
|
|
|
|
:type param_list: list of string
|
2017-01-06 23:14:51 +01:00
|
|
|
: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
|
2011-09-10 11:13:51 +02:00
|
|
|
:type var: string
|
2016-02-23 22:59:29 +01:00
|
|
|
:param value: obtain the program from the value passed exclusively
|
|
|
|
:type value: list or string (list is preferred)
|
2018-04-02 19:53:44 +02:00
|
|
|
:param exts: list of extensions for the binary (do not add an extension for portability)
|
|
|
|
:type exts: list of string
|
Tools/python: Extending python tool
add: show more command-line arguments to control which
python is used and where py/pyext files should be installed.
The new options are in addition to the existing PYTHON, PYTHONDIR,
and PYTHONARCHDIR environment variable, providing the help messages
to the user, so it can easily change parameters without second-guessing
of what are they.
Environmental parameters override command line arguments (is it right?)
updated: An extension to Configure.find_command to allow specification
of the name for the searched command (using `msg` option). This is
helpful when the search command is a huge list, as in the case of
python's python-config case.
updated: Do not specify prefix for `get_python_lib`, since this way it
can result in a wrong PYTHONDIR and PYTHONARCHDIR on certain platforms
(e.g., OSX)
updated: Changing logic with byte-compilation of python files. Mow, files are
compiled during the build stage, not install stage. During the
install stage files are simply copied to the requested
location.
updated: If there is an error in python source code, byte-compilation
phase will raise an exception and abort building process.
Signed-off-by: Thomas Nagy <tnagy2pow10@gmail.com>
2013-07-12 08:15:35 +02:00
|
|
|
:param msg: name to display in the log, by default filename is used
|
|
|
|
:type msg: string
|
2013-09-04 06:44:56 +02:00
|
|
|
:param interpreter: interpreter for the program
|
|
|
|
:type interpreter: ConfigSet variable key
|
2017-06-20 11:20:53 +02:00
|
|
|
:raises: :py:class:`waflib.Errors.ConfigurationError`
|
2011-09-10 11:13:51 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
exts = kw.get('exts', Utils.is_win32 and '.exe,.com,.bat,.cmd' or ',.sh,.pl,.py')
|
|
|
|
|
2014-02-02 04:04:21 +01:00
|
|
|
environ = kw.get('environ', getattr(self, 'environ', os.environ))
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
ret = ''
|
2014-04-07 22:10:56 +02:00
|
|
|
|
2011-09-10 11:13:51 +02:00
|
|
|
filename = Utils.to_list(filename)
|
2014-01-25 20:28:30 +01:00
|
|
|
msg = kw.get('msg', ', '.join(filename))
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
var = kw.get('var', '')
|
|
|
|
if not var:
|
2022-11-20 06:55:13 +01:00
|
|
|
var = re.sub(r'\W', '_', filename[0].upper())
|
2014-01-25 20:28:30 +01:00
|
|
|
|
2011-09-10 11:13:51 +02:00
|
|
|
path_list = kw.get('path_list', '')
|
2014-04-07 22:10:56 +02:00
|
|
|
if path_list:
|
|
|
|
path_list = Utils.to_list(path_list)
|
|
|
|
else:
|
|
|
|
path_list = environ.get('PATH', '').split(os.pathsep)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2016-04-19 22:00:21 +02:00
|
|
|
if kw.get('value'):
|
2016-02-23 22:59:29 +01:00
|
|
|
# user-provided in command-line options and passed to find_program
|
|
|
|
ret = self.cmd_to_list(kw['value'])
|
2017-04-10 20:39:45 +02:00
|
|
|
elif environ.get(var):
|
2016-02-23 22:59:29 +01:00
|
|
|
# user-provided in the os environment
|
|
|
|
ret = self.cmd_to_list(environ[var])
|
2014-04-07 22:10:56 +02:00
|
|
|
elif self.env[var]:
|
2016-02-23 22:59:29 +01:00
|
|
|
# a default option in the wscript file
|
|
|
|
ret = self.cmd_to_list(self.env[var])
|
2014-04-07 22:10:56 +02:00
|
|
|
else:
|
|
|
|
if not ret:
|
|
|
|
ret = self.find_binary(filename, exts.split(','), path_list)
|
|
|
|
if not ret and Utils.winreg:
|
|
|
|
ret = Utils.get_registry_app_path(Utils.winreg.HKEY_CURRENT_USER, filename)
|
|
|
|
if not ret and Utils.winreg:
|
|
|
|
ret = Utils.get_registry_app_path(Utils.winreg.HKEY_LOCAL_MACHINE, filename)
|
|
|
|
ret = self.cmd_to_list(ret)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2014-06-08 17:34:35 +02:00
|
|
|
if ret:
|
|
|
|
if len(ret) == 1:
|
|
|
|
retmsg = ret[0]
|
|
|
|
else:
|
|
|
|
retmsg = ret
|
|
|
|
else:
|
|
|
|
retmsg = False
|
|
|
|
|
2016-06-05 00:23:57 +02:00
|
|
|
self.msg('Checking for program %r' % msg, retmsg, **kw)
|
2016-04-19 22:00:21 +02:00
|
|
|
if not kw.get('quiet'):
|
2014-02-02 04:04:21 +01:00
|
|
|
self.to_log('find program=%r paths=%r var=%r -> %r' % (filename, path_list, var, ret))
|
2011-09-10 11:13:51 +02:00
|
|
|
|
|
|
|
if not ret:
|
2014-06-08 17:34:35 +02:00
|
|
|
self.fatal(kw.get('errmsg', '') or 'Could not find the program %r' % filename)
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2016-04-19 22:00:21 +02:00
|
|
|
interpreter = kw.get('interpreter')
|
2013-09-04 06:44:56 +02:00
|
|
|
if interpreter is None:
|
2014-04-07 22:10:56 +02:00
|
|
|
if not Utils.check_exe(ret[0], env=environ):
|
2014-06-08 17:34:35 +02:00
|
|
|
self.fatal('Program %r is not executable' % ret)
|
2014-01-25 20:59:14 +01:00
|
|
|
self.env[var] = ret
|
2013-09-04 06:44:56 +02:00
|
|
|
else:
|
2014-01-25 20:59:14 +01:00
|
|
|
self.env[var] = self.env[interpreter] + ret
|
2013-09-04 06:44:56 +02:00
|
|
|
|
|
|
|
return ret
|
2011-09-10 11:13:51 +02:00
|
|
|
|
2014-02-01 14:13:16 +01:00
|
|
|
@conf
|
|
|
|
def find_binary(self, filenames, exts, paths):
|
|
|
|
for f in filenames:
|
|
|
|
for ext in exts:
|
|
|
|
exe_name = f + ext
|
|
|
|
if os.path.isabs(exe_name):
|
2024-01-12 14:07:30 +01:00
|
|
|
if os.path.isfile(exe_name) and os.access(exe_name, os.X_OK):
|
2014-02-01 14:13:16 +01:00
|
|
|
return exe_name
|
|
|
|
else:
|
|
|
|
for path in paths:
|
|
|
|
x = os.path.expanduser(os.path.join(path, exe_name))
|
2022-12-21 20:19:00 +01:00
|
|
|
if os.path.isfile(x) and os.access(x, os.X_OK):
|
2014-02-01 14:13:16 +01:00
|
|
|
return x
|
|
|
|
return None
|
|
|
|
|
2014-09-14 10:58:29 +02:00
|
|
|
@conf
|
|
|
|
def run_build(self, *k, **kw):
|
|
|
|
"""
|
2019-12-20 07:55:36 +01:00
|
|
|
Create a temporary build context to execute a build. A temporary reference to that build
|
|
|
|
context is kept on self.test_bld for debugging purposes.
|
|
|
|
The arguments to this function are passed to a single task generator for that build.
|
|
|
|
Only three parameters are mandatory:
|
2014-09-14 10:58:29 +02:00
|
|
|
|
|
|
|
:param features: features to pass to a task generator created in the build
|
|
|
|
:type features: list of string
|
|
|
|
:param compile_filename: file to create for the compilation (default: *test.c*)
|
|
|
|
:type compile_filename: string
|
2019-12-20 07:55:36 +01:00
|
|
|
:param code: input file contents
|
2014-09-14 10:58:29 +02:00
|
|
|
:type code: string
|
|
|
|
|
2019-12-20 07:55:36 +01:00
|
|
|
Though this function returns *0* by default, the build may bind attribute named *retval* on the
|
2014-09-14 10:58:29 +02:00
|
|
|
build context object to return a particular value. See :py:func:`waflib.Tools.c_config.test_exec_fun` for example.
|
|
|
|
|
2019-12-20 07:55:36 +01:00
|
|
|
The temporary builds creates a temporary folder; the name of that folder is calculated
|
|
|
|
by hashing input arguments to this function, with the exception of :py:class:`waflib.ConfigSet.ConfigSet`
|
|
|
|
objects which are used for both reading and writing values.
|
|
|
|
|
|
|
|
This function also features a cache which is disabled by default; that cache relies
|
|
|
|
on the hash value calculated as indicated above::
|
2014-09-14 10:58:29 +02:00
|
|
|
|
|
|
|
def options(opt):
|
|
|
|
opt.add_option('--confcache', dest='confcache', default=0,
|
|
|
|
action='count', help='Use a configuration cache')
|
|
|
|
|
|
|
|
And execute the configuration with the following command-line::
|
|
|
|
|
|
|
|
$ waf configure --confcache
|
|
|
|
|
|
|
|
"""
|
2019-06-25 11:51:44 +02:00
|
|
|
buf = []
|
|
|
|
for key in sorted(kw.keys()):
|
2019-07-08 19:23:50 +02:00
|
|
|
v = kw[key]
|
2019-12-20 07:55:36 +01:00
|
|
|
if isinstance(v, ConfigSet.ConfigSet):
|
|
|
|
# values are being written to, so they are excluded from contributing to the hash
|
|
|
|
continue
|
|
|
|
elif hasattr(v, '__call__'):
|
2019-07-08 19:23:50 +02:00
|
|
|
buf.append(Utils.h_fun(v))
|
|
|
|
else:
|
|
|
|
buf.append(str(v))
|
2019-06-25 11:51:44 +02:00
|
|
|
h = Utils.h_list(buf)
|
2014-09-14 10:58:29 +02:00
|
|
|
dir = self.bldnode.abspath() + os.sep + (not Utils.is_win32 and '.' or '') + 'conf_check_' + Utils.to_hex(h)
|
|
|
|
|
2019-06-25 11:51:44 +02:00
|
|
|
cachemode = kw.get('confcache', getattr(Options.options, 'confcache', None))
|
|
|
|
|
|
|
|
if not cachemode and os.path.exists(dir):
|
|
|
|
shutil.rmtree(dir)
|
|
|
|
|
2014-09-14 10:58:29 +02:00
|
|
|
try:
|
|
|
|
os.makedirs(dir)
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
os.stat(dir)
|
|
|
|
except OSError:
|
|
|
|
self.fatal('cannot use the configuration test folder %r' % dir)
|
|
|
|
|
|
|
|
if cachemode == 1:
|
|
|
|
try:
|
|
|
|
proj = ConfigSet.ConfigSet(os.path.join(dir, 'cache_run_build'))
|
2016-06-04 09:33:13 +02:00
|
|
|
except EnvironmentError:
|
2014-09-14 10:58:29 +02:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
ret = proj['cache_run_build']
|
|
|
|
if isinstance(ret, str) and ret.startswith('Test does not build'):
|
|
|
|
self.fatal(ret)
|
|
|
|
return ret
|
|
|
|
|
|
|
|
bdir = os.path.join(dir, 'testbuild')
|
|
|
|
|
|
|
|
if not os.path.exists(bdir):
|
|
|
|
os.makedirs(bdir)
|
|
|
|
|
2017-03-04 08:22:12 +01:00
|
|
|
cls_name = kw.get('run_build_cls') or getattr(self, 'run_build_cls', 'build')
|
2016-09-10 12:12:46 +02:00
|
|
|
self.test_bld = bld = Context.create_context(cls_name, top_dir=dir, out_dir=bdir)
|
2014-09-14 10:58:29 +02:00
|
|
|
bld.init_dirs()
|
|
|
|
bld.progress_bar = 0
|
|
|
|
bld.targets = '*'
|
|
|
|
|
|
|
|
bld.logger = self.logger
|
|
|
|
bld.all_envs.update(self.all_envs) # not really necessary
|
|
|
|
bld.env = kw['env']
|
|
|
|
|
|
|
|
bld.kw = kw
|
|
|
|
bld.conf = self
|
|
|
|
kw['build_fun'](bld)
|
|
|
|
ret = -1
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
bld.compile()
|
|
|
|
except Errors.WafError:
|
2017-08-19 11:54:58 +02:00
|
|
|
ret = 'Test does not build: %s' % traceback.format_exc()
|
2014-09-14 10:58:29 +02:00
|
|
|
self.fatal(ret)
|
|
|
|
else:
|
|
|
|
ret = getattr(bld, 'retval', 0)
|
|
|
|
finally:
|
2019-06-25 11:51:44 +02:00
|
|
|
if cachemode:
|
2014-09-14 10:58:29 +02:00
|
|
|
# cache the results each time
|
|
|
|
proj = ConfigSet.ConfigSet()
|
|
|
|
proj['cache_run_build'] = ret
|
|
|
|
proj.store(os.path.join(dir, 'cache_run_build'))
|
|
|
|
else:
|
|
|
|
shutil.rmtree(dir)
|
|
|
|
return ret
|
|
|
|
|
|
|
|
@conf
|
|
|
|
def ret_msg(self, msg, args):
|
|
|
|
if isinstance(msg, str):
|
|
|
|
return msg
|
|
|
|
return msg(args)
|
|
|
|
|
|
|
|
@conf
|
|
|
|
def test(self, *k, **kw):
|
|
|
|
|
|
|
|
if not 'env' in kw:
|
|
|
|
kw['env'] = self.env.derive()
|
|
|
|
|
|
|
|
# validate_c for example
|
2016-04-19 22:00:21 +02:00
|
|
|
if kw.get('validate'):
|
2014-09-14 10:58:29 +02:00
|
|
|
kw['validate'](kw)
|
|
|
|
|
|
|
|
self.start_msg(kw['msg'], **kw)
|
|
|
|
ret = None
|
|
|
|
try:
|
|
|
|
ret = self.run_build(*k, **kw)
|
|
|
|
except self.errors.ConfigurationError:
|
|
|
|
self.end_msg(kw['errmsg'], 'YELLOW', **kw)
|
|
|
|
if Logs.verbose > 1:
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
self.fatal('The configuration failed')
|
|
|
|
else:
|
|
|
|
kw['success'] = ret
|
|
|
|
|
2016-04-19 22:00:21 +02:00
|
|
|
if kw.get('post_check'):
|
2014-09-14 10:58:29 +02:00
|
|
|
ret = kw['post_check'](kw)
|
|
|
|
|
|
|
|
if ret:
|
|
|
|
self.end_msg(kw['errmsg'], 'YELLOW', **kw)
|
|
|
|
self.fatal('The configuration failed %r' % ret)
|
|
|
|
else:
|
|
|
|
self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
|
|
|
|
return ret
|
|
|
|
|