waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

Options.py (11958B)


      1 #!/usr/bin/env python
      2 # encoding: utf-8
      3 # Scott Newton, 2005 (scottn)
      4 # Thomas Nagy, 2006-2018 (ita)
      5 
      6 """
      7 Support for waf command-line options
      8 
      9 Provides default and command-line options, as well the command
     10 that reads the ``options`` wscript function.
     11 """
     12 
     13 import os, tempfile, optparse, sys, re
     14 from waflib import Logs, Utils, Context, Errors
     15 
     16 options = optparse.Values()
     17 """
     18 A global dictionary representing user-provided command-line options::
     19 
     20 	$ waf --foo=bar
     21 """
     22 
     23 commands = []
     24 """
     25 List of commands to execute extracted from the command-line. This list
     26 is consumed during the execution by :py:func:`waflib.Scripting.run_commands`.
     27 """
     28 
     29 envvars = []
     30 """
     31 List of environment variable declarations placed after the Waf executable name.
     32 These are detected by searching for "=" in the remaining arguments.
     33 You probably do not want to use this.
     34 """
     35 
     36 lockfile = os.environ.get('WAFLOCK', '.lock-waf_%s_build' % sys.platform)
     37 """
     38 Name of the lock file that marks a project as configured
     39 """
     40 
     41 class 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 
    116 Main commands (example: ./%s build -j4)
    117 %s
    118 ''' % (Context.WAFNAME, Context.WAFNAME, ret)
    119 
    120 
    121 class 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