jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

360 lines
12KB

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