Allow load from multiple modules.
[waf-autooptions.git] / __init__.py
blobac9df56b4d6d32b7ad4c6a234bb8ac0c8c4c911c
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, default=True, conf_dest=None,
90 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 default:
117 help_comment = ' (enabled by default if possible)'
118 else:
119 help_comment = ' (disabled by default)'
121 self.dest = 'auto_option_' + name
123 self.default = default
125 safe_name = Utils.quote_define_name(name)
126 self.conf_dest = conf_dest or safe_name
128 default_define = opt.get_auto_options_define()
129 self.define = define or default_define % safe_name
131 if not style:
132 style = opt.get_auto_options_style()
133 self.style = style
135 # plain (default):
136 # --foo | --no-foo
137 # yesno:
138 # --foo=yes | --foo=no
139 # yesno_and_hack:
140 # --foo[=yes] | --foo=no or --no-foo
141 # enable:
142 # --enable-foo | --disble-foo
143 # wit:
144 # --with-foo | --without-foo
145 if style in ['plain', 'yesno', 'yesno_and_hack']:
146 self.no_option = '--no-' + name
147 self.yes_option = '--' + name
148 if style == 'yesno_and_hack':
149 apply_hack = True
150 elif style == 'enable':
151 self.no_option = '--disable-' + name
152 self.yes_option = '--enable-' + name
153 elif style == 'with':
154 self.no_option = '--without-' + name
155 self.yes_option = '--with-' + name
156 else:
157 opt.fatal("invalid style")
159 if style in ['yesno', 'yesno_and_hack']:
160 opt.add_option(
161 self.yes_option,
162 dest=self.dest,
163 action='store',
164 choices=['auto', 'no', 'yes'],
165 default='auto',
166 help=self.help + help_comment,
167 metavar='no|yes')
168 else:
169 opt.add_option(
170 self.yes_option,
171 dest=self.dest,
172 action='store_const',
173 const='yes',
174 default='auto',
175 help=self.help + help_comment)
176 opt.add_option(
177 self.no_option,
178 dest=self.dest,
179 action='store_const',
180 const='no',
181 default='auto')
183 funcs = ['check', 'check_cfg', 'find_program']
184 for fn in funcs:
185 def f(*k, fn=fn, **kw):
186 self.deps.append((fn, k, kw))
187 setattr(self, fn, f)
189 def add_function(self, func, *k, **kw):
191 Add a custom function to be invoked as part of the
192 configuration. During the configuration the function will be
193 invoked with the configuration context as first argument
194 followed by the arugments to this method, except for the func
195 argument. The function must print a "Checking for..." message,
196 because it is referred to if the check fails and this option is
197 requested.
199 On configuration error the function shall raise
200 conf.errors.ConfigurationError.
202 self.deps.append((func, k, kw))
204 def _check(self, conf, required):
206 This private method runs checks all dependencies.
207 It checks all dependencies (even if some dependency was not
208 found) so that the user can install all missing dependencies in
209 one go, instead of playing the infamous
210 hit-configure-hit-configure game.
212 This function returns True if all dependencies were found and
213 False otherwise.
215 all_found = True
217 for (f,k,kw) in self.deps:
218 if hasattr(f, '__call__'):
219 # This is a function supplied by add_function.
220 func = f
221 k = list(k)
222 k.insert(0, conf)
223 k = tuple(k)
224 else:
225 func = getattr(conf, f)
226 try:
227 func(*k, **kw)
228 except conf.errors.ConfigurationError:
229 all_found = False
230 if required:
231 Logs.error('The above check failed, but the '
232 'checkee is required for %s.' %
233 self.yes_option)
235 return all_found
237 def configure(self, conf):
239 This function configures the option examining the command line
240 option. It sets self.enable to whether this options should be
241 enabled or not, that is True or False respectively. If not all
242 dependencies were found self.enable will be False.
243 conf.env['NAME'] and a preprocessor symbol will be defined with
244 the result.
246 If the option was desired but one or more dependencies were not
247 found the an error message will be printed for each missing
248 dependency.
250 This function returns True on success and False on if the option
251 was requested but cannot be enabled.
253 # If the option has already been configured once, do not
254 # configure it again.
255 if self.enable != None:
256 return True
258 argument = getattr(Options.options, self.dest)
259 if argument == 'no':
260 self.enable = False
261 retvalue = True
262 elif argument == 'yes':
263 retvalue = self.enable = self._check(conf, True)
264 else:
265 self.enable = self.default and self._check(conf, False)
266 retvalue = True
268 conf.env[self.conf_dest] = self.enable
269 conf.define(self.define, int(self.enable))
270 return retvalue
272 def summarize(self, conf):
274 This function displays a result summary with the help text and
275 the result of the configuration.
277 if self.enable:
278 conf.msg(self.help, 'yes', color='GREEN')
279 else:
280 conf.msg(self.help, 'no', color='YELLOW')
282 def options(opt):
284 This function declares necessary variables in the option context.
285 The reason for saving variables in the option context is to allow
286 autooptions to be loaded from modules (which will receive a new
287 instance of this module, clearing any global variables) with a
288 uniform style and default in the entire project.
290 Call this function through opt.load('autooptions').
292 if not hasattr(opt, 'auto_options_style'):
293 opt.auto_options_style = 'plain'
294 if not hasattr(opt, 'auto_options_define'):
295 opt.auto_options_define = 'WITH_%s'
297 def opt(f):
299 Decorator: attach a new option function to Options.OptionsContext.
301 :param f: method to bind
302 :type f: function
304 setattr(Options.OptionsContext, f.__name__, f)
306 @opt
307 def add_auto_option(self, *k, **kw):
309 This function adds an AutoOptions to the options context. It takes
310 the same arguments as the initializer funtion of the AutoOptions
311 class.
313 option = AutoOption(self, *k, **kw)
314 auto_options.append(option)
315 return option
317 @opt
318 def get_auto_options_define(self):
320 This function gets the default define name. This default can be
321 changed through set_auto_optoins_define.
323 return self.auto_options_define
325 @opt
326 def set_auto_options_define(self, define):
328 This function sets the default define name. The default is
329 "WITH_%s", where %s will be replaced with the name of the option in
330 uppercase.
332 self.auto_options_define = define
334 @opt
335 def get_auto_options_style(self):
337 This function gets the default option style, which will be used for
338 the subsequent options.
340 return self.auto_options_style
342 @opt
343 def set_auto_options_style(self, style):
345 This function sets the default option style, which will be used for
346 the subsequent options.
348 self.auto_options_style = style
350 @opt
351 def apply_auto_options_hack(self):
353 This function applies the hack necessary for the yesno_and_hack
354 option style. The hack turns --foo into --foo=yes and --no-foo into
355 --foo=no.
357 It must be called before options are parsed, that is before the
358 configure phase.
360 for option in auto_options:
361 # With the hack the yesno options simply extend plain
362 # options.
363 if option.style == 'yesno_and_hack':
364 for i in range(1, len(sys.argv)):
365 if sys.argv[i] == option.yes_option:
366 sys.argv[i] = option.yes_option + '=yes'
367 elif sys.argv[i] == option.no_option:
368 sys.argv[i] = option.yes_option + '=no'
370 @Configure.conf
371 def summarize_auto_options(self):
373 This function prints a summary of the configuration of the auto
374 options. Obviously, it must be called after
375 conf.load("autooptions").
377 for option in auto_options:
378 option.summarize(self)
380 def configure(conf):
382 This configures all auto options. Call it through
383 conf.load("autooptions").
385 ok = True
386 for option in auto_options:
387 if not option.configure(conf):
388 ok = False
389 if not ok:
390 conf.fatal('Some requested options had unsatisfied ' +
391 'dependencies.\n' +
392 'See the above configuration for details.')