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.

322 lines
11 KiB

6 years ago
  1. """engine.SCons.Variables
  2. This file defines the Variables class that is used to add user-friendly
  3. customizable variables to an SCons build.
  4. """
  5. #
  6. # Copyright (c) 2001 - 2017 The SCons Foundation
  7. #
  8. # Permission is hereby granted, free of charge, to any person obtaining
  9. # a copy of this software and associated documentation files (the
  10. # "Software"), to deal in the Software without restriction, including
  11. # without limitation the rights to use, copy, modify, merge, publish,
  12. # distribute, sublicense, and/or sell copies of the Software, and to
  13. # permit persons to whom the Software is furnished to do so, subject to
  14. # the following conditions:
  15. #
  16. # The above copyright notice and this permission notice shall be included
  17. # in all copies or substantial portions of the Software.
  18. #
  19. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  20. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  21. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  23. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  24. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  25. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26. __revision__ = "src/engine/SCons/Variables/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
  27. import os.path
  28. import sys
  29. import SCons.Environment
  30. import SCons.Errors
  31. import SCons.Util
  32. import SCons.Warnings
  33. from .BoolVariable import BoolVariable # okay
  34. from .EnumVariable import EnumVariable # okay
  35. from .ListVariable import ListVariable # naja
  36. from .PackageVariable import PackageVariable # naja
  37. from .PathVariable import PathVariable # okay
  38. class Variables(object):
  39. instance=None
  40. """
  41. Holds all the options, updates the environment with the variables,
  42. and renders the help text.
  43. """
  44. def __init__(self, files=None, args=None, is_global=1):
  45. """
  46. files - [optional] List of option configuration files to load
  47. (backward compatibility) If a single string is passed it is
  48. automatically placed in a file list
  49. """
  50. # initialize arguments
  51. if files is None:
  52. files = []
  53. if args is None:
  54. args = {}
  55. self.options = []
  56. self.args = args
  57. if not SCons.Util.is_List(files):
  58. if files:
  59. files = [ files ]
  60. else:
  61. files = []
  62. self.files = files
  63. self.unknown = {}
  64. # create the singleton instance
  65. if is_global:
  66. self=Variables.instance
  67. if not Variables.instance:
  68. Variables.instance=self
  69. def _do_add(self, key, help="", default=None, validator=None, converter=None):
  70. class Variable(object):
  71. pass
  72. option = Variable()
  73. # if we get a list or a tuple, we take the first element as the
  74. # option key and store the remaining in aliases.
  75. if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key):
  76. option.key = key[0]
  77. option.aliases = key[1:]
  78. else:
  79. option.key = key
  80. option.aliases = [ key ]
  81. option.help = help
  82. option.default = default
  83. option.validator = validator
  84. option.converter = converter
  85. self.options.append(option)
  86. # options might be added after the 'unknown' dict has been set up,
  87. # so we remove the key and all its aliases from that dict
  88. for alias in list(option.aliases) + [ option.key ]:
  89. if alias in self.unknown:
  90. del self.unknown[alias]
  91. def keys(self):
  92. """
  93. Returns the keywords for the options
  94. """
  95. return [o.key for o in self.options]
  96. def Add(self, key, help="", default=None, validator=None, converter=None, **kw):
  97. """
  98. Add an option.
  99. @param key: the name of the variable, or a list or tuple of arguments
  100. @param help: optional help text for the options
  101. @param default: optional default value
  102. @param validator: optional function that is called to validate the option's value
  103. @type validator: Called with (key, value, environment)
  104. @param converter: optional function that is called to convert the option's value before putting it in the environment.
  105. """
  106. if SCons.Util.is_List(key) or isinstance(key, tuple):
  107. self._do_add(*key)
  108. return
  109. if not SCons.Util.is_String(key) or \
  110. not SCons.Environment.is_valid_construction_var(key):
  111. raise SCons.Errors.UserError("Illegal Variables.Add() key `%s'" % str(key))
  112. self._do_add(key, help, default, validator, converter)
  113. def AddVariables(self, *optlist):
  114. """
  115. Add a list of options.
  116. Each list element is a tuple/list of arguments to be passed on
  117. to the underlying method for adding options.
  118. Example::
  119. opt.AddVariables(
  120. ('debug', '', 0),
  121. ('CC', 'The C compiler'),
  122. ('VALIDATE', 'An option for testing validation', 'notset',
  123. validator, None),
  124. )
  125. """
  126. for o in optlist:
  127. self._do_add(*o)
  128. def Update(self, env, args=None):
  129. """
  130. Update an environment with the option variables.
  131. env - the environment to update.
  132. """
  133. values = {}
  134. # first set the defaults:
  135. for option in self.options:
  136. if not option.default is None:
  137. values[option.key] = option.default
  138. # next set the value specified in the options file
  139. for filename in self.files:
  140. if os.path.exists(filename):
  141. dir = os.path.split(os.path.abspath(filename))[0]
  142. if dir:
  143. sys.path.insert(0, dir)
  144. try:
  145. values['__name__'] = filename
  146. with open(filename, 'r') as f:
  147. contents = f.read()
  148. exec(contents, {}, values)
  149. finally:
  150. if dir:
  151. del sys.path[0]
  152. del values['__name__']
  153. # set the values specified on the command line
  154. if args is None:
  155. args = self.args
  156. for arg, value in args.items():
  157. added = False
  158. for option in self.options:
  159. if arg in list(option.aliases) + [ option.key ]:
  160. values[option.key] = value
  161. added = True
  162. if not added:
  163. self.unknown[arg] = value
  164. # put the variables in the environment:
  165. # (don't copy over variables that are not declared as options)
  166. for option in self.options:
  167. try:
  168. env[option.key] = values[option.key]
  169. except KeyError:
  170. pass
  171. # Call the convert functions:
  172. for option in self.options:
  173. if option.converter and option.key in values:
  174. value = env.subst('${%s}'%option.key)
  175. try:
  176. try:
  177. env[option.key] = option.converter(value)
  178. except TypeError:
  179. env[option.key] = option.converter(value, env)
  180. except ValueError as x:
  181. raise SCons.Errors.UserError('Error converting option: %s\n%s'%(option.key, x))
  182. # Finally validate the values:
  183. for option in self.options:
  184. if option.validator and option.key in values:
  185. option.validator(option.key, env.subst('${%s}'%option.key), env)
  186. def UnknownVariables(self):
  187. """
  188. Returns any options in the specified arguments lists that
  189. were not known, declared options in this object.
  190. """
  191. return self.unknown
  192. def Save(self, filename, env):
  193. """
  194. Saves all the options in the given file. This file can
  195. then be used to load the options next run. This can be used
  196. to create an option cache file.
  197. filename - Name of the file to save into
  198. env - the environment get the option values from
  199. """
  200. # Create the file and write out the header
  201. try:
  202. fh = open(filename, 'w')
  203. try:
  204. # Make an assignment in the file for each option
  205. # within the environment that was assigned a value
  206. # other than the default.
  207. for option in self.options:
  208. try:
  209. value = env[option.key]
  210. try:
  211. prepare = value.prepare_to_store
  212. except AttributeError:
  213. try:
  214. eval(repr(value))
  215. except KeyboardInterrupt:
  216. raise
  217. except:
  218. # Convert stuff that has a repr() that
  219. # cannot be evaluated into a string
  220. value = SCons.Util.to_String(value)
  221. else:
  222. value = prepare()
  223. defaultVal = env.subst(SCons.Util.to_String(option.default))
  224. if option.converter:
  225. defaultVal = option.converter(defaultVal)
  226. if str(env.subst('${%s}' % option.key)) != str(defaultVal):
  227. fh.write('%s = %s\n' % (option.key, repr(value)))
  228. except KeyError:
  229. pass
  230. finally:
  231. fh.close()
  232. except IOError as x:
  233. raise SCons.Errors.UserError('Error writing options to file: %s\n%s' % (filename, x))
  234. def GenerateHelpText(self, env, sort=None):
  235. """
  236. Generate the help text for the options.
  237. env - an environment that is used to get the current values
  238. of the options.
  239. """
  240. if sort:
  241. options = sorted(self.options, key=lambda x: x.key)
  242. else:
  243. options = self.options
  244. def format(opt, self=self, env=env):
  245. if opt.key in env:
  246. actual = env.subst('${%s}' % opt.key)
  247. else:
  248. actual = None
  249. return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases)
  250. lines = [_f for _f in map(format, options) if _f]
  251. return ''.join(lines)
  252. format = '\n%s: %s\n default: %s\n actual: %s\n'
  253. format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n'
  254. def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]):
  255. # Don't display the key name itself as an alias.
  256. aliases = [a for a in aliases if a != key]
  257. if len(aliases)==0:
  258. return self.format % (key, help, default, actual)
  259. else:
  260. return self.format_ % (key, help, default, actual, aliases)
  261. # Local Variables:
  262. # tab-width:4
  263. # indent-tabs-mode:nil
  264. # End:
  265. # vim: set expandtab tabstop=4 shiftwidth=4: