Add LICENSE.BSD-2
[waf-autooptions.git] / __init__.py
blobf0b22887031120e8c114b1f9797beef7ddf08e8e
2 # Copyright (C) 2017 Karl Linden
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
8 # 1. Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the
14 # distribution.
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 import optparse
30 import sys
31 from waflib import Configure, Logs, Options, Utils
33 # A list of AutoOptions. It is local to each module, so all modules that
34 # use AutoOptions need to run both opt.load and conf.load. In contrast
35 # to the define and style options this does not need to and cannot be
36 # declared in the OptionsContext, because it is needed both for the
37 # options and the configure phase.
38 auto_options = []
40 class AutoOption:
41 """
42 This class represents an auto option that can be used in conjunction
43 with the waf build system. By default it adds options --foo and
44 --no-foo respectively to turn on or off foo respectively.
45 Furthermore it incorporats logic and checks that are required for
46 these features.
48 An option can have an arbitrary number of dependencies that must be
49 present for the option to be enabled. An option can be enabled or
50 disabled by default. Here is how the logic works:
51 1. If the option is explicitly disabled, through --no-foo, then no
52 checks are made and the option is disabled.
53 2. If the option is explicitly enabled, through --foo, then check
54 for all required dependencies, and if some of them are not
55 found, then print a fatal error telling the user there were
56 dependencies missing.
57 3. Otherwise, if the option is enabled by default, then check for
58 all dependencies. If all dependencies are found the option is
59 enabled. Otherwise it is disabled.
60 4. Lastly, if no option was given and the option is disabled by
61 default, then no checks are performed and the option is
62 disabled.
64 To add a dependency to an option use the check, check_cfg and
65 find_program methods of this class. The methods are merely small
66 wrappers around their configuration context counterparts and behave
67 identically. Note that adding dependencies is done in the options
68 phase and not in the configure phase, although the checks are
69 acutally executed during the configure phase.
71 Custom check functions can be added using the add_function method.
72 As with the other checks the check function will be invoked during
73 the configuration. Refer to the documentation of the add_function
74 method for details.
76 When all checks have been made and the class has made a decision the
77 result is saved in conf.env['NAME'] where 'NAME' by default is the
78 uppercase of the name argument to __init__, with hyphens replaced by
79 underscores. This default can be changed with the conf_dest argument
80 to __init__.
82 The class will define a preprocessor symbol with the result. The
83 default name is WITH_NAME, to not collide with the standard define
84 of check_cfg, but it can be changed using the define argument to
85 __init__. It can also be changed globally using
86 set_auto_options_define.
87 """
89 def __init__(self, opt, name, help=None, default=True,
90 conf_dest=None, define=None, style=None):
91 """
92 Class initializing method.
94 Arguments:
95 opt OptionsContext
96 name name of the option, e.g. alsa
97 help help text that will be displayed in --help output
98 conf_dest conf.env variable to define to the result
99 define the preprocessor symbol to define with the result
100 style the option style to use; see below for options
103 # The dependencies to check for. The elements are on the form
104 # (func, k, kw) where func is the function or function name that
105 # is used for the check and k and kw are the arguments and
106 # options to give the function.
107 self.deps = []
109 # Whether or not the option should be enabled. None indicates
110 # that the checks have not been performed yet.
111 self.enable = None
113 self.help = help
114 if help:
115 if default:
116 help_comment = ' (enabled by default if possible)'
117 else:
118 help_comment = ' (disabled by default)'
119 option_help = help + help_comment
120 no_option_help = None
121 else:
122 option_help = no_option_help = optparse.SUPPRESS_HELP
124 self.dest = 'auto_option_' + name
126 self.default = default
128 safe_name = Utils.quote_define_name(name)
129 self.conf_dest = conf_dest or safe_name
131 default_define = opt.get_auto_options_define()
132 self.define = define or default_define % safe_name
134 if not style:
135 style = opt.get_auto_options_style()
136 self.style = style
138 # plain (default):
139 # --foo | --no-foo
140 # yesno:
141 # --foo=yes | --foo=no
142 # yesno_and_hack:
143 # --foo[=yes] | --foo=no or --no-foo
144 # enable:
145 # --enable-foo | --disble-foo
146 # with:
147 # --with-foo | --without-foo
148 if style in ['plain', 'yesno', 'yesno_and_hack']:
149 self.no_option = '--no-' + name
150 self.yes_option = '--' + name
151 elif style == 'enable':
152 self.no_option = '--disable-' + name
153 self.yes_option = '--enable-' + name
154 elif style == 'with':
155 self.no_option = '--without-' + name
156 self.yes_option = '--with-' + name
157 else:
158 opt.fatal('invalid style')
160 if style in ['yesno', 'yesno_and_hack']:
161 opt.add_option(
162 self.yes_option,
163 dest=self.dest,
164 action='store',
165 choices=['auto', 'no', 'yes'],
166 default='auto',
167 help=option_help,
168 metavar='no|yes')
169 else:
170 opt.add_option(
171 self.yes_option,
172 dest=self.dest,
173 action='store_const',
174 const='yes',
175 default='auto',
176 help=option_help)
177 opt.add_option(
178 self.no_option,
179 dest=self.dest,
180 action='store_const',
181 const='no',
182 default='auto',
183 help=no_option_help)
185 def check(self, *k, **kw):
186 self.deps.append(('check', k, kw))
187 def check_cfg(self, *k, **kw):
188 self.deps.append(('check_cfg', k, kw))
189 def find_program(self, *k, **kw):
190 self.deps.append(('find_program', k, kw))
192 def add_function(self, func, *k, **kw):
194 Add a custom function to be invoked as part of the
195 configuration. During the configuration the function will be
196 invoked with the configuration context as first argument
197 followed by the arugments to this method, except for the func
198 argument. The function must print a 'Checking for...' message,
199 because it is referred to if the check fails and this option is
200 requested.
202 On configuration error the function shall raise
203 conf.errors.ConfigurationError.
205 self.deps.append((func, k, kw))
207 def _check(self, conf, required):
209 This private method checks all dependencies. It checks all
210 dependencies (even if some dependency was not found) so that the
211 user can install all missing dependencies in one go, instead of
212 playing the infamous hit-configure-hit-configure game.
214 This function returns True if all dependencies were found and
215 False otherwise.
217 all_found = True
219 for (f,k,kw) in self.deps:
220 if hasattr(f, '__call__'):
221 # This is a function supplied by add_function.
222 func = f
223 k = list(k)
224 k.insert(0, conf)
225 k = tuple(k)
226 else:
227 func = getattr(conf, f)
229 try:
230 func(*k, **kw)
231 except conf.errors.ConfigurationError:
232 all_found = False
233 if required:
234 Logs.error('The above check failed, but the '
235 'checkee is required for %s.' %
236 self.yes_option)
238 return all_found
240 def configure(self, conf):
242 This function configures the option examining the command line
243 option. It sets self.enable to whether this options should be
244 enabled or not, that is True or False respectively. If not all
245 dependencies were found self.enable will be False.
246 conf.env['NAME'] and a preprocessor symbol will be defined with
247 the result.
249 If the option was desired but one or more dependencies were not
250 found the an error message will be printed for each missing
251 dependency.
253 This function returns True on success and False on if the option
254 was requested but cannot be enabled.
256 # If the option has already been configured once, do not
257 # configure it again.
258 if self.enable != None:
259 return True
261 argument = getattr(Options.options, self.dest)
262 if argument == 'no':
263 self.enable = False
264 retvalue = True
265 elif argument == 'yes':
266 retvalue = self.enable = self._check(conf, True)
267 else:
268 self.enable = self.default and self._check(conf, False)
269 retvalue = True
271 conf.env[self.conf_dest] = self.enable
272 conf.define(self.define, int(self.enable))
273 return retvalue
275 def summarize(self, conf):
277 This function displays a result summary with the help text and
278 the result of the configuration.
280 if self.help:
281 if self.enable:
282 conf.msg(self.help, 'yes', color='GREEN')
283 else:
284 conf.msg(self.help, 'no', color='YELLOW')
286 def options(opt):
288 This function declares necessary variables in the option context.
289 The reason for saving variables in the option context is to allow
290 autooptions to be loaded from modules (which will receive a new
291 instance of this module, clearing any global variables) with a
292 uniform style and default in the entire project.
294 Call this function through opt.load('autooptions').
296 if not hasattr(opt, 'auto_options_style'):
297 opt.auto_options_style = 'plain'
298 if not hasattr(opt, 'auto_options_define'):
299 opt.auto_options_define = 'WITH_%s'
301 def opt(f):
303 Decorator: attach a new option function to Options.OptionsContext.
305 :param f: method to bind
306 :type f: function
308 setattr(Options.OptionsContext, f.__name__, f)
310 @opt
311 def add_auto_option(self, *k, **kw):
313 This function adds an AutoOption to the options context. It takes
314 the same arguments as the initializer funtion of the AutoOptions
315 class.
317 option = AutoOption(self, *k, **kw)
318 auto_options.append(option)
319 return option
321 @opt
322 def get_auto_options_define(self):
324 This function gets the default define name. This default can be
325 changed through set_auto_optoins_define.
327 return self.auto_options_define
329 @opt
330 def set_auto_options_define(self, define):
332 This function sets the default define name. The default is
333 'WITH_%s', where %s will be replaced with the name of the option in
334 uppercase.
336 self.auto_options_define = define
338 @opt
339 def get_auto_options_style(self):
341 This function gets the default option style, which will be used for
342 the subsequent options.
344 return self.auto_options_style
346 @opt
347 def set_auto_options_style(self, style):
349 This function sets the default option style, which will be used for
350 the subsequent options.
352 self.auto_options_style = style
354 @opt
355 def apply_auto_options_hack(self):
357 This function applies the hack necessary for the yesno_and_hack
358 option style. The hack turns --foo into --foo=yes and --no-foo into
359 --foo=no.
361 It must be called before options are parsed, that is before the
362 configure phase.
364 for option in auto_options:
365 # With the hack the yesno options simply extend plain options.
366 if option.style == 'yesno_and_hack':
367 for i in range(1, len(sys.argv)):
368 if sys.argv[i] == option.yes_option:
369 sys.argv[i] = option.yes_option + '=yes'
370 elif sys.argv[i] == option.no_option:
371 sys.argv[i] = option.yes_option + '=no'
373 @Configure.conf
374 def summarize_auto_options(self):
376 This function prints a summary of the configuration of the auto
377 options. Obviously, it must be called after
378 conf.load('autooptions').
380 for option in auto_options:
381 option.summarize(self)
383 def configure(conf):
385 This configures all auto options. Call it through
386 conf.load('autooptions').
388 ok = True
389 for option in auto_options:
390 if not option.configure(conf):
391 ok = False
392 if not ok:
393 conf.fatal('Some requested options had unsatisfied ' +
394 'dependencies.\n' +
395 'See the above configuration for details.')