1#!/usr/bin/env python 2# encoding: utf-8 3# Scott Newton, 2005 (scottn) 4# Thomas Nagy, 2006-2018 (ita) 5 6""" 7Support for waf command-line options 8 9Provides default and command-line options, as well the command 10that reads the ``options`` wscript function. 11""" 12 13import os, tempfile, optparse, sys, re 14from waflib import Logs, Utils, Context, Errors 15 16options = optparse.Values() 17""" 18A global dictionary representing user-provided command-line options:: 19 20 $ waf --foo=bar 21""" 22 23commands = [] 24""" 25List of commands to execute extracted from the command-line. This list 26is consumed during the execution by :py:func:`waflib.Scripting.run_commands`. 27""" 28 29envvars = [] 30""" 31List of environment variable declarations placed after the Waf executable name. 32These are detected by searching for "=" in the remaining arguments. 33You probably do not want to use this. 34""" 35 36lockfile = os.environ.get('WAFLOCK', '.lock-waf_%s_build' % sys.platform) 37""" 38Name of the lock file that marks a project as configured 39""" 40 41class opt_parser(optparse.OptionParser): 42 """ 43 Command-line options parser. 44 """ 45 def __init__(self, ctx, allow_unknown=False): 46 optparse.OptionParser.__init__(self, conflict_handler='resolve', add_help_option=False, 47 version='%s %s (%s)' % (Context.WAFNAME, Context.WAFVERSION, Context.WAFREVISION)) 48 self.formatter.width = Logs.get_term_cols() 49 self.ctx = ctx 50 self.allow_unknown = allow_unknown 51 52 def _process_args(self, largs, rargs, values): 53 """ 54 Custom _process_args to allow unknown options according to the allow_unknown status 55 """ 56 while rargs: 57 try: 58 optparse.OptionParser._process_args(self,largs,rargs,values) 59 except (optparse.BadOptionError, optparse.AmbiguousOptionError) as e: 60 if self.allow_unknown: 61 largs.append(e.opt_str) 62 else: 63 self.error(str(e)) 64 65 def _process_long_opt(self, rargs, values): 66 # --custom-option=-ftxyz is interpreted as -f -t... see #2280 67 if self.allow_unknown: 68 back = [] + rargs 69 try: 70 optparse.OptionParser._process_long_opt(self, rargs, values) 71 except optparse.BadOptionError: 72 while rargs: 73 rargs.pop() 74 rargs.extend(back) 75 rargs.pop(0) 76 raise 77 else: 78 optparse.OptionParser._process_long_opt(self, rargs, values) 79 80 def print_usage(self, file=None): 81 return self.print_help(file) 82 83 def get_usage(self): 84 """ 85 Builds the message to print on ``waf --help`` 86 87 :rtype: string 88 """ 89 cmds_str = {} 90 for cls in Context.classes: 91 if not cls.cmd or cls.cmd == 'options' or cls.cmd.startswith( '_' ): 92 continue 93 94 s = cls.__doc__ or '' 95 cmds_str[cls.cmd] = s 96 97 if Context.g_module: 98 for (k, v) in Context.g_module.__dict__.items(): 99 if k in ('options', 'init', 'shutdown'): 100 continue 101 102 if type(v) is type(Context.create_context): 103 if v.__doc__ and not k.startswith('_'): 104 cmds_str[k] = v.__doc__ 105 106 just = 0 107 for k in cmds_str: 108 just = max(just, len(k)) 109 110 lst = [' %s: %s' % (k.ljust(just), v) for (k, v) in cmds_str.items()] 111 lst.sort() 112 ret = '\n'.join(lst) 113 114 return '''%s [commands] [options] 115 116Main commands (example: ./%s build -j4) 117%s 118''' % (Context.WAFNAME, Context.WAFNAME, ret) 119 120 121class OptionsContext(Context.Context): 122 """ 123 Collects custom options from wscript files and parses the command line. 124 Sets the global :py:const:`waflib.Options.commands` and :py:const:`waflib.Options.options` values. 125 """ 126 cmd = 'options' 127 fun = 'options' 128 129 def __init__(self, **kw): 130 super(OptionsContext, self).__init__(**kw) 131 132 self.parser = opt_parser(self) 133 """Instance of :py:class:`waflib.Options.opt_parser`""" 134 135 self.option_groups = {} 136 137 jobs = self.jobs() 138 p = self.add_option 139 color = os.environ.get('NOCOLOR', '') and 'no' or 'auto' 140 if os.environ.get('CLICOLOR', '') == '0': 141 color = 'no' 142 elif os.environ.get('CLICOLOR_FORCE', '') == '1': 143 color = 'yes' 144 p('-c', '--color', dest='colors', default=color, action='store', help='whether to use colors (yes/no/auto) [default: auto]', choices=('yes', 'no', 'auto')) 145 p('-j', '--jobs', dest='jobs', default=jobs, type='int', help='amount of parallel jobs (%r)' % jobs) 146 p('-k', '--keep', dest='keep', default=0, action='count', help='continue despite errors (-kk to try harder)') 147 p('-v', '--verbose', dest='verbose', default=0, action='count', help='verbosity level -v -vv or -vvv [default: 0]') 148 p('--zones', dest='zones', default='', action='store', help='debugging zones (task_gen, deps, tasks, etc)') 149 p('--profile', dest='profile', default=0, action='store_true', help=optparse.SUPPRESS_HELP) 150 p('--pdb', dest='pdb', default=0, action='store_true', help=optparse.SUPPRESS_HELP) 151 p('-h', '--help', dest='whelp', default=0, action='store_true', help="show this help message and exit") 152 153 gr = self.add_option_group('Configuration options') 154 self.option_groups['configure options'] = gr 155 156 gr.add_option('-o', '--out', action='store', default='', help='build dir for the project', dest='out') 157 gr.add_option('-t', '--top', action='store', default='', help='src dir for the project', dest='top') 158 159 gr.add_option('--no-lock-in-run', action='store_true', default=os.environ.get('NO_LOCK_IN_RUN', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_run') 160 gr.add_option('--no-lock-in-out', action='store_true', default=os.environ.get('NO_LOCK_IN_OUT', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_out') 161 gr.add_option('--no-lock-in-top', action='store_true', default=os.environ.get('NO_LOCK_IN_TOP', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_top') 162 163 default_prefix = getattr(Context.g_module, 'default_prefix', os.environ.get('PREFIX')) 164 if not default_prefix: 165 if Utils.unversioned_sys_platform() == 'win32': 166 d = tempfile.gettempdir() 167 default_prefix = d[0].upper() + d[1:] 168 # win32 preserves the case, but gettempdir does not 169 else: 170 default_prefix = '/usr/local/' 171 gr.add_option('--prefix', dest='prefix', default=default_prefix, help='installation prefix [default: %r]' % default_prefix) 172 gr.add_option('--bindir', dest='bindir', help='bindir') 173 gr.add_option('--libdir', dest='libdir', help='libdir') 174 175 gr = self.add_option_group('Build and installation options') 176 self.option_groups['build and install options'] = gr 177 gr.add_option('-p', '--progress', dest='progress_bar', default=0, action='count', help= '-p: progress bar; -pp: ide output') 178 gr.add_option('--targets', dest='targets', default='', action='store', help='task generators, e.g. "target1,target2"') 179 180 gr = self.add_option_group('Step options') 181 self.option_groups['step options'] = gr 182 gr.add_option('--files', dest='files', default='', action='store', help='files to process, by regexp, e.g. "*/main.c,*/test/main.o"') 183 184 default_destdir = os.environ.get('DESTDIR', '') 185 186 gr = self.add_option_group('Installation and uninstallation options') 187 self.option_groups['install/uninstall options'] = gr 188 gr.add_option('--destdir', help='installation root [default: %r]' % default_destdir, default=default_destdir, dest='destdir') 189 gr.add_option('-f', '--force', dest='force', default=False, action='store_true', help='force file installation') 190 gr.add_option('--distcheck-args', metavar='ARGS', help='arguments to pass to distcheck', default=None, action='store') 191 192 def jobs(self): 193 """ 194 Finds the optimal amount of cpu cores to use for parallel jobs. 195 At runtime the options can be obtained from :py:const:`waflib.Options.options` :: 196 197 from waflib.Options import options 198 njobs = options.jobs 199 200 :return: the amount of cpu cores 201 :rtype: int 202 """ 203 count = int(os.environ.get('JOBS', 0)) 204 if count < 1: 205 if 'NUMBER_OF_PROCESSORS' in os.environ: 206 # on Windows, use the NUMBER_OF_PROCESSORS environment variable 207 count = int(os.environ.get('NUMBER_OF_PROCESSORS', 1)) 208 else: 209 # on everything else, first try the POSIX sysconf values 210 if hasattr(os, 'sysconf_names'): 211 if 'SC_NPROCESSORS_ONLN' in os.sysconf_names: 212 count = int(os.sysconf('SC_NPROCESSORS_ONLN')) 213 elif 'SC_NPROCESSORS_CONF' in os.sysconf_names: 214 count = int(os.sysconf('SC_NPROCESSORS_CONF')) 215 if not count and os.name not in ('nt', 'java'): 216 try: 217 tmp = self.cmd_and_log(['sysctl', '-n', 'hw.ncpu'], quiet=0) 218 except Errors.WafError: 219 pass 220 else: 221 if re.match('^[0-9]+$', tmp): 222 count = int(tmp) 223 if count < 1: 224 count = 1 225 elif count > 1024: 226 count = 1024 227 return count 228 229 def add_option(self, *k, **kw): 230 """ 231 Wraps ``optparse.add_option``:: 232 233 def options(ctx): 234 ctx.add_option('-u', '--use', dest='use', default=False, 235 action='store_true', help='a boolean option') 236 237 :rtype: optparse option object 238 """ 239 return self.parser.add_option(*k, **kw) 240 241 def add_option_group(self, *k, **kw): 242 """ 243 Wraps ``optparse.add_option_group``:: 244 245 def options(ctx): 246 gr = ctx.add_option_group('some options') 247 gr.add_option('-u', '--use', dest='use', default=False, action='store_true') 248 249 :rtype: optparse option group object 250 """ 251 try: 252 gr = self.option_groups[k[0]] 253 except KeyError: 254 gr = self.parser.add_option_group(*k, **kw) 255 self.option_groups[k[0]] = gr 256 return gr 257 258 def get_option_group(self, opt_str): 259 """ 260 Wraps ``optparse.get_option_group``:: 261 262 def options(ctx): 263 gr = ctx.get_option_group('configure options') 264 gr.add_option('-o', '--out', action='store', default='', 265 help='build dir for the project', dest='out') 266 267 :rtype: optparse option group object 268 """ 269 try: 270 return self.option_groups[opt_str] 271 except KeyError: 272 for group in self.parser.option_groups: 273 if group.title == opt_str: 274 return group 275 return None 276 277 def sanitize_path(self, path, cwd=None): 278 if not cwd: 279 cwd = Context.launch_dir 280 p = os.path.expanduser(path) 281 p = os.path.join(cwd, p) 282 p = os.path.normpath(p) 283 p = os.path.abspath(p) 284 return p 285 286 def parse_cmd_args(self, _args=None, cwd=None, allow_unknown=False): 287 """ 288 Just parse the arguments 289 """ 290 self.parser.allow_unknown = allow_unknown 291 (options, leftover_args) = self.parser.parse_args(args=_args) 292 envvars = [] 293 commands = [] 294 for arg in leftover_args: 295 if '=' in arg: 296 envvars.append(arg) 297 elif arg != 'options': 298 commands.append(arg) 299 300 if options.jobs < 1: 301 options.jobs = 1 302 for name in 'top out destdir prefix bindir libdir'.split(): 303 # those paths are usually expanded from Context.launch_dir 304 if getattr(options, name, None): 305 path = self.sanitize_path(getattr(options, name), cwd) 306 setattr(options, name, path) 307 return options, commands, envvars 308 309 def init_module_vars(self, arg_options, arg_commands, arg_envvars): 310 options.__dict__.clear() 311 del commands[:] 312 del envvars[:] 313 314 options.__dict__.update(arg_options.__dict__) 315 commands.extend(arg_commands) 316 envvars.extend(arg_envvars) 317 318 for var in envvars: 319 (name, value) = var.split('=', 1) 320 os.environ[name.strip()] = value 321 322 def init_logs(self, options, commands, envvars): 323 Logs.verbose = options.verbose 324 if options.verbose >= 1: 325 self.load('errcheck') 326 327 colors = {'yes' : 2, 'auto' : 1, 'no' : 0}[options.colors] 328 Logs.enable_colors(colors) 329 330 if options.zones: 331 Logs.zones = options.zones.split(',') 332 if not Logs.verbose: 333 Logs.verbose = 1 334 elif Logs.verbose > 0: 335 Logs.zones = ['runner'] 336 if Logs.verbose > 2: 337 Logs.zones = ['*'] 338 339 def parse_args(self, _args=None): 340 """ 341 Parses arguments from a list which is not necessarily the command-line. 342 Initializes the module variables options, commands and envvars 343 If help is requested, prints it and exit the application 344 345 :param _args: arguments 346 :type _args: list of strings 347 """ 348 options, commands, envvars = self.parse_cmd_args() 349 self.init_logs(options, commands, envvars) 350 self.init_module_vars(options, commands, envvars) 351 352 def execute(self): 353 """ 354 See :py:func:`waflib.Context.Context.execute` 355 """ 356 super(OptionsContext, self).execute() 357 self.parse_args() 358 Utils.alloc_process_pool(options.jobs) 359 360