saveVersion.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #! /usr/bin/python
  2. # Licensed to the Apache Software Foundation (ASF) under one or more
  3. # contributor license agreements. See the NOTICE file distributed with
  4. # this work for additional information regarding copyright ownership.
  5. # The ASF licenses this file to You under the Apache License, Version 2.0
  6. # (the "License"); you may not use this file except in compliance with
  7. # the License. You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. # This file is used to generate package-info.java with an annotation that
  17. # records the version, revision, user, date, url, and srcChecksum
  18. import os
  19. import sys
  20. import re
  21. import subprocess
  22. import getpass
  23. import time
  24. import hashlib
  25. def usage():
  26. print >>sys.stderr, "Usage: saveVersion.py <annotation> <version> <src_checksum_root_dir> <package> <build_dir>"
  27. print >>sys.stderr, "Eg: saveVersion.py @HadoopVersionAnnotation 1.1.0 src org.apache.hadoop build"
  28. # This template is what gets written to package-info.java.
  29. # It is filled out by (annotation, version, revision, user, date, url,
  30. # srcChecksum, package)
  31. template_string = \
  32. '''/*
  33. * Generated by saveVersion.py
  34. */
  35. %s(version="%s", revision="%s", branch="%s",
  36. user="%s", date="%s", url="%s",
  37. srcChecksum="%s")
  38. package %s;
  39. '''
  40. # Convert Windows paths with back-slashes into canonical Unix paths
  41. # with forward slashes. Unix paths are unchanged. Mixed formats
  42. # just naively have back-slashes converted to forward slashes.
  43. def canonicalpath(path):
  44. return re.sub('\\\\','/',path)
  45. # This is an implementation of md5sum-like functionality in pure python.
  46. # The path name is output in canonical form to get the same aggregate
  47. # checksum as Linux.
  48. def md5sum(fname):
  49. block_size = 2**20
  50. f = open(os.path.normpath(fname), 'rb') # open in binary mode to make sure EOLs don't get munged
  51. md5 = hashlib.md5()
  52. while True:
  53. data = f.read(block_size)
  54. if not data:
  55. break
  56. md5.update(data)
  57. f.close()
  58. return md5.hexdigest()
  59. # Roughly emulate the Python 2.7 functionality of "subprocess.check_output()"
  60. # by accepting a CLI vector [cmd, arg1, arg2, ...], and returning its stdout result,
  61. # but without the "checking". For use in lower version Python interpreters.
  62. def subprocessOutput(args):
  63. # result = subprocess.check_output(args, shell=True) # requires py2.7
  64. process = subprocess.Popen(args, stdout=subprocess.PIPE)
  65. result = process.communicate()[0].strip()
  66. return result
  67. # Strip surrounding quotes from a string object that carried its quotes with it.
  68. def stripquotes(s):
  69. if (s[0] == '"' and s[-1] == '"') or (s[0] == "'" and s[-1] == "'") :
  70. return s[1:-1]
  71. elif (s[0] == '\\' and s[-1] == '"') :
  72. return s[1:-1]
  73. else:
  74. return s
  75. def main(argv=None):
  76. if argv is None:
  77. argv = sys.argv[1:]
  78. if (len(argv) != 5) or (argv[0] == "--help") or (argv[0] == "-h"):
  79. usage()
  80. return -1
  81. annotation = argv[0]
  82. version = argv[1]
  83. src_checksum_root_dir = argv[2]
  84. package = argv[3]
  85. build_dir = argv[4]
  86. user = getpass.getuser()
  87. date = time.strftime("%a %b %d %H:%M:%S %Z %Y") # simulate `date`
  88. os.chdir(os.path.normpath(os.path.join(os.path.dirname(sys.argv[0]), "..")))
  89. if os.path.isdir(".git"):
  90. revision = stripquotes(subprocessOutput(['git', 'log', '-1', '--pretty=format:"%H"']))
  91. origin = subprocessOutput(['git', 'config', '--get', 'remote.origin.url'])
  92. branch = subprocessOutput(['git', 'branch'])
  93. filter_current_branch = re.compile(r'^\* (.*)$', re.MULTILINE)
  94. branch = filter_current_branch.search(branch).group(1).strip()
  95. url = "%s on branch %s" % (origin, branch)
  96. else:
  97. svn_info = subprocessOutput(['svn', 'info'])
  98. filter_last_revision = re.compile(r'^Last Changed Rev: (.*)$', re.MULTILINE)
  99. revision = filter_last_revision.search(svn_info).group(1).strip()
  100. filter_url = re.compile(r'^URL: (.*)$', re.MULTILINE)
  101. url = filter_url.search(svn_info).group(1).strip()
  102. # Get canonical branch (branches/X, tags/X, or trunk)
  103. filter_current_branch = re.compile(r'.*((branches/.*$)|(tags/.*$)|.*(trunk)$)')
  104. branch = filter_current_branch.search(url).group(1).strip()
  105. filter_java = re.compile(r'.+\.java$')
  106. file_list = []
  107. for root, dirs, files in os.walk(os.path.normpath(src_checksum_root_dir)):
  108. for name in files:
  109. if filter_java.match(name):
  110. canonical_name = canonicalpath(os.path.join(root, name))
  111. if not 'generated-sources' in canonical_name:
  112. file_list.append(canonical_name)
  113. # Sorting is done on unix-format names, case-folded, in order to get a platform-independent sort.
  114. file_list.sort(key=str.upper)
  115. file_count = len(file_list)
  116. hash = hashlib.md5()
  117. for name in file_list:
  118. file_hash_string = md5sum(name)
  119. hash.update(file_hash_string)
  120. srcChecksum = hash.hexdigest()
  121. target_dir = os.path.normpath(build_dir)
  122. if not os.path.exists(target_dir):
  123. os.makedirs(target_dir)
  124. target_file = os.path.join(target_dir, 'package-info.java')
  125. fout = open(target_file, "w")
  126. fout.write(template_string % (annotation, version, revision, branch, user, date, url, srcChecksum, package))
  127. fout.close()
  128. print("Checksummed %s src/**.java files" % file_count)
  129. return 0
  130. ##########################
  131. if __name__ == "__main__":
  132. sys.exit(main())