PageRenderTime 939ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/Code/Tools/waf-1.7.13/waflib/extras/review.py

https://gitlab.com/dahbearz/CRYENGINE
Python | 328 lines | 264 code | 27 blank | 37 comment | 22 complexity | fcac83cda82ed5651eb08e34e2950253 MD5 | raw file
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Laurent Birtz, 2011
  4. # moved the code into a separate tool (ita)
  5. """
  6. There are several things here:
  7. - a different command-line option management making options persistent
  8. - the review command to display the options set
  9. Assumptions:
  10. - configuration options are not always added to the right group (and do not count on the users to do it...)
  11. - the options are persistent between the executions (waf options are NOT persistent by design), even for the configuration
  12. - when the options change, the build is invalidated (forcing a reconfiguration)
  13. """
  14. import os, textwrap, shutil
  15. from waflib import Logs, Context, ConfigSet, Options, Build, Configure
  16. class Odict(dict):
  17. """Ordered dictionary"""
  18. def __init__(self, data=None):
  19. self._keys = []
  20. dict.__init__(self)
  21. if data:
  22. # we were provided a regular dict
  23. if isinstance(data, dict):
  24. self.append_from_dict(data)
  25. # we were provided a tuple list
  26. elif type(data) == list:
  27. self.append_from_plist(data)
  28. # we were provided invalid input
  29. else:
  30. raise Exception("expected a dict or a tuple list")
  31. def append_from_dict(self, dict):
  32. map(self.__setitem__, dict.keys(), dict.values())
  33. def append_from_plist(self, plist):
  34. for pair in plist:
  35. if len(pair) != 2:
  36. raise Exception("invalid pairs list")
  37. for (k, v) in plist:
  38. self.__setitem__(k, v)
  39. def __delitem__(self, key):
  40. if not key in self._keys:
  41. raise KeyError(key)
  42. dict.__delitem__(self, key)
  43. self._keys.remove(key)
  44. def __setitem__(self, key, item):
  45. dict.__setitem__(self, key, item)
  46. if key not in self._keys:
  47. self._keys.append(key)
  48. def clear(self):
  49. dict.clear(self)
  50. self._keys = []
  51. def copy(self):
  52. return Odict(self.plist())
  53. def items(self):
  54. return zip(self._keys, self.values())
  55. def keys(self):
  56. return list(self._keys) # return a copy of the list
  57. def values(self):
  58. return map(self.get, self._keys)
  59. def plist(self):
  60. p = []
  61. for k, v in self.items():
  62. p.append( (k, v) )
  63. return p
  64. def __str__(self):
  65. s = "{"
  66. l = len(self._keys)
  67. for k, v in self.items():
  68. l -= 1
  69. strkey = str(k)
  70. if isinstance(k, basestring): strkey = "'"+strkey+"'"
  71. strval = str(v)
  72. if isinstance(v, basestring): strval = "'"+strval+"'"
  73. s += strkey + ":" + strval
  74. if l > 0: s += ", "
  75. s += "}"
  76. return s
  77. review_options = Odict()
  78. """
  79. Ordered dictionary mapping configuration option names to their optparse option.
  80. """
  81. review_defaults = {}
  82. """
  83. Dictionary mapping configuration option names to their default value.
  84. """
  85. old_review_set = None
  86. """
  87. Review set containing the configuration values before parsing the command line.
  88. """
  89. new_review_set = None
  90. """
  91. Review set containing the configuration values after parsing the command line.
  92. """
  93. class OptionsReview(Options.OptionsContext):
  94. def __init__(self, **kw):
  95. super(self.__class__, self).__init__(**kw)
  96. def prepare_config_review(self):
  97. """
  98. Find the configuration options that are reviewable, detach
  99. their default value from their optparse object and store them
  100. into the review dictionaries.
  101. """
  102. gr = self.get_option_group('configure options')
  103. for opt in gr.option_list:
  104. if opt.action != 'store' or opt.dest in ("out", "top"):
  105. continue
  106. review_options[opt.dest] = opt
  107. review_defaults[opt.dest] = opt.default
  108. if gr.defaults.has_key(opt.dest):
  109. del gr.defaults[opt.dest]
  110. opt.default = None
  111. def parse_args(self):
  112. self.prepare_config_review()
  113. self.parser.get_option('--prefix').help = 'installation prefix'
  114. super(OptionsReview, self).parse_args()
  115. Context.create_context('review').refresh_review_set()
  116. class ReviewContext(Context.Context):
  117. '''reviews the configuration values'''
  118. cmd = 'review'
  119. def __init__(self, **kw):
  120. super(self.__class__, self).__init__(**kw)
  121. out = Options.options.out
  122. if not out:
  123. out = getattr(Context.g_module, Context.OUT, None)
  124. if not out:
  125. out = Options.lockfile.replace('.lock-waf', '')
  126. self.build_path = (os.path.isabs(out) and self.root or self.path).make_node(out).abspath()
  127. """Path to the build directory"""
  128. self.cache_path = os.path.join(self.build_path, Build.CACHE_DIR)
  129. """Path to the cache directory"""
  130. self.review_path = os.path.join(self.cache_path, 'review.cache')
  131. """Path to the review cache file"""
  132. def execute(self):
  133. """
  134. Display and store the review set. Invalidate the cache as required.
  135. """
  136. if not self.compare_review_set(old_review_set, new_review_set):
  137. self.invalidate_cache()
  138. self.store_review_set(new_review_set)
  139. print(self.display_review_set(new_review_set))
  140. def invalidate_cache(self):
  141. """Invalidate the cache to prevent bad builds."""
  142. try:
  143. Logs.warn("Removing the cached configuration since the options have changed")
  144. shutil.rmtree(self.cache_path)
  145. except:
  146. pass
  147. def refresh_review_set(self):
  148. """
  149. Obtain the old review set and the new review set, and import the new set.
  150. """
  151. global old_review_set, new_review_set
  152. old_review_set = self.load_review_set()
  153. new_review_set = self.update_review_set(old_review_set)
  154. self.import_review_set(new_review_set)
  155. def load_review_set(self):
  156. """
  157. Load and return the review set from the cache if it exists.
  158. Otherwise, return an empty set.
  159. """
  160. if os.path.isfile(self.review_path):
  161. return ConfigSet.ConfigSet(self.review_path)
  162. return ConfigSet.ConfigSet()
  163. def store_review_set(self, review_set):
  164. """
  165. Store the review set specified in the cache.
  166. """
  167. if not os.path.isdir(self.cache_path):
  168. os.makedirs(self.cache_path)
  169. review_set.store(self.review_path)
  170. def update_review_set(self, old_set):
  171. """
  172. Merge the options passed on the command line with those imported
  173. from the previous review set and return the corresponding
  174. preview set.
  175. """
  176. # Convert value to string. It's important that 'None' maps to
  177. # the empty string.
  178. def val_to_str(val):
  179. if val == None or val == '':
  180. return ''
  181. return str(val)
  182. new_set = ConfigSet.ConfigSet()
  183. opt_dict = Options.options.__dict__
  184. for name in review_options.keys():
  185. # the option is specified explicitly on the command line
  186. if name in opt_dict:
  187. # if the option is the default, pretend it was never specified
  188. if val_to_str(opt_dict[name]) != val_to_str(review_defaults[name]):
  189. new_set[name] = opt_dict[name]
  190. # the option was explicitly specified in a previous command
  191. elif name in old_set:
  192. new_set[name] = old_set[name]
  193. return new_set
  194. def import_review_set(self, review_set):
  195. """
  196. Import the actual value of the reviewable options in the option
  197. dictionary, given the current review set.
  198. """
  199. for name in review_options.keys():
  200. if name in review_set:
  201. value = review_set[name]
  202. else:
  203. value = review_defaults[name]
  204. setattr(Options.options, name, value)
  205. def compare_review_set(self, set1, set2):
  206. """
  207. Return true if the review sets specified are equal.
  208. """
  209. if len(set1.keys()) != len(set2.keys()): return False
  210. for key in set1.keys():
  211. if not key in set2 or set1[key] != set2[key]:
  212. return False
  213. return True
  214. def display_review_set(self, review_set):
  215. """
  216. Return the string representing the review set specified.
  217. """
  218. term_width = Logs.get_term_cols()
  219. lines = []
  220. for dest in review_options.keys():
  221. opt = review_options[dest]
  222. name = ", ".join(opt._short_opts + opt._long_opts)
  223. help = opt.help
  224. actual = None
  225. if dest in review_set: actual = review_set[dest]
  226. default = review_defaults[dest]
  227. lines.append(self.format_option(name, help, actual, default, term_width))
  228. return "Configuration:\n\n" + "\n\n".join(lines) + "\n"
  229. def format_option(self, name, help, actual, default, term_width):
  230. """
  231. Return the string representing the option specified.
  232. """
  233. def val_to_str(val):
  234. if val == None or val == '':
  235. return "(void)"
  236. return str(val)
  237. max_name_len = 20
  238. sep_len = 2
  239. w = textwrap.TextWrapper()
  240. w.width = term_width - 1
  241. if w.width < 60: w.width = 60
  242. out = ""
  243. # format the help
  244. out += w.fill(help) + "\n"
  245. # format the name
  246. name_len = len(name)
  247. out += Logs.colors.CYAN + name + Logs.colors.NORMAL
  248. # set the indentation used when the value wraps to the next line
  249. w.subsequent_indent = " ".rjust(max_name_len + sep_len)
  250. w.width -= (max_name_len + sep_len)
  251. # the name string is too long, switch to the next line
  252. if name_len > max_name_len:
  253. out += "\n" + w.subsequent_indent
  254. # fill the remaining of the line with spaces
  255. else:
  256. out += " ".rjust(max_name_len + sep_len - name_len)
  257. # format the actual value, if there is one
  258. if actual != None:
  259. out += Logs.colors.BOLD + w.fill(val_to_str(actual)) + Logs.colors.NORMAL + "\n" + w.subsequent_indent
  260. # format the default value
  261. default_fmt = val_to_str(default)
  262. if actual != None:
  263. default_fmt = "default: " + default_fmt
  264. out += Logs.colors.NORMAL + w.fill(default_fmt) + Logs.colors.NORMAL
  265. return out
  266. # Monkey-patch ConfigurationContext.execute() to have it store the review set.
  267. old_configure_execute = Configure.ConfigurationContext.execute
  268. def new_configure_execute(self):
  269. old_configure_execute(self)
  270. Context.create_context('review').store_review_set(new_review_set)
  271. Configure.ConfigurationContext.execute = new_configure_execute