| The Gaudi Framework v27r1

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

prepare_gaudi_release.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 '''
3 Script to prepare the release of Gaudi.
4
5 @author Marco Clemencic
6 '''
7
8 import os
9 import sys
10 import logging
11 import re
12 import ConfigParser
13 from subprocess import Popen, PIPE
14
15 def checkGitVersion():
16 '''
17 Ensure we have a usable version of Git (>= 1.7.9.1).
18
19 See:
20 * https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/1.7.9.1.txt
21 * https://github.com/git/git/commit/36ed1913e1d5de0930e59db6eeec3ccb2bd58bd9
22 '''
23 proc = Popen(['git', '--version'], stdout=PIPE)
24 version = proc.communicate()[0].split()[-1]
25 if proc.returncode:
26 raise RuntimeError('could not get git version')
27 if versionKey(version) < versionKey('1.7.9.1'):
28 raise RuntimeError('bad version of git found: %s (1.7.9.1 required)' % version)
29
30 _VK_RE = re.compile(r'(\d+|\D+)')
31 def versionKey(x):
32 '''
33 Key function to be passes to list.sort() to sort strings
34 as version numbers.
35 '''
36 return [int(i) if i[0] in '0123456789' else i
37 for i in _VK_RE.split(x)
38 if i]
39
40 def findLatestTag():
41 '''
42 Return the latest Gaudi tag (of the format "GAUDI/GAUDI_*").
43 '''
44 logging.info('looking for latest tag')
45 cmd = ['git', 'tag']
46 logging.debug('using command %r', cmd)
47 proc = Popen(cmd, stdout=PIPE)
48 output = proc.communicate()[0]
49 tags = [tag
50 for tag in output.splitlines()
51 if tag.startswith('GAUDI/GAUDI_')]
52 if tags:
53 tags.sort(key=versionKey)
54 logging.info('found %s', tags[-1])
55 return tags[-1]
56 logging.info('no valid tag found')
57
58 def releaseNotes(path=os.curdir, from_tag=None, branch=None):
59 '''
60 Return the release notes (in the old LHCb format) extracted from git
61 commits for a given path.
62 '''
63 cmd = ['git', 'log',
64 '--first-parent', '--date=short',
65 '--pretty=format:! %ad - commit %h%n%n%w(80,1,3)- %s%n%n%b%n']
66 if from_tag:
67 cmd.append('{0}..{1}'.format(from_tag, branch or ''))
68 elif branch:
69 cmd.append(branch)
70 cmd.append('--')
71 cmd.append(path)
72 logging.info('preparing release notes for %s%s', path,
73 ' since ' + from_tag if from_tag else '')
74 logging.debug('using command %r', cmd)
75 proc = Popen(cmd, stdout=PIPE)
76 return proc.communicate()[0]
77
78 def updateReleaseNotes(path, notes):
79 '''
80 Smartly prepend the content of notes to the release.notes file in path.
81 '''
82 notes_filename = os.path.join(path, 'doc', 'release.notes')
83 logging.info('updating %s', notes_filename)
84 from itertools import takewhile, dropwhile
85 def dropuntil(predicate, iterable):
86 return dropwhile(lambda x: not predicate(x), iterable)
87 with open(notes_filename) as notes_file:
88 orig_data = iter(list(notes_file))
89 header = takewhile(str.strip, orig_data)
90 with open(notes_filename, 'w') as notes_file:
91 notes_file.writelines(header)
92 notes_file.write('\n')
93 notes_file.writelines(l.rstrip() + '\n' for l in notes.splitlines())
94 notes_file.writelines(dropuntil(re.compile(r'^!?============').match, orig_data))
95
96 # guess current version
97 _req_version_pattern = re.compile(r"^\s*version\s*(v[0-9]+r[0-9]+(?:p[0-9]+)?)\s*$")
98 _cml_version_pattern = re.compile(r"^\s*gaudi_subdir\s*\(\s*\S+\s+(v[0-9]+r[0-9]+(?:p[0-9]+)?)\)\s*$")
99 def extract_version(path):
100 """
101 Find the version number of a subdirectory.
102 """
103 global _cml_version_pattern
104 for l in open(os.path.join(path, 'CMakeLists.txt')):
105 m = _cml_version_pattern.match(l)
106 if m:
107 return m.group(1)
108 return None
109
110 def change_cml_version(cml, newversion):
111 if os.path.exists(cml):
112 out = []
113 changed = False
114 for l in open(cml):
115 m = _cml_version_pattern.match(l)
116 if m and m.group(1) != newversion:
117 logging.debug('%s: %s -> %s', cml, m.group(1), newversion)
118 l = l.replace(m.group(1), newversion)
119 changed = True
120 out.append(l)
121 if changed:
122 open(cml, "w").writelines(out)
123
124 def change_version(packagedir, newversion):
125 """
126 Compare the version of the package with the new one and update the package if
127 needed.
128
129 Returns true if the package have been modified.
130 """
131 global _req_version_pattern
132 changed = False
133 out = []
134 req = os.path.join(packagedir, 'cmt', 'requirements')
135 for l in open(req):
136 m = _req_version_pattern.match(l)
137 if m:
138 if m.group(1) != newversion:
139 logging.debug('%s: %s -> %s', packagedir, m.group(1), newversion)
140 l = l.replace(m.group(1),newversion)
141 changed = True
142 out.append(l)
143 if changed:
144 open(req,"w").writelines(out)
145 # verify the version.cmt file
146 ver = os.path.join(packagedir,"version.cmt")
147 if os.path.exists(ver):
148 current = open(ver).read().strip()
149 if current != newversion:
150 open(ver,"w").write(newversion + "\n")
151 # update CMakeLists.txt
152 cml = os.path.normpath(os.path.join(packagedir, 'CMakeLists.txt'))
153 change_cml_version(cml, newversion)
154 if 'GaudiKernel' in packagedir:
155 cml = os.path.normpath(os.path.join(packagedir, 'src', 'Util', 'CMakeLists.txt'))
156 change_cml_version(cml, newversion)
157 return changed
158
159 def tag_bar(pkg, version=None):
160 title = ' %s %s ' % (pkg, version)
161 letf_chars = (78 - len(title)) / 2
162 right_chars = 78 - letf_chars - len(title)
163 separator = ('=' * letf_chars) + title + ('=' * right_chars)
164 return separator
165
166 def main():
167 logging.basicConfig(level=logging.DEBUG)
168 os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
169 # Find the version of HEPTools (LCG)
170 for l in open('toolchain.cmake'):
171 m = re.match(r'^\s*set\(\s*heptools_version\s+(\S*)\s*\)', l)
172 if m:
173 HEPToolsVers = m.group(1)
174 print "Using HEPTools", HEPToolsVers
175 break
176 else:
177 logging.error('Cannot find HEPTools version')
178 sys.exit(1)
179
180 # Collect all the packages in the project with their directory
181 def all_subdirs():
182 for dirpath, dirnames, filenames in os.walk(os.curdir):
183 if 'CMakeLists.txt' in filenames and dirpath != os.curdir:
184 dirnames[:] = []
185 yield dirpath
186 else:
187 dirnames[:] = [dirname for dirname in dirnames
188 if not dirname.startswith('build.') and
189 dirname != 'cmake']
190
191 # Packages which version must match the version of the project
192 special_subdirs = ["Gaudi", "GaudiExamples", "GaudiSys", "GaudiRelease"]
193
194 # Ask for the version of the project
195 latest_tag = findLatestTag()
196
197 old_version = latest_tag.split('_')[-1]
198 new_version = raw_input("The old version of the project is %s, which is the new one? " % old_version)
199
200 old_versions = {}
201 release_notes = {}
202 new_versions = {}
203 # for each package in the project check if there were changes and ask for the new version number
204 for pkgdir in all_subdirs():
205 cmlfile = os.path.join(pkgdir, 'CMakeLists.txt')
206 reqfile = os.path.join(pkgdir, 'cmt', 'requirements')
207
208 pkg = os.path.basename(pkgdir)
209 old_vers = extract_version(pkgdir)
210
211 vers = None
212 if pkg in special_subdirs:
213 vers = new_version
214 else:
215 git_log = Popen(['git', 'log', '-m', '--first-parent',
216 '--stat', latest_tag + '..master', pkgdir],
217 stdout=PIPE).communicate()[0].strip()
218 if git_log:
219 msg = ('\nThe old version of {0} is {1}, these are the changes:\n'
220 '{2}\n'
221 'Which version you want (old is {1})? ').format(pkg,
222 old_vers,
223 git_log)
224 vers = raw_input(msg)
225 while not vers or vers == old_vers:
226 vers = raw_input('give me a version, please. ')
227 if vers:
228 change_version(pkgdir, vers)
229 updateReleaseNotes(pkgdir,
230 tag_bar(pkg, vers) + '\n\n' +
231 releaseNotes(pkgdir, latest_tag, 'master'))
232 new_versions[pkg] = vers or old_vers
233 print "=" * 80
234
235 # The changes in the GaudiRelease requirements for the other packages can be postponed to now
236 reqfile = os.path.join('GaudiRelease', 'cmt', 'requirements')
237 out = []
238 for l in open(reqfile):
239 sl = l.strip().split()
240 if sl and sl[0] == "use":
241 if sl[1] in new_versions:
242 if sl[2] != new_versions[sl[1]]:
243 l = l.replace(sl[2], new_versions[sl[1]])
244 out.append(l)
245 open(reqfile, "w").writelines(out)
246 # Update project.info
247 config = ConfigParser.ConfigParser()
248 config.optionxform = str # make the options case sensitive
249 if os.path.exists('project.info'):
250 config.read('project.info')
251 if not config.has_section('Packages'):
252 config.add_section('Packages')
253 for pack_vers in sorted(new_versions.items()):
254 config.set('Packages', *pack_vers)
255 config.write(open('project.info', 'wb'))
256
257 # update the global CMakeLists.txt
258 out = []
259 for l in open('CMakeLists.txt'):
260 if l.strip().startswith('gaudi_project'):
261 l = 'gaudi_project(Gaudi %s)\n' % new_version
262 out.append(l)
263 open('CMakeLists.txt', "w").writelines(out)
264
265
266 if __name__ == '__main__':
267 checkGitVersion()
268 main()
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
prepare_gaudi_release.tag_bar
def tag_bar
Definition: prepare_gaudi_release.py:159
prepare_gaudi_release.findLatestTag
def findLatestTag()
Definition: prepare_gaudi_release.py:40
prepare_gaudi_release.releaseNotes
def releaseNotes
Definition: prepare_gaudi_release.py:58
prepare_gaudi_release.main
def main()
Definition: prepare_gaudi_release.py:166
prepare_gaudi_release.updateReleaseNotes
def updateReleaseNotes(path, notes)
Definition: prepare_gaudi_release.py:78
prepare_gaudi_release.versionKey
def versionKey(x)
Definition: prepare_gaudi_release.py:31
prepare_gaudi_release.checkGitVersion
def checkGitVersion()
Definition: prepare_gaudi_release.py:15
prepare_gaudi_release.change_cml_version
def change_cml_version(cml, newversion)
Definition: prepare_gaudi_release.py:110
prepare_gaudi_release.extract_version
def extract_version(path)
Definition: prepare_gaudi_release.py:99
prepare_gaudi_release.change_version
def change_version(packagedir, newversion)
Definition: prepare_gaudi_release.py:124