autooptions: switch to git submodule
This commit is contained in:
parent
e8ab33123f
commit
9e3cd203f1
|
@ -0,0 +1,3 @@
|
|||
[submodule "waf-autooptions"]
|
||||
path = autooptions
|
||||
url = ../waf-autooptions
|
|
@ -0,0 +1 @@
|
|||
Subproject commit cc16822604501c96af265a2e3c51855d2aed635f
|
|
@ -1,6 +0,0 @@
|
|||
Do not modify the code in this directory. It has been copied from its upstream
|
||||
git repo [1]. When submodules are introduced this directory can be made a
|
||||
submodule, but until then a verbatim copy is better than inlining the code in
|
||||
the toplevel wscript.
|
||||
|
||||
[1] https://gitlab.com/karllinden/waf-autooptions
|
|
@ -1,395 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2017 Karl Linden
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
import optparse
|
||||
import sys
|
||||
from waflib import Configure, Logs, Options, Utils
|
||||
|
||||
# A list of AutoOptions. It is local to each module, so all modules that
|
||||
# use AutoOptions need to run both opt.load and conf.load. In contrast
|
||||
# to the define and style options this does not need to and cannot be
|
||||
# declared in the OptionsContext, because it is needed both for the
|
||||
# options and the configure phase.
|
||||
auto_options = []
|
||||
|
||||
class AutoOption:
|
||||
"""
|
||||
This class represents an auto option that can be used in conjunction
|
||||
with the waf build system. By default it adds options --foo and
|
||||
--no-foo respectively to turn on or off foo respectively.
|
||||
Furthermore it incorporats logic and checks that are required for
|
||||
these features.
|
||||
|
||||
An option can have an arbitrary number of dependencies that must be
|
||||
present for the option to be enabled. An option can be enabled or
|
||||
disabled by default. Here is how the logic works:
|
||||
1. If the option is explicitly disabled, through --no-foo, then no
|
||||
checks are made and the option is disabled.
|
||||
2. If the option is explicitly enabled, through --foo, then check
|
||||
for all required dependencies, and if some of them are not
|
||||
found, then print a fatal error telling the user there were
|
||||
dependencies missing.
|
||||
3. Otherwise, if the option is enabled by default, then check for
|
||||
all dependencies. If all dependencies are found the option is
|
||||
enabled. Otherwise it is disabled.
|
||||
4. Lastly, if no option was given and the option is disabled by
|
||||
default, then no checks are performed and the option is
|
||||
disabled.
|
||||
|
||||
To add a dependency to an option use the check, check_cfg and
|
||||
find_program methods of this class. The methods are merely small
|
||||
wrappers around their configuration context counterparts and behave
|
||||
identically. Note that adding dependencies is done in the options
|
||||
phase and not in the configure phase, although the checks are
|
||||
actually executed during the configure phase.
|
||||
|
||||
Custom check functions can be added using the add_function method.
|
||||
As with the other checks the check function will be invoked during
|
||||
the configuration. Refer to the documentation of the add_function
|
||||
method for details.
|
||||
|
||||
When all checks have been made and the class has made a decision the
|
||||
result is saved in conf.env['NAME'] where 'NAME' by default is the
|
||||
uppercase of the name argument to __init__, with hyphens replaced by
|
||||
underscores. This default can be changed with the conf_dest argument
|
||||
to __init__.
|
||||
|
||||
The class will define a preprocessor symbol with the result. The
|
||||
default name is WITH_NAME, to not collide with the standard define
|
||||
of check_cfg, but it can be changed using the define argument to
|
||||
__init__. It can also be changed globally using
|
||||
set_auto_options_define.
|
||||
"""
|
||||
|
||||
def __init__(self, opt, name, help=None, default=True,
|
||||
conf_dest=None, define=None, style=None):
|
||||
"""
|
||||
Class initializing method.
|
||||
|
||||
Arguments:
|
||||
opt OptionsContext
|
||||
name name of the option, e.g. alsa
|
||||
help help text that will be displayed in --help output
|
||||
conf_dest conf.env variable to define to the result
|
||||
define the preprocessor symbol to define with the result
|
||||
style the option style to use; see below for options
|
||||
"""
|
||||
|
||||
# The dependencies to check for. The elements are on the form
|
||||
# (func, k, kw) where func is the function or function name that
|
||||
# is used for the check and k and kw are the arguments and
|
||||
# options to give the function.
|
||||
self.deps = []
|
||||
|
||||
# Whether or not the option should be enabled. None indicates
|
||||
# that the checks have not been performed yet.
|
||||
self.enable = None
|
||||
|
||||
self.help = help
|
||||
if help:
|
||||
if default:
|
||||
help_comment = ' (enabled by default if possible)'
|
||||
else:
|
||||
help_comment = ' (disabled by default)'
|
||||
option_help = help + help_comment
|
||||
no_option_help = None
|
||||
else:
|
||||
option_help = no_option_help = optparse.SUPPRESS_HELP
|
||||
|
||||
self.dest = 'auto_option_' + name
|
||||
|
||||
self.default = default
|
||||
|
||||
safe_name = Utils.quote_define_name(name)
|
||||
self.conf_dest = conf_dest or safe_name
|
||||
|
||||
default_define = opt.get_auto_options_define()
|
||||
self.define = define or default_define % safe_name
|
||||
|
||||
if not style:
|
||||
style = opt.get_auto_options_style()
|
||||
self.style = style
|
||||
|
||||
# plain (default):
|
||||
# --foo | --no-foo
|
||||
# yesno:
|
||||
# --foo=yes | --foo=no
|
||||
# yesno_and_hack:
|
||||
# --foo[=yes] | --foo=no or --no-foo
|
||||
# enable:
|
||||
# --enable-foo | --disble-foo
|
||||
# with:
|
||||
# --with-foo | --without-foo
|
||||
if style in ['plain', 'yesno', 'yesno_and_hack']:
|
||||
self.no_option = '--no-' + name
|
||||
self.yes_option = '--' + name
|
||||
elif style == 'enable':
|
||||
self.no_option = '--disable-' + name
|
||||
self.yes_option = '--enable-' + name
|
||||
elif style == 'with':
|
||||
self.no_option = '--without-' + name
|
||||
self.yes_option = '--with-' + name
|
||||
else:
|
||||
opt.fatal('invalid style')
|
||||
|
||||
if style in ['yesno', 'yesno_and_hack']:
|
||||
opt.add_option(
|
||||
self.yes_option,
|
||||
dest=self.dest,
|
||||
action='store',
|
||||
choices=['auto', 'no', 'yes'],
|
||||
default='auto',
|
||||
help=option_help,
|
||||
metavar='no|yes')
|
||||
else:
|
||||
opt.add_option(
|
||||
self.yes_option,
|
||||
dest=self.dest,
|
||||
action='store_const',
|
||||
const='yes',
|
||||
default='auto',
|
||||
help=option_help)
|
||||
opt.add_option(
|
||||
self.no_option,
|
||||
dest=self.dest,
|
||||
action='store_const',
|
||||
const='no',
|
||||
default='auto',
|
||||
help=no_option_help)
|
||||
|
||||
def check(self, *k, **kw):
|
||||
self.deps.append(('check', k, kw))
|
||||
def check_cfg(self, *k, **kw):
|
||||
self.deps.append(('check_cfg', k, kw))
|
||||
def find_program(self, *k, **kw):
|
||||
self.deps.append(('find_program', k, kw))
|
||||
|
||||
def add_function(self, func, *k, **kw):
|
||||
"""
|
||||
Add a custom function to be invoked as part of the
|
||||
configuration. During the configuration the function will be
|
||||
invoked with the configuration context as first argument
|
||||
followed by the arguments to this method, except for the func
|
||||
argument. The function must print a 'Checking for...' message,
|
||||
because it is referred to if the check fails and this option is
|
||||
requested.
|
||||
|
||||
On configuration error the function shall raise
|
||||
conf.errors.ConfigurationError.
|
||||
"""
|
||||
self.deps.append((func, k, kw))
|
||||
|
||||
def _check(self, conf, required):
|
||||
"""
|
||||
This private method checks all dependencies. It checks all
|
||||
dependencies (even if some dependency was not found) so that the
|
||||
user can install all missing dependencies in one go, instead of
|
||||
playing the infamous hit-configure-hit-configure game.
|
||||
|
||||
This function returns True if all dependencies were found and
|
||||
False otherwise.
|
||||
"""
|
||||
all_found = True
|
||||
|
||||
for (f,k,kw) in self.deps:
|
||||
if hasattr(f, '__call__'):
|
||||
# This is a function supplied by add_function.
|
||||
func = f
|
||||
k = list(k)
|
||||
k.insert(0, conf)
|
||||
k = tuple(k)
|
||||
else:
|
||||
func = getattr(conf, f)
|
||||
|
||||
try:
|
||||
func(*k, **kw)
|
||||
except conf.errors.ConfigurationError:
|
||||
all_found = False
|
||||
if required:
|
||||
Logs.error('The above check failed, but the '
|
||||
'checkee is required for %s.' %
|
||||
self.yes_option)
|
||||
|
||||
return all_found
|
||||
|
||||
def configure(self, conf):
|
||||
"""
|
||||
This function configures the option examining the command line
|
||||
option. It sets self.enable to whether this options should be
|
||||
enabled or not, that is True or False respectively. If not all
|
||||
dependencies were found self.enable will be False.
|
||||
conf.env['NAME'] and a preprocessor symbol will be defined with
|
||||
the result.
|
||||
|
||||
If the option was desired but one or more dependencies were not
|
||||
found the an error message will be printed for each missing
|
||||
dependency.
|
||||
|
||||
This function returns True on success and False on if the option
|
||||
was requested but cannot be enabled.
|
||||
"""
|
||||
# If the option has already been configured once, do not
|
||||
# configure it again.
|
||||
if self.enable != None:
|
||||
return True
|
||||
|
||||
argument = getattr(Options.options, self.dest)
|
||||
if argument == 'no':
|
||||
self.enable = False
|
||||
retvalue = True
|
||||
elif argument == 'yes':
|
||||
retvalue = self.enable = self._check(conf, True)
|
||||
else:
|
||||
self.enable = self.default and self._check(conf, False)
|
||||
retvalue = True
|
||||
|
||||
conf.env[self.conf_dest] = self.enable
|
||||
conf.define(self.define, int(self.enable))
|
||||
return retvalue
|
||||
|
||||
def summarize(self, conf):
|
||||
"""
|
||||
This function displays a result summary with the help text and
|
||||
the result of the configuration.
|
||||
"""
|
||||
if self.help:
|
||||
if self.enable:
|
||||
conf.msg(self.help, 'yes', color='GREEN')
|
||||
else:
|
||||
conf.msg(self.help, 'no', color='YELLOW')
|
||||
|
||||
def options(opt):
|
||||
"""
|
||||
This function declares necessary variables in the option context.
|
||||
The reason for saving variables in the option context is to allow
|
||||
autooptions to be loaded from modules (which will receive a new
|
||||
instance of this module, clearing any global variables) with a
|
||||
uniform style and default in the entire project.
|
||||
|
||||
Call this function through opt.load('autooptions').
|
||||
"""
|
||||
if not hasattr(opt, 'auto_options_style'):
|
||||
opt.auto_options_style = 'plain'
|
||||
if not hasattr(opt, 'auto_options_define'):
|
||||
opt.auto_options_define = 'WITH_%s'
|
||||
|
||||
def opt(f):
|
||||
"""
|
||||
Decorator: attach a new option function to Options.OptionsContext.
|
||||
|
||||
:param f: method to bind
|
||||
:type f: function
|
||||
"""
|
||||
setattr(Options.OptionsContext, f.__name__, f)
|
||||
|
||||
@opt
|
||||
def add_auto_option(self, *k, **kw):
|
||||
"""
|
||||
This function adds an AutoOption to the options context. It takes
|
||||
the same arguments as the initializer function of the AutoOptions
|
||||
class.
|
||||
"""
|
||||
option = AutoOption(self, *k, **kw)
|
||||
auto_options.append(option)
|
||||
return option
|
||||
|
||||
@opt
|
||||
def get_auto_options_define(self):
|
||||
"""
|
||||
This function gets the default define name. This default can be
|
||||
changed through set_auto_optoins_define.
|
||||
"""
|
||||
return self.auto_options_define
|
||||
|
||||
@opt
|
||||
def set_auto_options_define(self, define):
|
||||
"""
|
||||
This function sets the default define name. The default is
|
||||
'WITH_%s', where %s will be replaced with the name of the option in
|
||||
uppercase.
|
||||
"""
|
||||
self.auto_options_define = define
|
||||
|
||||
@opt
|
||||
def get_auto_options_style(self):
|
||||
"""
|
||||
This function gets the default option style, which will be used for
|
||||
the subsequent options.
|
||||
"""
|
||||
return self.auto_options_style
|
||||
|
||||
@opt
|
||||
def set_auto_options_style(self, style):
|
||||
"""
|
||||
This function sets the default option style, which will be used for
|
||||
the subsequent options.
|
||||
"""
|
||||
self.auto_options_style = style
|
||||
|
||||
@opt
|
||||
def apply_auto_options_hack(self):
|
||||
"""
|
||||
This function applies the hack necessary for the yesno_and_hack
|
||||
option style. The hack turns --foo into --foo=yes and --no-foo into
|
||||
--foo=no.
|
||||
|
||||
It must be called before options are parsed, that is before the
|
||||
configure phase.
|
||||
"""
|
||||
for option in auto_options:
|
||||
# With the hack the yesno options simply extend plain options.
|
||||
if option.style == 'yesno_and_hack':
|
||||
for i in range(1, len(sys.argv)):
|
||||
if sys.argv[i] == option.yes_option:
|
||||
sys.argv[i] = option.yes_option + '=yes'
|
||||
elif sys.argv[i] == option.no_option:
|
||||
sys.argv[i] = option.yes_option + '=no'
|
||||
|
||||
@Configure.conf
|
||||
def summarize_auto_options(self):
|
||||
"""
|
||||
This function prints a summary of the configuration of the auto
|
||||
options. Obviously, it must be called after
|
||||
conf.load('autooptions').
|
||||
"""
|
||||
for option in auto_options:
|
||||
option.summarize(self)
|
||||
|
||||
def configure(conf):
|
||||
"""
|
||||
This configures all auto options. Call it through
|
||||
conf.load('autooptions').
|
||||
"""
|
||||
ok = True
|
||||
for option in auto_options:
|
||||
if not option.configure(conf):
|
||||
ok = False
|
||||
if not ok:
|
||||
conf.fatal('Some requested options had unsatisfied ' +
|
||||
'dependencies.\n' +
|
||||
'See the above configuration for details.')
|
Loading…
Reference in New Issue