autooptions: switch to git submodule

This commit is contained in:
Nedko Arnaudov 2022-08-15 03:12:07 +03:00
parent e8ab33123f
commit 9e3cd203f1
4 changed files with 4 additions and 401 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "waf-autooptions"]
path = autooptions
url = ../waf-autooptions

1
autooptions Submodule

@ -0,0 +1 @@
Subproject commit cc16822604501c96af265a2e3c51855d2aed635f

View File

@ -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

View File

@ -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.')