3 subversion_id = "$Id: TestMain.py 7635 2008-01-04 09:46:06Z thierry $"
5 import sys, os, os.path
9 from optparse import OptionParser
11 def prompt (question,default=True):
13 question += " [y]/n ? "
15 question += " y/[n] ? "
17 answer=raw_input(question)
20 elif answer[0] in [ 'y','Y']:
22 elif answer[0] in [ 'n','N']:
25 return prompt(question,default)
26 except KeyboardInterrupt:
33 def __init__ (self,command,options):
36 self.tmp="/tmp/command-%d"%os.getpid()
39 if self.options.verbose:
40 print '+',self.command
42 return os.system(self.command)
44 def run_silent (self):
45 if self.options.verbose:
46 print '+',self.command,' .. ',
48 retcod=os.system(self.command + " &> " + self.tmp)
50 print "FAILED ! -- output quoted below "
51 os.system("cat " + self.tmp)
52 print "FAILED ! -- end of quoted output"
53 elif self.options.verbose:
59 if self.run_silent() !=0:
60 raise Exception,"Command %s failed"%self.command
62 # returns stdout, like bash's $(mycommand)
64 tmp="/tmp/status-%d"%os.getpid()
65 if self.options.debug:
66 print '+',self.command,' .. ',
68 os.system(self.command + " &> " + tmp)
69 result=file(tmp).read()
71 if self.options.debug:
72 print '+',self.command,'Done',
76 def __init__(self,path,options):
80 def url_exists (self):
81 if self.options.verbose:
82 print 'Checking url',self.path
83 return os.system("svn list %s &> /dev/null"%self.path) == 0
85 def dir_needs_revert (self):
86 command="svn status %s"%self.path
87 return len(Command(command,self.options).output_of()) != 0
88 # turns out it's the same implem.
89 def file_needs_commit (self):
90 command="svn status %s"%self.path
91 return len(Command(command,self.options).output_of()) != 0
95 # where to store user's config
96 config_storage="CONFIG"
98 configKeys=[ ('svnpath',"Enter your toplevel svnpath (e.g. svn+ssh://thierry@svn.planet-lab.org/svn/)"),
99 ('username',"Enter your firstname and lastname for changelogs"),
100 ("email","Enter your email address for changelogs") ]
103 svn_magic_line="--This line, and those below, will be ignored--"
105 def __init__ (self,name,options):
108 self.moddir="%s/%s/%s"%(os.getenv("HOME"),options.modules,name)
109 self.trunkdir="%s/trunk"%(self.moddir)
110 # what to parse in a spec file
111 self.varnames = ["name",options.version,options.taglevel]
112 self.varmatcher=re.compile("%define\s+(\S+)\s+(.*)")
115 def run (self,command):
116 return Command(command,self.options).run()
117 def run_fatal (self,command):
118 return Command(command,self.options).run_fatal()
119 def run_prompt (self,message,command):
120 if not self.options.verbose:
123 question="Want to run " + command
124 if prompt(question,True):
128 def init_homedir (options):
129 topdir="%s/%s"%(os.getenv("HOME"),options.modules)
131 print 'Checking for',topdir
132 storage="%s/%s"%(topdir,Module.config_storage)
133 if not os.path.isdir (topdir):
134 # prompt for login or whatever svnpath
135 print "Cannot find",topdir,"let's create it"
136 for (key,message) in Module.configKeys:
137 Module.config[key]=raw_input(message+" : ").strip()
138 Command("svn co -N %s %s"%(Module.config['svnpath'],topdir),options).run_fatal()
141 for (key,message) in Module.configKeys:
142 f.write("%s=%s\n"%(key,Module.config[key]))
145 print 'Stored',storage
146 Command("cat %s"%storage,options).run()
150 for line in f.readlines():
151 (key,value)=re.compile("^(.+)=(.+)$").match(line).groups()
152 Module.config[key]=value
156 for (key,message) in Module.configKeys:
157 print key,'=',Module.config[key]
159 def init_moddir (self):
160 if self.options.verbose:
161 print 'Checking for',self.moddir
162 if not os.path.isdir (self.moddir):
163 self.run_fatal("svn up -N %s"%self.moddir)
164 if not os.path.isdir (self.moddir):
165 print 'Cannot find %s - check module name'%self.moddir
168 def init_trunkdir (self):
169 if self.options.verbose:
170 print 'Checking for',self.trunkdir
171 if not os.path.isdir (self.trunkdir):
172 self.run_fatal("svn up -N %s"%self.trunkdir)
174 def revert_trunkdir (self):
175 if self.options.verbose:
176 print 'Checking whether',self.trunkdir,'needs being reverted'
177 if Svnpath(self.trunkdir,self.options).dir_needs_revert():
178 self.run_fatal("svn revert -R %s"%self.trunkdir)
180 def update_trunkdir (self):
181 if self.options.skip_update:
183 if self.options.verbose:
184 print 'Updating',self.trunkdir
185 self.run_fatal("svn update %s"%self.trunkdir)
187 def guess_specname (self):
188 attempt="%s/%s.spec"%(self.trunkdir,self.name)
189 if os.path.isfile (attempt):
193 return glob("%s/*.spec"%self.trunkdir)[0]
195 print 'Cannot guess specfile for module %s'%self.name
198 def spec_dict (self):
199 specfile=self.guess_specname()
200 if self.options.verbose:
201 print 'Parsing',specfile,
204 for line in f.readlines():
205 if self.varmatcher.match(line):
206 (var,value)=self.varmatcher.match(line).groups()
207 if var in self.varnames:
210 if self.options.verbose:
211 print 'found',len(result),'keys'
214 def patch_spec_var (self, patch_dict):
215 specfile=self.guess_specname()
216 newspecfile=specfile+".new"
217 if self.options.verbose:
218 print 'Patching',specfile,'for',patch_dict.keys()
220 new=open(newspecfile,"w")
222 for line in spec.readlines():
223 if self.varmatcher.match(line):
224 (var,value)=self.varmatcher.match(line).groups()
225 if var in patch_dict.keys():
226 new.write('%%define %s %s\n'%(var,patch_dict[var]))
231 os.rename(newspecfile,specfile)
233 def unignored_lines (self, logfile):
235 for logline in file(logfile).readlines():
236 if logline.strip() == Module.svn_magic_line:
241 def insert_changelog (self, logfile, oldtag, newtag):
242 specfile=self.guess_specname()
243 newspecfile=specfile+".new"
244 if self.options.verbose:
245 print 'Inserting changelog from %s into %s'%(logfile,specfile)
247 new=open(newspecfile,"w")
248 for line in spec.readlines():
250 if re.compile('%changelog').match(line):
251 dateformat="* %a %b %d %Y"
252 datepart=time.strftime(dateformat)
253 logpart="%s <%s> - %s %s"%(Module.config['username'],
254 Module.config['email'],
256 new.write(datepart+" "+logpart+"\n")
257 for logline in self.unignored_lines(logfile):
262 os.rename(newspecfile,specfile)
264 def show_dict (self, spec_dict):
265 if self.options.verbose:
266 for (k,v) in spec_dict.iteritems():
269 def trunk_url (self):
270 return "%s/%s/trunk"%(Module.config['svnpath'],self.name)
271 def tag_name (self, spec_dict):
272 return "%s-%s-%s"%(spec_dict['name'],spec_dict[self.options.version],spec_dict[self.options.taglevel])
273 def tag_url (self, spec_dict):
274 return "%s/%s/tags/%s"%(Module.config['svnpath'],self.name,self.tag_name(spec_dict))
276 # locate specfile, parse it, check it and show values
277 def do_version (self):
280 self.revert_trunkdir()
281 self.update_trunkdir()
282 print '==============================',self.name
283 #for (key,message) in Module.configKeys:
284 # print key,':',Module.config[key]
285 spec_dict = self.spec_dict()
286 print 'trunk url',self.trunk_url()
287 print 'latest tag url',self.tag_url(spec_dict)
288 print 'specfile:',self.guess_specname()
289 for varname in self.varnames:
290 if not spec_dict.has_key(varname):
291 print 'Could not find %%define for %s'%varname
294 print varname+":",spec_dict[varname]
296 init_warning="""WARNING
297 The module-init function has the following limitations
298 * it does not handle changelogs
299 * it does not scan the -tags.mk files to adopt the new tags"""
301 if self.options.verbose:
302 print Module.init_warning
303 if not prompt('Want to proceed anyway'):
308 self.revert_trunkdir()
309 self.update_trunkdir()
310 spec_dict = self.spec_dict()
312 trunk_url=self.trunk_url()
313 tag_name=self.tag_name(spec_dict)
314 tag_url=self.tag_url(spec_dict)
315 # check the tag does not exist yet
316 if Svnpath(tag_url,self.options).url_exists():
317 print 'Module %s already has a tag %s'%(self.name,tag_name)
320 self.run_prompt("Create initial tag",
321 "svn copy --editor-cmd=%s %s %s"%(self.options.editor,trunk_url,tag_url))
326 self.revert_trunkdir()
327 self.update_trunkdir()
328 spec_dict = self.spec_dict()
329 self.show_dict(spec_dict)
331 trunk_url=self.trunk_url()
332 tag_url=self.tag_url(spec_dict)
333 for url in [ trunk_url, tag_url ] :
334 if not Svnpath(url,self.options).url_exists():
335 print 'Could not find svn URL %s'%url
338 self.run("svn diff %s %s"%(tag_url,trunk_url))
340 def patch_tags_files (self, tagsfile, oldname, newname):
341 newtagsfile=tagsfile+".new"
342 if self.options.verbose:
343 print 'Replacing %s into %s in %s'%(oldname,newname,tagsfile)
345 new=open(newtagsfile,"w")
346 matcher=re.compile("^(.*)%s(.*)"%oldname)
347 for line in tags.readlines():
348 if not matcher.match(line):
351 (begin,end)=matcher.match(line).groups()
352 new.write(begin+newname+end+"\n")
355 os.rename(newtagsfile,tagsfile)
360 self.revert_trunkdir()
361 self.update_trunkdir()
362 spec_dict = self.spec_dict()
363 self.show_dict(spec_dict)
365 # parse specfile, check that the old tag exists and the new one does not
366 trunk_url=self.trunk_url()
367 old_tag_name = self.tag_name(spec_dict)
368 old_tag_url=self.tag_url(spec_dict)
370 new_taglevel = str ( int (spec_dict[self.options.taglevel]) + 1)
371 spec_dict[self.options.taglevel] = new_taglevel
372 new_tag_name = self.tag_name(spec_dict)
373 new_tag_url=self.tag_url(spec_dict)
374 for url in [ trunk_url, old_tag_url ] :
375 if not Svnpath(url,self.options).url_exists():
376 print 'Could not find svn URL %s'%url
378 if Svnpath(new_tag_url,self.options).url_exists():
379 print 'New tag\'s svn URL %s already exists ! '%url
382 # side effect in trunk's specfile
383 self.patch_spec_var({self.options.taglevel:new_taglevel})
385 # prepare changelog file
386 # we use the standard subversion magic string (see svn_magic_line)
387 # so we can provide useful information, such as version numbers and diff
389 changelog="/tmp/%s-%d.txt"%(self.name,os.getpid())
390 file(changelog,"w").write("""
395 """%(Module.svn_magic_line,self.name,old_tag_url,new_tag_url))
397 if not self.options.verbose or prompt('Want to run diff',True):
398 self.run("(echo 'DIFF========='; svn diff %s %s) >> %s"%(old_tag_url,trunk_url,changelog))
400 self.run("%s %s"%(self.options.editor,changelog))
401 # insert changelog in spec
402 if self.options.changelog:
403 self.insert_changelog (changelog,old_tag_name,new_tag_name)
406 build = Module(self.options.build,self.options)
408 build.init_trunkdir()
409 build.revert_trunkdir()
410 build.update_trunkdir()
412 for tagsfile in glob(build.trunkdir+"/*-tags.mk"):
413 if prompt("Want to check %s"%tagsfile):
414 self.patch_tags_files(tagsfile,old_tag_name,new_tag_name)
417 paths += self.trunkdir + " "
418 paths += build.trunkdir + " "
419 self.run_prompt("Check","svn diff " + paths)
420 self.run_prompt("Commit","svn commit --file %s %s"%(changelog,paths))
421 self.run_prompt("Create tag","svn copy --file %s %s %s"%(changelog,trunk_url,new_tag_url))
423 if self.options.debug:
424 print 'Preserving',changelog
428 usage="""Usage: %prog options module1 [ .. modulen ]
430 manage subversion tags and specfile
431 requires the specfile to define name, version and taglevel
433 module-diff : show difference between trunk and latest tag
434 module-tag : increment taglevel in specfile, insert changelog in specfile,
435 create new tag and and adopt it in build/*-tags.mk
436 module-init : create initial tag
437 module-version : only check specfile and print out details"""
440 all_modules=os.path.dirname(sys.argv[0])+"/modules.list"
442 parser=OptionParser(usage=usage,version=subversion_id)
443 parser.add_option("-a","--all",action="store_true",dest="all_modules",default=False,
444 help="Runs all modules as found in %s"%all_modules)
445 parser.add_option("-e","--editor", action="store", dest="editor", default="emacs",
446 help="Specify editor")
447 parser.add_option("-u","--no-update",action="store_true",dest="skip_update",default=False,
448 help="Skips svn updates")
449 parser.add_option("-c","--no-changelog", action="store_false", dest="changelog", default=True,
450 help="Does not update changelog section in specfile when tagging")
451 parser.add_option("-m","--modules", action="store", dest="modules", default="modules",
452 help="Name for topdir - defaults to modules")
453 parser.add_option("-b","--build", action="store", dest="build", default="build",
454 help="Set module name for build")
455 parser.add_option("-t","--taglevel",action="store",dest="taglevel",default="taglevel",
456 help="Specify an alternate spec variable for taglevel")
457 parser.add_option("-s","--version-string",action="store",dest="version",default="version",
458 help="Specify an alternate spec variable for version")
459 parser.add_option("-v","--verbose", action="store_true", dest="verbose", default=False,
460 help="Run in verbose mode")
461 parser.add_option("-d","--debug", action="store_true", dest="debug", default=False,
462 help="Debug mode - mostly more verbose")
463 (options, args) = parser.parse_args()
464 if options.debug: options.verbose=True
467 if options.all_modules:
468 args=Command("grep -v '#' %s"%all_modules,options).output_of().split()
472 Module.init_homedir(options)
474 module=Module(modname,options)
475 if sys.argv[0].find("diff") >= 0:
477 elif sys.argv[0].find("tag") >= 0:
479 elif sys.argv[0].find("init") >= 0:
481 elif sys.argv[0].find("version") >= 0:
484 print "Unsupported command",sys.argv[0]
488 # basically, we exit if anything goes wrong
489 if __name__ == "__main__" :