Prefer single quotes
[waf-autooptions.git] / __init__.py
blobab6c7d5756fd96b470da039633e2f6ac2b007680
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 # Dependencies and dependencies that are not found respectively.
104 # elements of both are on the form (func, k, kw) where type
105 # is a string equal to 'library', 'header', 'package' or
106 # 'program', func is the function or function name that is used
107 # for the check and k and kw are the arguments and options to
108 # give the check function.
109 self.deps = []
111 # Whether or not the option should be enabled or not. None
112 # indicates that the checks are not done yet.
113 self.enable = None
115 self.help = help
116 if help:
117 if default:
118 help_comment = ' (enabled by default if possible)'
119 else:
120 help_comment = ' (disabled by default)'
121 option_help = help + help_comment
122 no_option_help = None
123 else:
124 option_help = no_option_help = optparse.SUPPRESS_HELP
126 self.dest = 'auto_option_' + name
128 self.default = default
130 safe_name = Utils.quote_define_name(name)
131 self.conf_dest = conf_dest or safe_name
133 default_define = opt.get_auto_options_define()
134 self.define = define or default_define % safe_name
136 if not style:
137 style = opt.get_auto_options_style()
138 self.style = style
140 # plain (default):
141 # --foo | --no-foo
142 # yesno:
143 # --foo=yes | --foo=no
144 # yesno_and_hack:
145 # --foo[=yes] | --foo=no or --no-foo
146 # enable:
147 # --enable-foo | --disble-foo
148 # wit:
149 # --with-foo | --without-foo
150 if style in ['plain', 'yesno', 'yesno_and_hack']:
151 self.no_option = '--no-' + name
152 self.yes_option = '--' + name
153 elif style == 'enable':
154 self.no_option = '--disable-' + name
155 self.yes_option = '--enable-' + name
156 elif style == 'with':
157 self.no_option = '--without-' + name
158 self.yes_option = '--with-' + name
159 else:
160 opt.fatal('invalid style')
162 if style in ['yesno', 'yesno_and_hack']:
163 opt.add_option(
164 self.yes_option,
165 dest=self.dest,
166 action='store',
167 choices=['auto', 'no', 'yes'],
168 default='auto',
169 help=option_help,
170 metavar='no|yes')
171 else:
172 opt.add_option(
173 self.yes_option,
174 dest=self.dest,
175 action='store_const',
176 const='yes',
177 default='auto',
178 help=option_help)
179 opt.add_option(
180 self.no_option,
181 dest=self.dest,
182 action='store_const',
183 const='no',
184 default='auto',
185 help=no_option_help)
187 def check(self, *k, **kw):
188 self.deps.append(('check', k, kw))
189 def check_cfg(self, *k, **kw):
190 self.deps.append(('check_cfg', k, kw))
191 def find_program(self, *k, **kw):
192 self.deps.append(('find_program', k, kw))
194 def add_function(self, func, *k, **kw):
196 Add a custom function to be invoked as part of the
197 configuration. During the configuration the function will be
198 invoked with the configuration context as first argument
199 followed by the arugments to this method, except for the func
200 argument. The function must print a 'Checking for...' message,
201 because it is referred to if the check fails and this option is
202 requested.
204 On configuration error the function shall raise
205 conf.errors.ConfigurationError.
207 self.deps.append((func, k, kw))
209 def _check(self, conf, required):
211 This private method runs checks all dependencies.
212 It checks all dependencies (even if some dependency was not
213 found) so that the user can install all missing dependencies in
214 one go, instead of playing the infamous
215 hit-configure-hit-configure game.
217 This function returns True if all dependencies were found and
218 False otherwise.
220 all_found = True
222 for (f,k,kw) in self.deps:
223 if hasattr(f, '__call__'):
224 # This is a function supplied by add_function.
225 func = f
226 k = list(k)
227 k.insert(0, conf)
228 k = tuple(k)
229 else:
230 func = getattr(conf, f)
231 try:
232 func(*k, **kw)
233 except conf.errors.ConfigurationError:
234 all_found = False
235 if required:
236 Logs.error('The above check failed, but the '
237 'checkee is required for %s.' %
238 self.yes_option)
240 return all_found
242 def configure(self, conf):
244 This function configures the option examining the command line
245 option. It sets self.enable to whether this options should be
246 enabled or not, that is True or False respectively. If not all
247 dependencies were found self.enable will be False.
248 conf.env['NAME'] and a preprocessor symbol will be defined with
249 the result.
251 If the option was desired but one or more dependencies were not
252 found the an error message will be printed for each missing
253 dependency.
255 This function returns True on success and False on if the option
256 was requested but cannot be enabled.
258 # If the option has already been configured once, do not
259 # configure it again.
260 if self.enable != None:
261 return True
263 argument = getattr(Options.options, self.dest)
264 if argument == 'no':
265 self.enable = False
266 retvalue = True
267 elif argument == 'yes':
268 retvalue = self.enable = self._check(conf, True)
269 else:
270 self.enable = self.default and self._check(conf, False)
271 retvalue = True
273 conf.env[self.conf_dest] = self.enable
274 conf.define(self.define, int(self.enable))
275 return retvalue
277 def summarize(self, conf):
279 This function displays a result summary with the help text and
280 the result of the configuration.
282 if self.help:
283 if self.enable:
284 conf.msg(self.help, 'yes', color='GREEN')
285 else:
286 conf.msg(self.help, 'no', color='YELLOW')
288 def options(opt):
290 This function declares necessary variables in the option context.
291 The reason for saving variables in the option context is to allow
292 autooptions to be loaded from modules (which will receive a new
293 instance of this module, clearing any global variables) with a
294 uniform style and default in the entire project.
296 Call this function through opt.load('autooptions').
298 if not hasattr(opt, 'auto_options_style'):
299 opt.auto_options_style = 'plain'
300 if not hasattr(opt, 'auto_options_define'):
301 opt.auto_options_define = 'WITH_%s'
303 def opt(f):
305 Decorator: attach a new option function to Options.OptionsContext.
307 :param f: method to bind
308 :type f: function
310 setattr(Options.OptionsContext, f.__name__, f)
312 @opt
313 def add_auto_option(self, *k, **kw):
315 This function adds an AutoOptions to the options context. It takes
316 the same arguments as the initializer funtion of the AutoOptions
317 class.
319 option = AutoOption(self, *k, **kw)
320 auto_options.append(option)
321 return option
323 @opt
324 def get_auto_options_define(self):
326 This function gets the default define name. This default can be
327 changed through set_auto_optoins_define.
329 return self.auto_options_define
331 @opt
332 def set_auto_options_define(self, define):
334 This function sets the default define name. The default is
335 'WITH_%s', where %s will be replaced with the name of the option in
336 uppercase.
338 self.auto_options_define = define
340 @opt
341 def get_auto_options_style(self):
343 This function gets the default option style, which will be used for
344 the subsequent options.
346 return self.auto_options_style
348 @opt
349 def set_auto_options_style(self, style):
351 This function sets the default option style, which will be used for
352 the subsequent options.
354 self.auto_options_style = style
356 @opt
357 def apply_auto_options_hack(self):
359 This function applies the hack necessary for the yesno_and_hack
360 option style. The hack turns --foo into --foo=yes and --no-foo into
361 --foo=no.
363 It must be called before options are parsed, that is before the
364 configure phase.
366 for option in auto_options:
367 # With the hack the yesno options simply extend plain
368 # options.
369 if option.style == 'yesno_and_hack':
370 for i in range(1, len(sys.argv)):
371 if sys.argv[i] == option.yes_option:
372 sys.argv[i] = option.yes_option + '=yes'
373 elif sys.argv[i] == option.no_option:
374 sys.argv[i] = option.yes_option + '=no'
376 @Configure.conf
377 def summarize_auto_options(self):
379 This function prints a summary of the configuration of the auto
380 options. Obviously, it must be called after
381 conf.load('autooptions').
383 for option in auto_options:
384 option.summarize(self)
386 def configure(conf):
388 This configures all auto options. Call it through
389 conf.load('autooptions').
391 ok = True
392 for option in auto_options:
393 if not option.configure(conf):
394 ok = False
395 if not ok:
396 conf.fatal('Some requested options had unsatisfied ' +
397 'dependencies.\n' +
398 'See the above configuration for details.')