# P4Utilities.py # David Bowman # September 19, 2001 # # Utility functions for using Perforce. import sys, re, os, marshal, string if sys.platform == 'win32': gDictFlags = 'rb' else: gDictFlags = 'r' # on MacOS X, Perforce 2002.1 has a bug which causes it to either crash or corrupt the # operating system unless it is called by its full path name (UGLY) if sys.platform in ['darwin1', 'darwin']: p4 = '/usr/local/bin/p4 ' else: p4 = 'p4 ' kReturnFile = 0 kReturnDict = 1 kReturnDictList = 2 def CallP4(cmd, returnKind=kReturnFile, openFlags='r', client='', user='', password='', host='', port=''): ''' Use this to call Perforce with your command line. Do not start your command with "p4". Parameters: cmd : The command line to run. returnKind : Set to 1 to return a marshaled dictionary. The "-G" flag will be added for you. openFlags : File open flags such as 'r' or 'rb'. Returns: Either the opened file or the dictionary. ''' options = '' if client != '': options += ' -c "' + client +'"' if user != '': options += ' -u "' + user +'"' if password != '': options += ' -P "' + password +'"' if host != '': options += ' -H "' + host +'"' if port != '': options += ' -p "' + port +'"' if returnKind == kReturnDict: f = os.popen(p4 + options + ' -G ' + cmd, gDictFlags) return marshal.load(f) elif returnKind == kReturnFile: return os.popen(p4 + options + " " + cmd, openFlags) elif returnKind == kReturnDictList: dictList = [] f = os.popen(p4 + options + ' -G ' + cmd, gDictFlags) while 1: try: opened_dict = marshal.load(f) dictList.append(opened_dict) except EOFError: break return dictList else: raise "CallP4 does not understand this returnKind constant" def DoesUserHaveCurrentRevision(file, client='', user='', returnRevisions=0): ''' Determine if a user has the most current version of a file. Parameters: file : The full path to the depot file to check. client : The client spec to use. Will use client if not supplied. user : The user to check. Will use current user if not supplied. returnRevisions : If 0, will only return 'boolean', if non-0, will return the 'boolean, have, need'. (see below) Returns: (boolean, have, need) boolean: 0 : User does not have the most current version. 1 : User has the most current version. have: The reviison of the file that the user has. need: The head revision number of the file. ''' haveCurrent = 0 haveRev = 0 needRev = GetCurrentFileRevision(file) if needRev > 0: haveRev = GetUserFileRevision(file, client, user) if haveRev > 0: haveCurrent = haveRev == needRev if returnRevisions: return haveCurrent, haveRev, needRev return haveCurrent def GetChangelistAuthor(cl): ''' Get the username of the person who submitted a changelist. Parameters: cl : Checgelist number to check. ''' changelistDescDict = CallP4('describe -s ' + str(cl), kReturnDict) username = changelistDescDict['user'] return username def GetChangelistClient(cl): ''' Get the clientspec used for a changelist. Parameters: cl : Changelist number to check. ''' dict = CallP4('change -o ' + str(cl), kReturnDict) return dict['Client'] def GetChangelistDescription(cl): ''' Get the text description of the changelist number passed to this function. Parameters: cl : Changelist number to check. ''' dict = CallP4('change -o ' + str(cl), kReturnDict) return dict['Description'] def GetCounter(name): cmd = "counter " + name dict = CallP4(cmd, kReturnDict) return int( dict['data'] ) def GetClientspecViews(cs): dict = CallP4("client -o " + cs, kReturnDict) views = []; i = 0 while 1: try: v = dict['View' + str(i)] v = v.replace('"', '') # remove any quotes t = v.split('/... ') # split into depot path and local path t[0] += '/...' # put back the chars the split removed views.append(t) i += 1 except: break return views def SetChangelistDescription(cl, description): ''' Set the text description of the changelist number passed to this function. Parameters: cl : Changelist number to modify. description : new description for this changelist ''' dict = CallP4("change -o " + str(cl), kReturnDict) # this function demonstrates the technique of passing a python # object through stdin to a Perforce command (inFile, outFile) = os.popen2(p4 + " -G change -i -f", 'b') dict['Description'] = description marshal.dump(dict, inFile) inFile.close() def SetCounter(name, value): cmd = "counter " + name + " " + str(value) CallP4(cmd) def GetChangelistFiles(cl): ''' Get the files in a changelist. Parameters: cl : Changelist to check. Returns: A string array of all the files in the changelist. If the fuction failes, the array will be empty. ''' files = [] try: changeDict = CallP4('change -o ' + str(cl), kReturnDict) if changeDict['Status'] == 'pending': i = 0 while 1: try: fn = changeDict['Files' + str(i)] files.append(fn) i += 1 except: break elif changeDict['Status'] == 'submitted': describeDict = CallP4('describe -s ' + str(cl), kReturnDict) i = 0 while 1: try: fn = describeDict['depotFile' + str(i)] files.append(fn) i += 1 except: break except: return files return files def GetCurrentFileRevision(file): ''' Get the revision number of the most current version of a file. ''' cmd = 'files "' + file + '"' dict = CallP4(cmd, kReturnDict) return int(dict['rev']) def GetLatestFileRevisionDate(file): ''' Get the date of the submit of the most recent revision of a file. ''' cmd = 'filelog -m 1 "' + file + '"' dict = CallP4(cmd, kReturnDict) try: datetime = int(dict['time0']) except: datetime = 0 return datetime def GetUserFileRevision(file, client='', user=''): ''' Get the current revision number of a file that the user has in their client. This function will fail if the client spec has a host restriction. Returns: -1 : An error occurred or the user does not have any revision of the file. >0 : The revision of the file that the user has. ''' cmd = '' if user: cmd += '-u ' + user + ' ' if client: cmd += '-c ' + client + ' ' cmd += 'have "' + file + '"' dict = CallP4(cmd, kReturnDict) g = re.match(r'^.+#(\d+) ', dict['data']) if g: rev = int(g.groups()[0]) return rev return -1 def IsUserInGroup(user, group): ''' Determine if a given user is part of the given group. ''' user = string.lower(user) dict = CallP4('group -o ' + group, kReturnDict) i = 0 try: while dict['Users' + str(i)]: u = dict['Users' + str(i)] u = string.lower(u) if u == user: return 1 i += 1 except: return 0 return 0 def OpenForEdit(fn, changelist=0): ''' Open a file for edit. Parameters: fn : The depot file to open. changelist : The changelist into which the file will be opened. If 0, the default changelist will be used. Returns: 0 : Failure. 1 : Sucess. ''' if changelist == 0: cmd = 'edit "' + fn + '"' else: cmd = 'edit -c ' + str(changelist) + ' "' + fn + '"' dict = CallP4(cmd, kReturnDict) try: if dict['code'] == 'error': return 0 except: return 1 return 1 def Submit(changeList): dict = CallP4('submit -c ' + str(changeList), kReturnDict) try: if dict['code'] == 'error': return 0 except: return 1 return 1 def UserLookup(who): ''' Gievn a Perforce username, lookup their email address and full name. ''' userDescDict = CallP4('user -o ' + who, kReturnDict) email = userDescDict['Email'] fullname = userDescDict['FullName'] return email, fullname