927 lines
23 KiB
Python
Executable File
927 lines
23 KiB
Python
Executable File
"""
|
|
Description:
|
|
Helper functions for interfacing with Perforce through our .NET RSG.SourceControl.Perforce library.
|
|
|
|
Author:
|
|
Jason Hayes <jason.hayes@rockstarsandiego.com>
|
|
"""
|
|
|
|
__DoNotReload__ = True # Module is NOT safe to be dynamciy reloaded
|
|
|
|
import re
|
|
|
|
import os
|
|
import types
|
|
import datetime
|
|
import subprocess
|
|
import marshal
|
|
import platform
|
|
|
|
import clr
|
|
|
|
import RS.Config
|
|
|
|
clr.AddReference("RSG.SourceControl.Perforce")
|
|
clr.AddReference("RSG.Base")
|
|
|
|
from RSG.Base import Logging
|
|
from RSG.SourceControl.Perforce import P4
|
|
from RSG.SourceControl.Perforce import FileState
|
|
from RSG.SourceControl.Perforce import FileAction
|
|
from RSG.SourceControl.Perforce import Changelist
|
|
from PySide import QtGui
|
|
|
|
|
|
# # Constants # #
|
|
|
|
# Setup the Perforce connection to use the current project root.
|
|
# Wrapped up around try catch as there is a new constructor for p4 coming
|
|
try:
|
|
p4 = P4()
|
|
except:
|
|
p4 = P4(Logging.LogFactory.ApplicationLog)
|
|
|
|
p4.set_MaxLockTime(10)
|
|
p4.CWD = RS.Config.Project.Path.Root
|
|
|
|
|
|
try:
|
|
import RS.Globals
|
|
# Add callback to ensure the connection is dispose on application shut down
|
|
RS.Globals.Application.OnFileExit.Add(lambda c, e: p4.Dispose())
|
|
except ImportError:
|
|
pass
|
|
|
|
# # Functions # #
|
|
|
|
|
|
def Reconnect():
|
|
"""
|
|
Disconnects and connects the perforce instance. This is to resolve network issues where
|
|
the connection is invalid. This can be called after a long running process to ensure
|
|
the connection did not time out.
|
|
"""
|
|
global p4
|
|
p4.Disconnect()
|
|
p4.Connect()
|
|
|
|
|
|
def User():
|
|
"""
|
|
Gets the name of the user using perforce
|
|
|
|
Return:
|
|
string
|
|
"""
|
|
global p4
|
|
return p4.User
|
|
|
|
|
|
def Client():
|
|
"""
|
|
Gets the name of the client using perforce
|
|
|
|
Return:
|
|
string
|
|
"""
|
|
global p4
|
|
return p4.Client
|
|
|
|
|
|
def Port():
|
|
"""
|
|
Gets the port used by perforce
|
|
|
|
Return:
|
|
string
|
|
"""
|
|
global p4
|
|
return p4.Port
|
|
|
|
|
|
def Host():
|
|
"""
|
|
Gets the host for perforce
|
|
|
|
Return:
|
|
string
|
|
"""
|
|
global p4
|
|
return p4.Host
|
|
|
|
|
|
def CreateConfig(directory, client=None, port=None, user=None):
|
|
"""
|
|
Creates a .p4config file in the supplied directory.
|
|
|
|
Arguments:
|
|
|
|
directory (string): The directory to create the .p4config file.
|
|
|
|
Keyword Arguments:
|
|
|
|
client (string): The client workspace to set.
|
|
port (string): The server and port to use. Format would be: 'rsgsanp4p01:1999'
|
|
user (string): The username to use.
|
|
"""
|
|
if not os.path.isdir(directory):
|
|
os.makedirs(directory)
|
|
|
|
if not port:
|
|
port = Port()
|
|
|
|
if not client:
|
|
client = Client()
|
|
|
|
if not user:
|
|
user = User()
|
|
|
|
try:
|
|
p4config = open('{0}\\.p4config'.format(directory), 'w')
|
|
p4config.write('P4CLIENT={0}\n'.format(client))
|
|
p4config.write('P4PORT={0}\n'.format(port))
|
|
p4config.write('P4USER={0}\n'.format(user))
|
|
|
|
except IOError, err:
|
|
print 'Could not create the .p4config file due to the following error!\n', err
|
|
|
|
finally:
|
|
p4config.close()
|
|
|
|
|
|
def SwitchWorkspace(cwd):
|
|
"""
|
|
Switches the current p4 connection to a new workspace.
|
|
|
|
Arguments:
|
|
cwd (string): Path to the .p4config file containing the client, workspace and server information.
|
|
"""
|
|
global p4
|
|
|
|
p4.Disconnect()
|
|
|
|
p4 = P4()
|
|
p4.CWD = cwd
|
|
|
|
p4.Connect()
|
|
|
|
|
|
def _AssertFileState(fileStateObj):
|
|
"""
|
|
Asserts if the supplied object isn't of type RSG.SourceControl.Perforce.FileState
|
|
|
|
Arguments:
|
|
|
|
fileStateObj (RSG.SourceControl.Perforce.FileState): 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 (string): The filename to check.
|
|
|
|
Returns:
|
|
|
|
True or False
|
|
"""
|
|
return p4.Exists(FixFilePath(filename))
|
|
|
|
|
|
def FixFilePath(filename):
|
|
"""
|
|
Removes characters not recognized by P4 with their escapes.
|
|
|
|
Arguments:
|
|
filename (string): path to file
|
|
|
|
Return:
|
|
string; path to file with unrecognized characters by perforce removed.
|
|
"""
|
|
for character, escape, regex in (("%", "%25", "%25|%23|%40"), ("#", "%23", "%23"), ("@", "%40", "%40")):
|
|
if not re.search(regex, filename) and character in filename:
|
|
filename = filename.replace(character, escape)
|
|
|
|
# Not sure if this is needed
|
|
return str(r"{}".format(filename))
|
|
|
|
|
|
def IsLatest(fileStateObj):
|
|
"""
|
|
Checks to see if the supplied RSG.SourceControl.Perforce.FileState object is the latest revision.
|
|
|
|
Arguments:
|
|
|
|
fileStateObj (RSG.SourceControl.Perforce.FileState): 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 (RSG.SourceControl.Perforce.FileState): 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 (RSG.SourceControl.Perforce.FileState): The RSG.SourceControl.Perforce.FileState object to check.
|
|
|
|
Returns:
|
|
|
|
True or False
|
|
"""
|
|
_AssertFileState(fileStateObj)
|
|
|
|
return fileStateObj.Locked
|
|
|
|
|
|
# TODO: Create a generic method for calling perforce methods, most of the code making calls to perforce is duplicate
|
|
# TODO: code and it doesn't need to be
|
|
|
|
def FixFilePaths( filenames ):
|
|
"""
|
|
Some file paths contain @, we need to convert it %
|
|
"""
|
|
if not isinstance(filenames, (list, tuple)):
|
|
filenames = [filenames]
|
|
|
|
newFilenames = []
|
|
for filename in filenames:
|
|
filename = FixFilePath(filename)
|
|
newFilenames.append( filename )
|
|
return newFilenames
|
|
|
|
|
|
def SwitchPathStyle(path):
|
|
if os.path.splitdrive(path)[0]:
|
|
_, path = os.path.splitdrive(path)[0]
|
|
return "/{}{}".format(["", "depot/"]["gta5" in path.lower()], path.replace("\\", "/"))
|
|
else:
|
|
return r"x:{}".format(path.replace("/", "\\")[1:])
|
|
|
|
|
|
def GetFileState(filenames):
|
|
"""
|
|
Runs fstat on the supplied filenames.
|
|
|
|
Arguments:
|
|
|
|
filenames (list[string, ect.]): The file names to test.
|
|
|
|
Returns:
|
|
|
|
A list of RSG.SourceControl.Perforce.FileState objects.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
if type(filenames) not in (types.TupleType, types.ListType):
|
|
filenames = [filenames]
|
|
|
|
#Fixing file paths
|
|
filenames = FixFilePaths( filenames )
|
|
|
|
fileStates = list(FileState.Create(p4, filenames))
|
|
|
|
if len(fileStates) == 1:
|
|
return fileStates[0]
|
|
|
|
return fileStates
|
|
|
|
|
|
def GetFileInfo(localFilePath, revision, key):
|
|
"""
|
|
Gets information about the file from Perforce based on the revision number and key
|
|
|
|
Arguments:
|
|
|
|
localFilePath (string): local perforce file path to test
|
|
revision (int): revision you want to check
|
|
key (string): the type of information you wish to return; acceptable values are
|
|
time, path, status, desc, client, change, changeType, and user.
|
|
|
|
Returns:
|
|
|
|
key result - could be int/string etc.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
result = None
|
|
filePathState = GetFileState(localFilePath)
|
|
|
|
if filePathState:
|
|
depotFile = filePathState.DepotFilename
|
|
pathWithRevision = [str('{0}#{1}').format(str(depotFile), str(revision))]
|
|
recordSet = p4.Run('changes', pathWithRevision)
|
|
|
|
if recordSet.Records.Length != 0 and recordSet.Records[0].Fields.ContainsKey(key):
|
|
result = recordSet.Records[0].Fields.get_Item(key)
|
|
|
|
return result
|
|
|
|
|
|
def GetAllMarkedDeleteDepotFiles(startingDirectory):
|
|
"""
|
|
Gets ALL the files marked for delete within the supplied directory. This includes fi
|
|
|
|
Arguments:
|
|
|
|
startingDirectory (string): directory to inspect
|
|
lResourceDirectory = "//depot/gta5/art/animation/resources/...fbx"
|
|
|
|
Returns:
|
|
list[string, etc.]; A list of depot file names.
|
|
"""
|
|
global p4
|
|
cmd = "opened"
|
|
args = ["-a", startingDirectory]
|
|
p4Files = p4.Run(cmd, args)
|
|
|
|
fileList = [record[key]
|
|
for record in p4Files.Records
|
|
for key in record.get_Fields().Keys
|
|
if key == "depotFile" and record['action'] == 'delete']
|
|
|
|
return fileList
|
|
|
|
|
|
def Add(filenames, changelistNum=None, force=False, fileType=None):
|
|
"""
|
|
Adds the supplied filenames to Perforce.
|
|
|
|
Arguments:
|
|
|
|
filenames (string): The filenames to add.
|
|
|
|
Keyword Arguments:
|
|
|
|
changelistNum (int):
|
|
Add files to the specified changelist.
|
|
force (boolean):
|
|
Indicates that the file should be force added
|
|
fileType (string):
|
|
The file type that we want to add the file as. Should be string that matches
|
|
one of the following list of fle types;
|
|
http://www.perforce.com/perforce/r15.1/manuals/cmdref/file.types.html
|
|
|
|
Returns:
|
|
|
|
A single or list of RSG.SourceControl.Perforce.FileState objects that were added.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
if type(filenames) not in (types.TupleType, types.ListType):
|
|
filenames = [filenames]
|
|
|
|
# IMPORTANT: p4 add should NOT escape symbols in the file paths (@, etc).
|
|
# The P4 Add command accepts characters normally ignored by the other P4 commands.
|
|
# Replacing the characters with their escapes would add a file to perforce with the escapes in
|
|
# the filename instead of the intended character.
|
|
|
|
args = [filename for filename in filenames]
|
|
|
|
if fileType:
|
|
args[0:0] = ['-t', fileType]
|
|
|
|
if force:
|
|
args[0:0] = ['-f']
|
|
|
|
if changelistNum:
|
|
args[0:0] = ['-c', str(changelistNum)]
|
|
|
|
|
|
recordSet = p4.Run('add', args)
|
|
fileStates = list(FileState.Create(p4, recordSet))
|
|
|
|
if len(fileStates) == 1:
|
|
return fileStates[0]
|
|
|
|
return fileStates
|
|
|
|
|
|
def Edit(filenames, changelistNum=None, fileType=None):
|
|
"""
|
|
Opens for edit the supplied filenames.
|
|
|
|
Arguments:
|
|
|
|
filenames (list[string, etc.]): The filenames to open for edit.
|
|
|
|
Keyword Arguments:
|
|
|
|
changelistNum (int):
|
|
Open for edit the files under this changelist.
|
|
fileType:
|
|
The file type that we want to add the file as. Should be string that matches
|
|
one of the following list of fle types;
|
|
http://www.perforce.com/perforce/r15.1/manuals/cmdref/file.types.html
|
|
|
|
Returns:
|
|
|
|
A single or list of RSG.SourceControl.Perforce.FileState objects that were opened for edit.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
if type(filenames) not in (types.TupleType, types.ListType):
|
|
filenames = [filenames]
|
|
|
|
filenames = FixFilePaths(filenames)
|
|
|
|
# Get the filenames
|
|
args = [filename for filename in filenames]
|
|
|
|
# Add file type flag to the front of the args list
|
|
if fileType:
|
|
args[0:0] = ['-t', fileType]
|
|
|
|
# Add changelist flag to the front of the args list
|
|
if changelistNum:
|
|
args[0:0] = ['-c', str(changelistNum)]
|
|
|
|
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 (list[string, etc.]): The filename(s) to sync.
|
|
|
|
Keyword Arguments:
|
|
|
|
force (boolean): Whether or not to force the sync.
|
|
|
|
Returns:
|
|
|
|
A single or list of RSG.SourceControl.Perforce.FileState objects that were synced.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
if type(filenames) not in (types.TupleType, types.ListType):
|
|
filenames = [filenames]
|
|
|
|
filenames = FixFilePaths(filenames)
|
|
|
|
args = []
|
|
|
|
if force:
|
|
args.append('-f')
|
|
|
|
for filename in filenames:
|
|
if revision:
|
|
filename = '{}#{}'.format(filename, revision)
|
|
|
|
args.append(filename)
|
|
print args
|
|
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(int): 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 (string): 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 (int): 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 GetDescription(changelistNumber):
|
|
"""
|
|
Gets the description from the given changelist. The results are returned in a normal python dictionary
|
|
|
|
Arguments:
|
|
changelistNumber (int): changelist number
|
|
|
|
Returns:
|
|
dictionary
|
|
"""
|
|
results = p4.Run("describe", [str(changelistNumber)])
|
|
return ConvertRecordSetToDictionary(results)
|
|
|
|
|
|
def GetUserData(user):
|
|
"""
|
|
Gets information on the user based on the perforce user name. The results are returned in a normal python dictionary
|
|
|
|
Arguments:
|
|
user (string): perforce username to get data from
|
|
|
|
Return:
|
|
dictionary
|
|
"""
|
|
results = p4.Run("users", [user])
|
|
return ConvertRecordSetToDictionary(results)
|
|
|
|
|
|
def ConvertRecordSetToDictionary(recordSet):
|
|
return {key: record[key] for record in recordSet.Records for key in record.Fields.Keys}
|
|
|
|
|
|
def DeleteChangelist(changelistNum, deleteShelvedFiles=True):
|
|
"""
|
|
Delete a changelist.
|
|
|
|
Arguments:
|
|
|
|
changelistNum(int): The changelist number to delete.
|
|
|
|
Keyword Arguments:
|
|
|
|
deleteShelvedFiles(boolean): 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 (int): The changelist number to delete.
|
|
|
|
Keyword Arguments:
|
|
|
|
deleteShelvedFiles (boolean): 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 (int): 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 (int): 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 (list[string, etc.]): The filenames to shelve.
|
|
|
|
Keyword Arguments:
|
|
|
|
changelistNum (int): The changelist number.
|
|
|
|
Returns:
|
|
|
|
P4API.P4RecordSet object.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
if type(filenames) not in (types.TupleType, types.ListType):
|
|
filenames = [filenames]
|
|
|
|
args = [filename for filename in filenames]
|
|
|
|
if changelistNum:
|
|
args[0:0] = ['-c', str(changelistNum), '-f']
|
|
|
|
return p4.Run('shelve', args)
|
|
|
|
|
|
def Revert(filenames):
|
|
"""
|
|
Revert a list of files.
|
|
|
|
Arguments:
|
|
|
|
filenames (list[string, etc.]): The filenames to revert.
|
|
|
|
Returns:
|
|
|
|
P4API.P4RecordSet object.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
if type(filenames) not in (types.TupleType, types.ListType):
|
|
filenames = [filenames]
|
|
|
|
filenames = FixFilePaths(filenames)
|
|
|
|
args = [filename for filename in filenames]
|
|
|
|
return p4.Run('revert', args)
|
|
|
|
|
|
def Submit(changelistNum):
|
|
"""
|
|
Submit a changelist.
|
|
|
|
Arguments:
|
|
|
|
changelistNum (int): The changelist number to submit.
|
|
|
|
Returns:
|
|
|
|
P4API.P4RecordSet object.
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
return p4.Run('submit', ['-c', str(changelistNum)])
|
|
|
|
|
|
def Run(p4Cmd, args=None):
|
|
"""
|
|
Native P4 Run command.
|
|
|
|
Arguments:
|
|
|
|
p4Cmd (string): valid p4 cmd
|
|
args (list[strings, etc.]): list of paths and/or perforce arguments
|
|
|
|
Returns:
|
|
|
|
P4 Record or error message
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
records = p4.Run(p4Cmd, False, args)
|
|
if records.HasErrors():
|
|
# This shouldn't be here, but to stop other tools from breaking, this is a lesser of 2 evils.
|
|
# Would be good to re-writing this as module a class object
|
|
QtGui.QMessageBox.critical(None, "P4 Error", "P4 the following errors:\n{0}".format(records.ErrorMessage))
|
|
return records
|
|
else:
|
|
return records
|
|
|
|
|
|
def ConvertPerforceSecondsToDate(seconds):
|
|
"""
|
|
Converts the seconds returned by perforce to a date and time
|
|
|
|
Arguments:
|
|
seconds (int): number of seconds grabbed from the perforce server
|
|
|
|
Return:
|
|
string
|
|
"""
|
|
return str(datetime.datetime(1970, 1, 1) + datetime.timedelta(0, int(seconds)))
|
|
|
|
|
|
def getHostClients( server ):
|
|
"""
|
|
This will return the p4 hosts client name. There is a possibility a machine can have on or more clients so will return a list
|
|
:return: List of Strings
|
|
"""
|
|
|
|
# Using the global G option so we get back a python dictionary which is easier to work with
|
|
cmd = 'p4 -G -p {0} clients -E *{1}*'.format(server, platform.node())
|
|
proc = subprocess.Popen( cmd, stdout=subprocess.PIPE , shell=True)
|
|
# because we may have multiple results we need to append them to a list
|
|
clients = []
|
|
try:
|
|
while 1:
|
|
output = marshal.load(proc.stdout)
|
|
client = output.get('client',None)
|
|
if client != None:
|
|
clients.append(client)
|
|
except EOFError:
|
|
pass
|
|
finally:
|
|
proc.stdout.close()
|
|
|
|
return clients
|
|
|
|
|
|
def SwitchToMocapWorkspace(server="rsglicp4s1:1666"):
|
|
"""
|
|
Currently the p4 api doesn't allow the parsing of global parameters- before the actual command
|
|
This is currently the only way for us to gain access to the mocap server via script
|
|
"""
|
|
global p4
|
|
p4.Connect()
|
|
|
|
# First check to see if we have already created a config file for the mocap server
|
|
mocapServerConfig = os.path.normpath("X:\\projects\.p4config")
|
|
|
|
if os.path.exists(mocapServerConfig):
|
|
SwitchWorkspace(mocapServerConfig)
|
|
else:
|
|
# If we don't create one
|
|
# This is a more robust way to get the valid user clients.
|
|
clients = getHostClients(server)
|
|
if len(clients) > 0:
|
|
# Create config file, we will assume to use the first client we find, should be rare to have multiple
|
|
# clients
|
|
CreateConfig("X:\\projects", client=clients[len(clients)-1], port= server, user=User())
|
|
|
|
# Switch to the mocap server
|
|
SwitchWorkspace(mocapServerConfig)
|
|
|
|
|
|
def Connected():
|
|
""" Connects to perforce """
|
|
global p4
|
|
return p4.IsValidConnection(True, True)
|
|
|
|
|
|
class SyncOperation(object):
|
|
"""
|
|
Perforce p4.exe command line
|
|
"""
|
|
|
|
def __init__(self, fileList, start= False, force= False):
|
|
"""
|
|
Contructor
|
|
"""
|
|
|
|
self.p4root = RS.Config.Project.Path.Root
|
|
self.fileList = FixFilePaths(fileList)
|
|
self.force = force
|
|
self.proc = None
|
|
|
|
if start:
|
|
self.Start()
|
|
|
|
def Start(self):
|
|
"""
|
|
Sync Operation
|
|
"""
|
|
|
|
# send a request for a sync to perforce
|
|
fileListString = ""
|
|
for i in range(len(self.fileList)):
|
|
fileListString= fileListString+ ("\"{0}\" ").format(self.fileList[i])
|
|
|
|
if self.force:
|
|
commandText = ('p4 sync -f {0} ').format(fileListString)
|
|
else:
|
|
commandText = ('p4 sync {0} ').format(fileListString)
|
|
|
|
# Remember the original working directory
|
|
originaDir = RS.Config.Project.Path.Root
|
|
|
|
# Change it to the current working one so that we can use the p4 command
|
|
os.chdir( self.p4root )
|
|
try:
|
|
self.proc = subprocess.Popen(commandText, shell=True, stdout=subprocess.PIPE)
|
|
except:
|
|
self.proc = subprocess.Popen(commandText, shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
stdout=subprocess.PIPE)
|
|
self.proc.stdin.close()
|
|
self.proc.stderr.close()
|
|
# Change it back to the original directory
|
|
os.chdir( originaDir )
|
|
|
|
def WaitForCompletion(self):
|
|
"""
|
|
Wait for process to finish
|
|
"""
|
|
|
|
self.proc.wait()
|