fix-copyright-years 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Copyright 2006 Free Software Foundation, Inc.
  4. #
  5. # GNU Radio is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 3, or (at your option)
  8. # any later version.
  9. #
  10. # GNU Radio is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with GNU Radio; see the file COPYING. If not, write to
  17. # the Free Software Foundation, Inc., 51 Franklin Street,
  18. # Boston, MA 02110-1301, USA.
  19. # This file is based on code from GNU Radio.
  20. # https://github.com/balister/GNU-Radio/blob/master/dtools/bin/fix-copyright-years
  21. import re
  22. import datetime
  23. import subprocess
  24. import multiprocessing
  25. def command(*args): return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
  26. def is_free_software(lines):
  27. for line in lines[:50]:
  28. if 'This program is free software' in line: return True
  29. return False
  30. def get_copyright_lines(lines):
  31. start = -1
  32. end = -1
  33. for i, line in enumerate(lines[:20]):
  34. if line.startswith('# Copyright'):
  35. if start == -1: start = i
  36. end = i
  37. if start == -1: raise Exception
  38. return start, end
  39. def copyright_line(author, years):
  40. from_year = years.pop()
  41. to_year = from_year
  42. for year in years:
  43. if year > to_year: to_year = year
  44. elif year < from_year: from_year = year
  45. if from_year == to_year:
  46. return '# Copyright (C) %d %s\n' % (from_year, author)
  47. else:
  48. return '# Copyright (C) %d–%d %s\n' % (from_year, to_year, author)
  49. author_re = re.compile('\nAuthor: (.*)\nDate:.* (\d\d\d\d) ')
  50. def fix_co_years(files):
  51. for file in files:
  52. print file
  53. years = {}
  54. changed = False
  55. lines = open(file).readlines()
  56. if not is_free_software(lines):
  57. print ' is not Free Software'
  58. continue
  59. # extract authors and years from git log
  60. log = command('git', 'log', file)
  61. it = re.finditer(author_re, log)
  62. for match in it:
  63. author, year = match.groups()
  64. if author in years:
  65. years[author].add(int(year))
  66. else:
  67. years[author] = set([int(year)])
  68. # extract copyright beginning and end from
  69. try:
  70. start, end = get_copyright_lines(lines)
  71. except:
  72. print ' copyright lines not found'
  73. continue
  74. # only update or add existing authors, don't touch authors not in git log
  75. for author in years.keys():
  76. found = False
  77. for i in range(start, end + 1):
  78. if author in lines[i]:
  79. line = copyright_line(author, years[author])
  80. if line != lines[i]:
  81. lines[i] = line
  82. changed = True
  83. found = True
  84. continue
  85. if not found:
  86. lines.insert(end, copyright_line(author, years[author]))
  87. changed = True
  88. # write file
  89. if changed:
  90. # print ''.join(lines[:30])
  91. # print '------------------------------------------------------------------------'
  92. open(file, 'w').write(''.join(lines))
  93. if __name__ == "__main__":
  94. # get recursive list of files in the repo
  95. files = command('git', 'ls-tree', '--name-only', 'HEAD', '-r').splitlines()
  96. # files = ['contrib/campaignwiki/add-link.pl']
  97. # start n+1 processes to handle the files
  98. num_procs = multiprocessing.cpu_count()
  99. # num_procs = 1
  100. procs = [multiprocessing.Process(
  101. target=lambda *files: fix_co_years(files),
  102. args=files[num::num_procs],
  103. ) for num in range(num_procs)]
  104. map(multiprocessing.Process.start, procs)
  105. map(multiprocessing.Process.join, procs)