PageRenderTime 27ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/scripts/convert-ly.py

#
Python | 390 lines | 357 code | 10 blank | 23 comment | 6 complexity | 431005d4f87d1660f25ac2cca43c404f MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-4.0, MIT
  1. #!@TARGET_PYTHON@
  2. # convert-ly.py -- Update old LilyPond input files (fix name?)
  3. # converting rules are found in python/convertrules.py
  4. # This file is part of LilyPond, the GNU music typesetter.
  5. #
  6. # Copyright (C) 1998--2014 Han-Wen Nienhuys <hanwen@xs4all.nl>
  7. # Jan Nieuwenhuizen <janneke@gnu.org>
  8. #
  9. # LilyPond is free software: you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License as published by
  11. # the Free Software Foundation, either version 3 of the License, or
  12. # (at your option) any later version.
  13. #
  14. # LilyPond is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License
  20. # along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
  21. import os
  22. import sys
  23. import re
  24. import shutil
  25. """
  26. @relocate-preamble@
  27. """
  28. import lilylib as ly
  29. global _;_=ly._
  30. ly.require_python_version ()
  31. import convertrules
  32. lilypond_version_re_str = '\\\\version *\"([0-9.]+)"'
  33. lilypond_version_re = re.compile (lilypond_version_re_str)
  34. lilypond_version_strict_re_str = '\\\\version *\"([0-9]+[.][0-9]+[.][0-9]+)"'
  35. lilypond_version_strict_re = re.compile (lilypond_version_strict_re_str)
  36. help_summary = (
  37. _ ('''Update LilyPond input to newer version. By default, update from the
  38. version taken from the \\version command, to the current LilyPond version.''')
  39. + "\n"
  40. + _ ("If FILE is `-', read from standard input.")
  41. + "\n\n"
  42. + _ ("Examples:")
  43. + '''
  44. $ convert-ly -e old.ly
  45. $ convert-ly --from=2.3.28 --to=2.5.21 foobar.ly > foobar-new.ly
  46. ''')
  47. copyright = ('Jan Nieuwenhuizen <janneke@gnu.org>',
  48. 'Han-Wen Nienhuys <hanwen@xs4all.nl>')
  49. program_name = os.path.basename (sys.argv[0])
  50. program_version = '@TOPLEVEL_VERSION@'
  51. authors = ('Jan Nieuwenhuizen <janneke@gnu.org>',
  52. 'Han-Wen Nienhuys <hanwen@xs4all.nl>')
  53. def identify ():
  54. ly.progress ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
  55. def warranty ():
  56. identify ()
  57. ly.encoded_write (sys.stdout, '''
  58. %s
  59. %s
  60. %s
  61. %s
  62. ''' % ( _ ('Copyright (c) %s by') % '2001--2014',
  63. ' '.join (authors),
  64. _ ('Distributed under terms of the GNU General Public License.'),
  65. _ ('It comes with NO WARRANTY.')))
  66. def get_option_parser ():
  67. p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'convert-ly',
  68. description=help_summary,
  69. add_help_option=False)
  70. p.version="@TOPLEVEL_VERSION@"
  71. p.add_option("--version",
  72. action="version",
  73. help=_ ("show version number and exit"))
  74. p.add_option("-h", "--help",
  75. action="help",
  76. help=_ ("show this help and exit"))
  77. p.add_option ('-f', '--from',
  78. action="store",
  79. metavar=_ ("VERSION"),
  80. dest="from_version",
  81. help=_ ("start from VERSION [default: \\version found in file]"),
  82. default='')
  83. p.add_option ('-e', '--edit', help=_ ("edit in place"),
  84. action='store_true')
  85. p.add_option ("-l", "--loglevel",
  86. help=_ ("Print log messages according to LOGLEVEL "
  87. "(NONE, ERROR, WARNING, PROGRESS (default), DEBUG)"),
  88. metavar=_ ("LOGLEVEL"),
  89. action='callback',
  90. callback=ly.handle_loglevel_option,
  91. type='string')
  92. p.add_option ('-n', '--no-version',
  93. help=_ ("do not add \\version command if missing"),
  94. action='store_true',
  95. dest='skip_version_add',
  96. default=False)
  97. p.add_option ('-c', '--current-version',
  98. help=_ ("force updating \\version number to %s") % program_version,
  99. action='store_true',
  100. dest='force_current_version',
  101. default=False)
  102. p.add_option ('-d', '--diff-version-update',
  103. help=_ ("only update \\version number if file is modified"),
  104. action='store_true',
  105. dest='diff_version_update',
  106. default=False)
  107. p.add_option ("-s", '--show-rules',
  108. help=_ ("show rules [default: -f 0, -t %s]") % program_version,
  109. dest='show_rules',
  110. action='store_true', default=False)
  111. p.add_option ('-t', '--to',
  112. help=_ ("convert to VERSION [default: %s]") % program_version,
  113. metavar=_ ('VERSION'),
  114. action='store',
  115. dest="to_version",
  116. default='')
  117. p.add_option ('-b', '--backup-numbered',
  118. help=_ ("make a numbered backup [default: filename.ext~]"),
  119. action='store_true',
  120. dest="backup_numbered",
  121. default='')
  122. p.add_option ('-w', '--warranty', help=_ ("show warranty and copyright"),
  123. action='store_true',
  124. ),
  125. p.add_option_group ('',
  126. description=(
  127. _ ("Report bugs via %s")
  128. % 'http://post.gmane.org/post.php'
  129. '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
  130. return p
  131. def str_to_tuple (s):
  132. return tuple ([int(n) for n in s.split ('.')])
  133. def tup_to_str (t):
  134. return '.'.join (['%s' % x for x in t])
  135. def version_cmp (t1, t2):
  136. for x in [0, 1, 2]:
  137. if t1[x] - t2[x]:
  138. return t1[x] - t2[x]
  139. return 0
  140. def get_conversions (from_version, to_version):
  141. def is_applicable (v, f = from_version, t = to_version):
  142. return version_cmp (v[0], f) > 0 and version_cmp (v[0], t) <= 0
  143. return filter (is_applicable, convertrules.conversions)
  144. def latest_version ():
  145. return convertrules.conversions[-1][0]
  146. def show_rules (file, from_version, to_version):
  147. for x in convertrules.conversions:
  148. if (not from_version or x[0] > from_version) \
  149. and (not to_version or x[0] <= to_version):
  150. ly.encoded_write (file, '%s: %s\n' % (tup_to_str (x[0]), x[2]))
  151. def do_conversion (str, from_version, to_version):
  152. """Apply conversions from FROM_VERSION to TO_VERSION. Return
  153. tuple (LAST,LASTCHANGED,STR,ERRORS), with the last applied conversion,
  154. the last conversion resulting in a change, the resulting
  155. string and the number of errors."""
  156. conv_list = get_conversions (from_version, to_version)
  157. ly.progress (_ ("Applying conversion: "), newline = False)
  158. last_conversion = None
  159. last_change = None
  160. errors = 0
  161. try:
  162. for x in conv_list:
  163. if x != conv_list[-1]:
  164. ly.progress (tup_to_str (x[0]), newline = False)
  165. ly.progress (', ', newline = False)
  166. else:
  167. ly.progress (tup_to_str (x[0]))
  168. newstr = x[1] (str)
  169. last_conversion = x[0]
  170. if (newstr != str):
  171. last_change = last_conversion
  172. str = newstr
  173. except convertrules.FatalConversionError:
  174. ly.error (_ ("Error while converting")
  175. + '\n'
  176. + _ ("Stopping at last successful rule"))
  177. errors += 1
  178. return (last_conversion, last_change, str, errors)
  179. def guess_lilypond_version (input):
  180. m = lilypond_version_strict_re.search (input)
  181. if m:
  182. return m.group (1)
  183. m = lilypond_version_re.search (input)
  184. if m:
  185. raise InvalidVersion (m.group (1))
  186. else:
  187. return ''
  188. class FatalConversionError (Exception):
  189. pass
  190. class UnknownVersion (Exception):
  191. pass
  192. class InvalidVersion (Exception):
  193. def __init__ (self, version):
  194. self.version = version
  195. def back_up (file, numbered):
  196. if numbered:
  197. n = 0
  198. while True:
  199. n = n + 1
  200. back_up = file + '.~' + str(n) + '~'
  201. if not os.path.exists (back_up):
  202. break
  203. else:
  204. back_up = file + '~'
  205. shutil.copy2 (file, back_up)
  206. return back_up
  207. def do_one_file (infile_name):
  208. ly.progress (_ (u"Processing `%s\'... ") % infile_name, True)
  209. if infile_name:
  210. infile = open (infile_name, 'r')
  211. input = infile.read ()
  212. infile.close ()
  213. else:
  214. input = sys.stdin.read ()
  215. from_version = None
  216. to_version = None
  217. if global_options.from_version:
  218. from_version = global_options.from_version
  219. else:
  220. guess = guess_lilypond_version (input)
  221. if not guess:
  222. raise UnknownVersion ()
  223. from_version = str_to_tuple (guess)
  224. if global_options.to_version:
  225. to_version = global_options.to_version
  226. else:
  227. to_version = latest_version ()
  228. if len (from_version) != 3:
  229. raise InvalidVersion (".".join ([str(n) for n in from_version]))
  230. (last, last_change, result, errors) = \
  231. do_conversion (input, from_version, to_version)
  232. if global_options.force_current_version and \
  233. (last is None or last == to_version):
  234. last = str_to_tuple (program_version)
  235. if last:
  236. if global_options.diff_version_update:
  237. # Note that last_change can be set even if the result is
  238. # the same if two conversion rules cancelled out
  239. if result == input:
  240. # make no (actual) change to the version number
  241. last = from_version
  242. else:
  243. last = last_change
  244. # If the last update was to an unstable version
  245. # number, and the final update target is no longer in
  246. # the same unstable series, we update to the stable
  247. # series following the unstable version.
  248. if last[1]%2: # unstable
  249. next_stable = (last[0], last[1]+1, 0)
  250. if next_stable <= to_version:
  251. last = next_stable
  252. newversion = r'\version "%s"' % tup_to_str (last)
  253. if lilypond_version_re.search (result):
  254. result = re.sub (lilypond_version_re_str,
  255. '\\' + newversion, result)
  256. elif not global_options.skip_version_add:
  257. result = newversion + '\n' + result
  258. ly.progress ('\n')
  259. if global_options.edit:
  260. backup = back_up (infile_name, global_options.backup_numbered)
  261. outfile = open (infile_name, 'w')
  262. else:
  263. outfile = sys.stdout
  264. outfile.write (result)
  265. sys.stderr.flush ()
  266. return errors
  267. def do_options ():
  268. opt_parser = get_option_parser()
  269. (options, args) = opt_parser.parse_args ()
  270. if options.warranty:
  271. warranty ()
  272. sys.exit (0)
  273. if options.from_version:
  274. options.from_version = str_to_tuple (options.from_version)
  275. if options.to_version:
  276. options.to_version = str_to_tuple (options.to_version)
  277. options.outfile_name = ''
  278. global global_options
  279. global_options = options
  280. if not args and not options.show_rules:
  281. opt_parser.print_help ()
  282. sys.exit (2)
  283. return args
  284. def main ():
  285. files = do_options ()
  286. # should parse files[] to read \version?
  287. if global_options.show_rules:
  288. show_rules (sys.stdout, global_options.from_version, global_options.to_version)
  289. sys.exit (0)
  290. identify ()
  291. errors = 0
  292. for f in files:
  293. f = f.decode (sys.stdin.encoding or "utf-8")
  294. if f == '-':
  295. f = ''
  296. elif not os.path.isfile (f):
  297. ly.error (_ (u"%s: Unable to open file") % f)
  298. errors += 1
  299. continue
  300. try:
  301. errors += do_one_file (f)
  302. except UnknownVersion:
  303. ly.error (_ (u"%s: Unable to determine version. Skipping") % f)
  304. errors += 1
  305. except InvalidVersion:
  306. # Compat code for 2.x and 3.0 syntax ("except .. as v" doesn't
  307. # work in python 2.4!):
  308. t, v, b = sys.exc_info ()
  309. ly.error (_ (u"%s: Invalid version string `%s' \n"
  310. "Valid version strings consist of three numbers, "
  311. "separated by dots, e.g. `2.8.12'") % (f, v.version) )
  312. errors += 1
  313. if errors:
  314. ly.warning (ly.ungettext ("There was %d error.",
  315. "There were %d errors.", errors) % errors)
  316. sys.exit (1)
  317. main ()