"""P4Python - Python interface to Perforce API Perforce is the fast SCM system at www.perforce.com. This package provides a simple interface from Python wrapping the Perforce C++ API to gain performance and ease of coding. Similar to interfaces available for Ruby and Perl. """ classifiers = """\ Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: Freely Distributable Programming Language :: Python Topic :: Software Development :: Libraries :: Python Modules Topic :: Software Development :: Version Control Topic :: Software Development Topic :: Utilities Operating System :: Microsoft :: Windows Operating System :: Unix """ # Customisations needed to use to build: # 1. Set directory for p4api in setup.cfg # See notes in P4API documentation for building with API on different # platforms: # http://www.perforce.com/perforce/doc.current/manuals/p4api/02_clientprog.html from distutils.core import setup, Extension import ConfigParser import os, os.path, sys, re, shutil, stat from platform import uname # Fix for older versions of Python if sys.version_info < (2, 3): _setup = setup def setup(**kwargs): if kwargs.has_key("classifiers"): del kwargs["classifiers"] _setup(**kwargs) class VersionInfo: def __init__(self, p4ApiDir): self.release_year = None self.release_version = None self.release_special = None self.patchlevel = None self.suppdate_year = None self.suppdate_month = None self.suppdate_day = None releasePattern = re.compile("RELEASE\s+=\s+(?P\d+)\s+(?P\d+)\s*(?P\w+)?") patchlevelPattern = re.compile("PATCHLEVEL\s+=\s(?P\d+)") suppdatePattern = re.compile("SUPPDATE\s+=\s(?P\d+)\s+(?P\d+)\s+(?P\d+)") self.patterns=[] self.patterns.append((releasePattern, self.handleRelease)) self.patterns.append((patchlevelPattern, self.handlePatchlevel)) self.patterns.append((suppdatePattern, self.handleSuppDate)) verFile = os.path.join(p4ApiDir, "sample", "Version") if not os.path.exists(verFile): verFile = os.path.join(p4ApiDir, "Version") input = open(verFile) for line in input: for pattern, handler in self.patterns: m = pattern.match(line) if m: handler(**m.groupdict()) input.close() def handleRelease(self, year=0, version=0, special=''): self.release_year = year self.release_version = version self.release_special = special def handlePatchlevel(self, level=0): self.patchlevel = level def handleSuppDate(self, year=0, month=0, day=0): self.suppdate_year = year self.suppdate_month = month self.suppdate_day = day def getP4Version(self): return "%s.%s" % (self.release_year, self.release_version) def getFullP4Version(self): version = "%s.%s" % (self.release_year, self.release_version) if self.release_special: version += ".%s" % self.release_special return version doclines = __doc__.split("\n") NAME = "P4Python" VERSION = "2008.1" PY_MODULES = ["P4"] P4_API_DIR = "p4api" DESCRIPTION=doclines[0] AUTHOR="Perforce Software Inc" MAINTAINER="Perforce Software Inc" AUTHOR_EMAIL="support@perforce.com" MAINTAINER_EMAIL="support@perforce.com" LICENSE="LICENSE.txt" URL="http://www.perforce.com" KEYWORDS="Perforce perforce P4Python" P4_CONFIG_SECTION="p4python_config" P4_CONFIG_P4APIDIR="p4_api" P4_DOC_RELNOTES="../p4-doc/user/p4pythonnotes.txt" P4_RELNOTES="RELNOTES.txt" def copyReleaseNotes(): """Copies the relnotes from the doc directory to the local directory if they exist Returns True if the release notes were copied, otherwise False """ if os.path.exists(P4_DOC_RELNOTES): try: shutil.copy(P4_DOC_RELNOTES, P4_RELNOTES) return True except Exception, e: print e return False else: return False def deleteReleaseNotes(): """Removes RELNOTES.txt from the current directory again""" os.chmod(P4_RELNOTES, stat.S_IWRITE) os.remove(P4_RELNOTES) class PlatformInfo: def __init__(self, apiVersion, releaseVersion): self.libraries=None self.extra_compile_args=None self.define_macros=None self.extra_link_args=None if os.name == "nt": self.ID_OS=self.inStrStr("NTX86") self.ID_REL=self.inStrStr(releaseVersion.getFullP4Version()) self.ID_PATCH=self.inStrStr(releaseVersion.patchlevel) self.ID_API=self.inStrStr(apiVersion.getP4Version()) self.ID_Y=self.inStrStr(releaseVersion.suppdate_year) self.ID_M=self.inStrStr(releaseVersion.suppdate_month) self.ID_D=self.inStrStr(releaseVersion.suppdate_day) self.libraries=["oldnames", "wsock32", "advapi32", # MSVC libs "libclient", "librpc", "libsupp"] # P4API libs self.extra_compile_args=["/DOS_NT", "/DMT", "/DCASE_INSENSITIVE"] self.extra_link_args=["/NODEFAULTLIB:libcmt"] elif os.name == "posix": self.libraries=["client", "rpc", "supp"] # P4API libs self.extra_compile_args = [] # it is UNIX, but which one? Let's ask uname() # details later unameOut = uname() if unameOut[0] == "Linux": unix = "LINUX" release = unameOut[2][0:1] + unameOut[2][2:3] platform = self.architecture(unameOut[4]) elif unameOut[0] == "Darwin": unix = "MACOSX" self.extra_compile_args.append("-fvisibility-inlines-hidden") self.extra_compile_args.append("-DCASE_INSENSITIVE") if unameOut[2][0] == "8": release = "104" elif unameOut[2][0] == "9" : release = "105" self.extra_link_args = ["-framework", "Carbon"] platform = self.architecture(unameOut[4]) # The following is another hack # There is no way to remove the standard compile flags. The default on a MAC # is to build a universal binary. The Perforce API is only built for one # platform, so we need to remove these flags. By setting the environment # variable ARCHFLAGS the defaults can be overriden. if platform == "PPC": os.environ["ARCHFLAGS"] = "-arch ppc" elif platform == "i386": os.environ["ARCHFLAGS"] = "-arch i386" elif unameOut[0] == "SunOS": unix = "SUNSOLARIS" release = re.match("5.(\d+)", unameOut[2]).group(1) platform = self.architecture(unameOut[4]) elif unameOut[0] == 'FreeBSD': unix = "FREEBSD" release = unameOut[2][0] if release == '5': release += unameOut[2][2] platform = self.architecture(unameOut[4]) self.ID_OS = self.inStr(unix + release + platform) self.ID_REL=self.inStr(releaseVersion.getFullP4Version()) self.ID_PATCH=self.inStr(releaseVersion.patchlevel) self.ID_API=self.inStr(apiVersion.getP4Version()) self.ID_Y=self.inStr(releaseVersion.suppdate_year) self.ID_M=self.inStr(releaseVersion.suppdate_month) self.ID_D=self.inStr(releaseVersion.suppdate_day) self.extra_compile_args.append("-DOS_" + unix) self.extra_compile_args.append("-DOS_" + unix + release) self.extra_compile_args.append("-DOS_" + unix + platform) self.extra_compile_args.append("-DOS_" + unix + release + platform) self.define_macros = [('ID_OS', self.ID_OS), ('ID_REL', self.ID_REL), ('ID_PATCH', self.ID_PATCH), ('ID_API', self.ID_API), ('ID_Y', self.ID_Y), ('ID_M', self.ID_M), ('ID_D', self.ID_D)] def inStr(self, str): return '"' + str + '"' def inStrStr(self, str): return '"\\"' + str + '\\""' def architecture(self, str): if str == 'x86_64': return "X86_64" elif re.match('i.86', str): return "X86" elif str == 'i86pc': return "X86" elif str == 'Power Macintosh': return 'PPC' elif str == 'powerpc': return 'PPC' elif str == 'amd64': return 'X86_64' elif str == 'sparc': return 'SPARC' def do_setup(): config = ConfigParser.ConfigParser() config.read('setup.cfg') p4_api_dir = None if config.has_section(P4_CONFIG_SECTION): if config.has_option(P4_CONFIG_SECTION, P4_CONFIG_P4APIDIR): p4_api_dir = config.get(P4_CONFIG_SECTION, P4_CONFIG_P4APIDIR) if not p4_api_dir: print "Error: %s section in setup.cfg needs option %s set to the directory containing the perforce API!" % ( P4_CONFIG_SECTION, P4_CONFIG_P4APIDIR) sys.exit(100) try: apiVersion = VersionInfo(p4_api_dir) releaseVersion = VersionInfo(".") except IOError: print "Cannot find Version file in API dir or distribution dir." print "API path = ", p4_api_dir exit(1) ryear = int(apiVersion.release_year) rversion = int(apiVersion.release_version) if (ryear < 2008) or (ryear == 2008 and rversion < 1): print "API Release %s.%s not supported. Minimum requirement is 2008.1." % (ryear, rversion) print "Please download a more recent API release from the Perforce ftp site." exit(1) else: print "API Release %s.%s" % (ryear, rversion) inc_path = [p4_api_dir, os.path.join(p4_api_dir, "include", "p4")] lib_path = [p4_api_dir, os.path.join(p4_api_dir, "lib")] info = PlatformInfo(apiVersion, releaseVersion) setup(name=NAME, version=VERSION, description=DESCRIPTION, author=AUTHOR, author_email=AUTHOR_EMAIL, maintainer=MAINTAINER, maintainer_email=MAINTAINER_EMAIL, license=LICENSE, url=URL, keywords=KEYWORDS, classifiers = filter(None, classifiers.split("\n")), long_description = "\n".join(doclines[2:]), py_modules=PY_MODULES, ext_modules=[Extension("P4API", ["P4API.cpp", "PythonClientAPI.cpp", "PythonClientUser.cpp", "SpecMgr.cpp", "P4Result.cpp", "PythonThreadGuard.cpp", "PythonMergeData.cpp"], include_dirs = inc_path, library_dirs = lib_path, libraries = info.libraries, extra_compile_args = info.extra_compile_args, define_macros = info.define_macros, extra_link_args = info.extra_link_args )]) if __name__ == "__main__": # The following is a hack to ensure that the RELNOTES.txt are copied from the doc # directory and removed again after the distribution is built copied = False if 'sdist' in sys.argv: copied = copyReleaseNotes() do_setup() if copied: deleteReleaseNotes()