#! /usr/bin/env python # # Copyright 2011-2014 by Ben Key # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY # OF SUCH DAMAGE. # import getopt import locale import os import stat import sys if "nt" == os.name.lower(): import win32api import win32console class Command(object): """Run a command and capture it's output string, error string and exit status""" def __init__(self, command): self.command = command def run(self, Shell=True): import subprocess as sp process = sp.Popen(self.command, shell=Shell, stdout=sp.PIPE, stderr=sp.PIPE) self.pid = process.pid self.output, self.error = process.communicate() self.returncode = process.returncode return self class Singleton(object): """A Python implementation of the Singleton Design Patern obtained from the "python singleton and singleton borg" article found at http://snippets.dzone.com/posts/show/651.""" def __new__(cls, *p, **k): cls._do_initialization = False if not '_the_instance' in cls.__dict__: cls._do_initialization = True cls._the_instance = object.__new__(cls) return cls._the_instance class GetCurrentCodepage(Singleton): """Obtains the active code page. This is implemented as a singleton callable object because this information is needed in several places and the work to obtain the information only needs to be done once.""" def __init__(self): """Constructor of the GetCurrentCodepage class. Uses the GetConsoleOutputCP function to obtain the current codepage on Microsoft Windows. An implementation that will work on other operating systems is pending.""" Singleton(self) if self._do_initialization: self.__codepage = "" if "nt" == os.name.lower(): self.__codepage = str(win32console.GetConsoleOutputCP()) def __call__(self): """The __call__ object method that makes the GetCurrentCodepage class a callable object.""" return self.__codepage class CommandLineArgumentsParser: """A class that parses the command line arguments that are passed to this script.""" def __init__(self, argv = sys.argv): """Constructor of the CommandLineArgumentsParser class.""" self.__display_usage = False self.__display_version = False self.__list_only = False self.__client = None self.__argv = argv self.__args = [] def parse_command_line(self): """Parses the command line options.""" parse_opts = True if len(self.__argv) > 1: arg1 = self.__argv[1] arg1 = arg1.lower() if ( ("/?" == arg1) or ("/h" == arg1) or ("/help" == arg1) ): self.__display_usage = True return 0 try: opts, args = getopt.getopt( self.__argv[1:], "hH?vVlLc:C:", ["help", "HELP", "version", "VERSION", "list", "LIST", "client=", "CLIENT="]) self.__args = args except getopt.GetoptError as err: print (err.msg) parse_opts = False self.__display_usage = True if parse_opts: for o, a in opts: if o in ("-h", "-H", "-?", "--help", "--HELP"): self.__display_usage = True elif o in ("-v", "-V", "--version", "--VERSION"): self.__display_version = True elif o in ("-l", "-L", "--list", "--LIST"): self.__list_only = True elif o in ("-c", "-C", "--client", "--CLIENT"): self.__client = a def should_display_usage(self): """Return the current value of the display_usage member variable.""" return self.__display_usage def should_display_version(self): """Return the current value of the display_version member variable.""" return self.__display_version def should_list_only(self): """Return the current value of the list_only member variable.""" return self.__list_only def get_client(self): """Return the current value of the client member variable.""" return self.__client def get_workspace(self): """Return the current value of the client member variable.""" return self.__client def get_args(self): """Return the current value of the args member variable.""" return self.__args def InitLocale(): loc = locale.setlocale(locale.LC_ALL, '') if 0 != len(loc) and "nt" == os.name.lower(): pos = loc.rfind('.') if -1 != pos: cp = int(loc[pos + 1:]) win32console.SetConsoleOutputCP(cp) return loc def _find(name, matchFunc=os.path.isfile): path = os.environ['PATH'].split(os.pathsep) path.extend(sys.path) for dirname in path: candidate = os.path.join(dirname, name) if matchFunc(candidate): return candidate return None def find(name): return _find(name) def findDir(name): return _find(name, matchFunc=os.path.isdir) def findP4(): p4name = "p4" if "nt" == os.name.lower(): p4name = "p4.exe" if (None != find(p4name)): return True return False def GetPerforceWorkspaceRoot(workspaceName): """Obtains the root directory of the Perforce client that is currently in use.""" ret = None commandString = "p4 client -o" if (workspaceName != None): commandString = "p4 client -o %s" % (workspaceName, ) command = Command(commandString).run() if 0 == command.returncode: codePage = GetCurrentCodepage()() rootStr = "Root:" foundCount = 0 lines = command.output.splitlines() for line in lines: lineString = line.decode(codePage) pos = lineString.find(rootStr) if -1 != pos: foundCount += 1 if 2 == foundCount: ret = lineString[pos + len(rootStr):].strip() break return ret def GetPerforceHaveFilesList(workspaceName, workspaceRoot): """Returns a list of all files in your Perforce workspace using the p4 have command. Also returns a set of all directories in your Perforce workspace. All items inserted into the returned list and set are converted to lower case.""" haveFilesList = [] perforceDirSet = set() commandString = "p4 have" if (workspaceName != None): commandString = "p4 -c %s have" % (workspaceName, ) command = Command(commandString).run() if 0 == command.returncode: lines = command.output.splitlines() codePage = GetCurrentCodepage()() for line in lines: lineString = line.decode(codePage) pos = lineString.rfind(workspaceRoot) fileName = lineString[pos:] haveFilesList.append(fileName.lower()) dir = os.path.dirname(fileName) perforceDirSet.add(dir.lower()) return haveFilesList, perforceDirSet