Adding huge improvements. There are still a few more to make to account for computers not setup correctly, but it's functional. Still has the occasional console hang bug. Now also prints out run time. There is one new minor bug, reverting back to the previously set client view.

This commit is contained in:
U-ILLFONIC\bernst 2014-08-13 17:09:19 -06:00
parent 6236ead338
commit 06b0cbe426
1 changed files with 146 additions and 18 deletions

View File

@ -11,7 +11,7 @@
# todo: buffer output, after exceeding a certain amount print to the output.
# todo: allow logging output besides console output, or redirection altogether
import datetime, inspect, multiprocessing, optparse, os, re, stat, subprocess, sys, threading, traceback
import datetime, inspect, marshal, multiprocessing, optparse, os, re, stat, subprocess, sys, threading, time, traceback
# trying ntpath, need to test on linux
import ntpath
@ -80,6 +80,13 @@ def match_in_ignore_list( path, ignore_list ):
def call_process( args ):
return subprocess.call( args.split( ), stdout=subprocess.PIPE, stderr=subprocess.PIPE )
def try_call_process( args, path=None ):
try:
subprocess.check_output( args.split( ), shell=False, cwd=path )
return 0
except subprocess.CalledProcessError:
return 1
use_bytearray_str_conversion = type( b"str" ) is not str
def get_str_from_process_stdout( line ):
if use_bytearray_str_conversion:
@ -90,6 +97,33 @@ def get_str_from_process_stdout( line ):
def singular_pulural( val, singular, plural ):
return singular if val == 1 else plural
def parse_info_from_command( args, value, path = None ):
"""
:rtype : string
"""
proc = subprocess.Popen( args.split( ), stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=path )
for line in proc.stdout:
line = get_str_from_process_stdout( line )
if not line.startswith( value ):
continue
return line[ len( value ) : ].strip( )
return None
def get_p4_py_results( args, path = None ):
results = []
proc = subprocess.Popen( [ 'p4', '-G' ] + args.split( ), stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=path )
try:
while True:
output = marshal.load( proc.stdout )
results.append( output )
except EOFError:
pass
finally:
proc.stdout.close()
return results
# Keep these in mind if you have issues:
# https://stackoverflow.com/questions/16557908/getting-output-of-a-process-at-runtime
# https://stackoverflow.com/questions/4417546/constantly-print-subprocess-output-while-process-is-running
@ -115,10 +149,33 @@ def get_client_set( path ):
files.add( local_path )
# TODO: check error to see if the path is not in the client view. Prompt anyway?
proc.wait( )
for line in proc.stderr:
raise Exception(line)
return files
def get_client_root( ):
"""
:rtype : string
"""
command = "p4 info"
proc = subprocess.Popen( command.split( ), stdout=subprocess.PIPE, stderr=subprocess.PIPE )
for line in proc.stdout:
line = get_str_from_process_stdout( line )
clientFile_tag = "Client root: "
if not line.startswith( clientFile_tag ):
continue
local_path = normpath( line[ len( clientFile_tag ) : ].strip( ) )
return local_path
return None
class PTable( list ):
def __init__( self, *args ):
list.__init__( self, args )
@ -212,18 +269,18 @@ class Console( threading.Thread ):
self.queue.task_done( )
class Task( threading.Event ):
def __init__( data, cmd = None ):
threading.Event.__init__( self )
# class Task( threading.Event ):
# def __init__( data, cmd = None ):
# threading.Event.__init__( self )
self.cmd = cmd if cmd is None MSG.RUN_FUNCTION
self.data = data
# self.cmd = cmd if cmd is None MSG.RUN_FUNCTION
# self.data = data
def isDone( self ):
return self.isSet()
# def isDone( self ):
# return self.isSet()
def join( self ):
self.wait( )
# def join( self ):
# self.wait( )
class Worker( threading.Thread ):
def __init__( self, console, queue, files_to_ignore ):
@ -257,6 +314,8 @@ class Worker( threading.Thread ):
self.queue.task_done( )
def main( args ):
start = time.clock()
# check requirements
if call_process( 'p4 -V' ) != 0:
print( 'Perforce Command-line Client(p4) is required for this script.' )
@ -269,11 +328,60 @@ def main( args ):
parser.add_option( "-t", "--threads", dest="thread_count", help="Number of threads to crawl your drive and poll p4.", default=100 )
parser.add_option( "-q", "--quiet", action="store_true", dest="quiet", help="This overrides verbose", default=False )
parser.add_option( "-v", "--verbose", action="store_true", dest="verbose", default=True )
parser.add_option( "-i", "--interactive", action="store_true", dest="interactive", default=False )
( options, args ) = parser.parse_args( )
( options, args ) = parser.parse_args( args )
directory = normpath( options.directory if options.directory is not None else os.getcwd( ) )
# get user
print("\nChecking p4 info...")
result = get_p4_py_results('info')
if len(result) == 0 or b'userName' not in result[0].keys():
print("Can't find perforce info, is it even setup?")
sys.exit(1)
username = get_str_from_process_stdout(result[0][b'userName'])
client_host = get_str_from_process_stdout(result[0][b'clientHost'])
print("|Done.")
client_root = get_client_root()
ldirectory = directory.lower()
workspace_name = None
if not ldirectory.startswith(client_root.lower()):
print("\nCurrent directory not in client view, checking other workspaces for user '" + username + "' ...")
workspace_name = parse_info_from_command('p4 info', 'Client name: ')
# get user workspaces
result = get_p4_py_results('workspaces -u ' + username)
workspaces = []
for r in result:
whost = get_str_from_process_stdout(r[b'Host'])
if whost is not None and len(whost) != 0 and client_host != whost:
continue
workspace = {'root': get_str_from_process_stdout(r[b'Root']), 'name': get_str_from_process_stdout(r[b'client'])}
workspaces.append(workspace)
del result
# check current directory against current workspace, see if it matches existing workspaces.
for w in workspaces:
wname = w['name']
wlower = w['root'].lower()
if ldirectory.startswith(wlower):
# set current directory, don't forget to revert it back to the existing one
print("|Setting client view to: " + wname)
if try_call_process( 'p4 set P4CLIENT=' + wname ):
print("|There was a problem trying to set the p4 client view (workspace).")
sys.exit(1)
break
else:
print( "|Couldn't find a workspace root that matches the current directory for the current user." )
sys.exit(1)
print("|Done.")
# Files are added from .p4ignore
# Key is the file root, the value is the table of file regexes for that directory.
files_to_ignore = PDict()
@ -285,11 +393,13 @@ def main( args ):
with Console( auto_flush_num=20, auto_flush_time=1000 ) as c:
if not options.quiet:
c.writeflush( "Caching files in depot, this may take a little while..." )
c.writeflush( "\nCaching files in depot, this may take a little while..." )
# TODO: push this off to a thread and walk the directory so we get a headstart.
files_in_depot = get_client_set( directory )
c.writeflush( "|Done." )
# TODO: push a os.walk request off to a thread to build a list of files in the directory; create batch based on directory?
# TODO: at this point join on both tasks to wait until they're done
@ -299,7 +409,7 @@ def main( args ):
# will at least need to redo how the ignore lists are handled for efficiencies sake.
if not options.quiet:
c.writeflush( "Checking " + directory)
c.writeflush( "\nChecking " + directory)
for root, dirs, files in os.walk( directory ):
ignore_list = get_ignore_list( root, files_to_ignore )
@ -346,21 +456,35 @@ def main( args ):
if match_in_ignore_list( path, ignore_list ):
# add option of using send2trash
if not options.quiet:
c.write( "| ignoring " + d )
c.write( "| ignoring " + path )
dirs.remove( d )
try:
os.rmdir(path)
remove_dir_count += 1
if not options.quiet:
c.write( "| " + d + " was removed." )
c.write( "| " + path + " was removed." )
except OSError:
# Fails on non-empty directory
pass
if not options.quiet:
c.write( "|Done." )
# This needs to happen automatically even when an exception happens, when we leave scope.
if workspace_name is not None:
c.write("\nReverting back to original client view...")
# set workspace back to the original one
if try_call_process( 'p4 set P4CLIENT=' + workspace_name ):
error_count += 1
if not options.quiet:
c.write("|There was a problem trying to restore the set p4 client view (workspace).")
else:
if not options.quiet:
c.write("|Reverted client view back to '" + workspace_name + "'.")
if not options.quiet:
c.write("|Done.")
if not options.quiet:
output = "\nRemoved " + str( remove_file_count ) + singular_pulural( remove_file_count, " file, ", " files, " )
output = "\n[ Removed " + str( remove_file_count ) + singular_pulural( remove_file_count, " file, ", " files, " )
output += str( remove_dir_count ) + singular_pulural( remove_dir_count, " directory", " directories")
if warning_count > 0:
@ -368,7 +492,11 @@ def main( args ):
if error_count > 0:
output += " w/ " + str( error_count ) + singular_pulural( error_count, " error", " errors" )
c.write( output + "." )
end = time.clock()
delta = end - start
output += ", and finished in " + str(delta) + "s"
c.write( output + " ]" )
if __name__ == "__main__":
try: