''' Perforce Author: Jason Hayes Description: Helper functions for interfacing with Perforce through our .NET RSG.SourceControl.Perforce library. ''' import os import types import clr import RS.Config clr.AddReference( "RSG.SourceControl.Perforce" ) from RSG.SourceControl.Perforce import P4 from RSG.SourceControl.Perforce import FileState from RSG.SourceControl.Perforce import FileAction from RSG.SourceControl.Perforce import Changelist ## Constants ## # Setup the Perforce connection to use the current project root. p4 = P4() p4.CWD = RS.Config.Project.Path.Root ## Functions ## def _AssertFileState( fileStateObj ): ''' Asserts if the supplied object isn't of type RSG.SourceControl.Perforce.FileState Arguments: fileStateObj: The RSG.SourceControl.Perforce.FileState object to check. ''' assert isinstance( fileStateObj, FileState ), 'Incorrect type: Must supply a RSG.SourceControl.Perforce.FileState object!' def DoesFileExist( filename ): ''' Checks to see if the supplied filename exists in Perforce. Arguments: filename: The filename to check. Returns: True or False ''' return p4.Exists( filename ) def IsLatest( fileStateObj ): ''' Checks to see if the supplied RSG.SourceControl.Perforce.FileState object is the latest revision. Arguments: fileStateObj: The RSG.SourceControl.Perforce.FileState object to check. Returns: True or False ''' _AssertFileState( fileStateObj ) return fileStateObj.HaveRevision == fileStateObj.HeadRevision def IsOpenForEdit( fileStateObj ): ''' Checks whether or not the supplied file is opened for edit by the current user. Arguments: fileStateObj: The RSG.SourceControl.Perforce.FileState object to check. Returns: True or False ''' _AssertFileState( fileStateObj ) return fileStateObj.OpenAction == FileAction.Edit def CanOpenForEdit( fileStateObj ): ''' Tests whether or not a file can be opened for edit by the current user. Arguments: fileStateObj: The RSG.SourceControl.Perforce.FileState object to check. Returns: True or False ''' _AssertFileState( fileStateObj ) return fileStateObj.Locked def GetFileState( filenames ): ''' Runs fstat on the supplied filenames. Arguments: filenames: The filenames to test. Returns: A list of RSG.SourceControl.Perforce.FileState objects. ''' global p4 p4.Connect() if type( filenames ) != types.TupleType and type( filenames ) != types.ListType: filenames = [ filenames ] fileStates = list( FileState.Create( p4, filenames ) ) if len( fileStates ) == 1: return fileStates[ 0 ] return fileStates def Add( filenames, changelistNum = None): ''' Adds the supplied filenames to Perforce. Arguments: filenames: The filenames to add. Keyword Arguments: changelistNum: Add files to the specified changelist. Returns: A single or list of RSG.SourceControl.Perforce.FileState objects that were added. ''' global p4 p4.Connect() if type( filenames ) != types.TupleType and type( filenames ) != types.ListType: filenames = [ filenames ] args = [] if changelistNum: args = [ '-c', str( changelistNum ) ] for filename in filenames: args.append( filename ) recordSet = p4.Run( 'add', args ) fileStates = list( FileState.Create( p4, recordSet ) ) if len( fileStates ) == 1: return fileStates[ 0 ] return fileStates def Find( searchString, searchDirectory='/'): ''' Runs 'files' p4 command on a depot path and search string Arguments: searchString = partial or full string to search for, no need to include wildcards, it gets built into the arguments searchDirectory = starting path for the search in the depot Returns: A single list of paths found. ''' global p4 p4.Connect() args = ["-e", searchDirectory + "/..." + searchString] recordSet = p4.Run( 'files', args ) fileStates = list( FileState.Create( p4, recordSet ) ) foundFiles = [] for state in fileStates: foundFiles.append(state.get_DepotFilename()) return foundFiles def Edit( filenames, changelistNum = None ): ''' Opens for edit the supplied filenames. Arguments: filenames: The filenames to open for edit. Keyword Arguments: changelistNum: Open for edit the files under this changelist. Returns: A single or list of RSG.SourceControl.Perforce.FileState objects that were opened for edit. ''' global p4 p4.Connect() if type( filenames ) != types.TupleType and type( filenames ) != types.ListType: filenames = [ filenames ] args = [] if changelistNum: args.append( '-c' ) args.append( str( changelistNum ) ) for filename in filenames: args.append( filename ) recordSet = p4.Run( 'edit', args ) fileStates = list( FileState.Create( p4, recordSet ) ) if len( fileStates ) == 1: return fileStates[ 0 ] return fileStates def Sync( filenames, force = False, revision = None ): ''' Syncs the supplied filename(s). Arguments: filenames: The filename(s) to sync. Keyword Arguments: force: Whether or not to force the sync. revision: Sync to the specified revision. Returns: A single or list of RSG.SourceControl.Perforce.FileState objects that were synced. ''' global p4 p4.Connect() if type( filenames ) != types.TupleType and type( filenames ) != types.ListType: filenames = [ filenames ] args = [] if force: args.append( '-f' ) for filename in filenames: if revision: filename += '#{0}'.format( revision ) args.append( filename ) recordSet = p4.Run( 'sync', args ) fileStates = list( FileState.Create( p4, recordSet ) ) if len( fileStates ) == 1: return fileStates[ 0 ] return fileStates def GetChangelist( changelistNum ): ''' Query a changelist. Arguments: changelistNum: The changelist number to query. Returns: RSG.SourceControl.Perforce.Changelist object. None if not found. ''' global p4 p4.Connect() changelist = Changelist.Create( p4, [ changelistNum ] ) if changelist.Length == 1: return changelist[ 0 ] return None def CreateChangelist( description ): ''' Creates a new changelist. Arguments: description: The changelist desription. Returns: RSG.SourceControl.Perforce.Changelist object. ''' global p4 p4.Connect() changelist = p4.CreatePendingChangelist( description ) return GetChangelist( changelist.Number ) def GetFilesInChangelist( changelistNum ): ''' Query all files in a changelist. Arguments: changelistNum: The changelist number to query. Returns: List of files found in the changelist. ''' changelist = GetChangelist( changelistNum ) if changelist: return list( changelist.Files ) return None def DeleteChangelist( changelistNum, deleteShelvedFiles = True ): ''' Delete a changelist. Arguments: changelistNum: The changelist number to delete. Keyword Arguments: deleteShelvedFiles: Delete shelved files in the changelist. Returns: P4API.P4RecordSet object. ''' global p4 p4.Connect() if deleteShelvedFiles: DeleteShelvedFiles( changelistNum ) return p4.Run( 'change', [ '-d', str( changelistNum ) ] ) def RevertChangelist( changelistNum, deleteShelvedFiles = False ): ''' Revert all files in a changelist. Arguments: changelistNum: The changelist number to delete. Keyword Arguments: deleteShelvedFiles: Delete any shelved files in the changelist. Returns: P4API.P4RecordSet object. ''' global p4 p4.Connect() if deleteShelvedFiles: DeleteShelvedFiles( changelistNum ) return p4.Run( 'revert', [ '-c', str( changelistNum ), '//...' ] ) def DeleteShelvedFiles( changelistNum ): ''' Delete all shelved files in a changelist. Arguments: changelistNum: The changelist number to delete. Returns: P4API.P4RecordSet object. ''' global p4 p4.Connect() return p4.Run( 'shelve', [ '-c', str( changelistNum ), '-d' ] ) def ShelveChangelist( changelistNum ): ''' Shelve all files in a changelist. Arguments: changelistNum: The changelist number. Returns: P4API.P4RecordSet object. ''' global p4 p4.Connect() return p4.Run( 'shelve', [ '-c', str( changelistNum ), '-f' ] ) def Shelve( filenames, changelistNum = None ): ''' Shelve a list of files. Arguments: filenames: The filenames to shelve. Keyword Arguments: changelistNum: The changelist number. Returns: P4API.P4RecordSet object. ''' global p4 p4.Connect() if type( filenames ) != types.TupleType and type( filenames ) != types.ListType: filenames = [ filenames ] args = [] if changelistNum: args.append( '-c' ) args.append( str( changelistNum ) ) args.append( '-f' ) for filename in filenames: args.append( filename ) return p4.Run( 'shelve', args ) def Revert( filenames ): ''' Revert a list of files. Arguments: filenames: The filenames to revert. Returns: P4API.P4RecordSet object. ''' global p4 p4.Connect() if type( filenames ) != types.TupleType and type( filenames ) != types.ListType: filenames = [ filenames ] args = [] for filename in filenames: args.append( filename ) return p4.Run( 'revert', args ) def Submit( changelistNum ): ''' Submit a changelist. Arguments: changelistNum: The changelist number to submit. Returns: P4API.P4RecordSet object. ''' global p4 p4.Connect() return p4.Run( 'submit', [ '-c', str( changelistNum ) ] )