# I'll be the first to admit that the code in this build script is an aesthetic # atrocity, but it's a build script, so I don't care. from __future__ import nested_scopes # Python 2.1 compatibility import ConfigParser, os, os.path, re, shutil, sys, types import distutils.core import distutils.ccompiler import distutils.sysconfig class BuildError(Exception): pass def doCommand(cmd, header='COMMAND EXECUTION ERROR'): print '\t' + cmd taskOutStream = os.popen(cmd) taskOutput = taskOutStream.read() taskRetCode = taskOutStream.close() if taskRetCode is not None: raise BuildError('\n%s\n Command [%s] died with error:\n[%s]\n' % (header, cmd, taskOutput) ) return taskOutput def doLibConvCmd(cmd): return doCommand(cmd, LIBCONVERSION_ERROR_HEADER) def determineWindowsSystemDir(): if sys.platform.lower() == 'cygwin': return doCommand('cygpath --sysdir')[:-1] # Trailing newline. else: # (normal win32) # If I were willing to introduce a win32all dependency into this build # script, this function would be replaced by win32api.GetSystemDirectory. winDir = os.environ.get('SYSTEMROOT', os.environ.get('WINDIR', 'C:\\Windows')) winSysDir = os.path.join(winDir, 'system32') if not os.path.isdir(winSysDir): winSysDir = os.path.join(winDir, 'system') return winSysDir # Be careful about changing these messages; the build documentation refers to them. PYTHON_SYSTEM_ERROR_HEADER = 'PYTHON SYSTEM ERROR:' COMPILER_CONFIGURATION_ERROR_HEADER = 'COMPILER CONFIGURATION ERROR:' KIDB_DISTRIBUTION_ERROR_HEADER = 'KINTERBASDB DISTRIBUTION ERROR:' LIBCONVERSION_ERROR_HEADER = 'LIBRARY CONVERSION ERROR:' AUTODETECTION_ERROR_HEADER = 'LIBRARY AUTODETECTION ERROR:' MANUAL_SPECIFICATION_ERROR_HEADER = 'LIBRARY MANUAL SPECIFICATION ERROR:' DISTUTILS_URL = 'http://www.python.org/sigs/distutils-sig/distutils.html' VERSION_FILE = 'version.txt' CONFIG_FILE = 'setup.cfg' PYTHON_VERSION_THRESHOLD = (2,1) if sys.version_info[:2] < PYTHON_VERSION_THRESHOLD: sys.stderr.write( '%s\n' ' Beginning with kinterbasdb 3.1, kinterbasdb' ' no longer officially supports\n versions of Python prior to %s.\n\n' % (PYTHON_SYSTEM_ERROR_HEADER, PYTHON_VERSION_THRESHOLD) ) sys.exit(-1) DEBUG = int(os.environ.get('KINTERBASDB_DEBUG', 0)) # Module name and version number: kinterbasdb_name = 'kinterbasdb' # Retrive the kinterbasdb version number from a central text file for the sake # of maintainability: try: kinterbasdb_version = open(VERSION_FILE).read().strip() except IOError: raise BuildError( "\n%s\n" " File 'version.txt' is missing; cannot determine kinterbasdb" " version." % KIDB_DISTRIBUTION_ERROR_HEADER ) argJam = ' '.join(sys.argv[1:]).lower() # These config parameters are user-specifiable via setup.cfg: CHECKED_BUILD = 0 ENABLE_FIELD_PRECISION_DETERMINATION = 1 DATABASE_IS_FIREBIRD = None DATABASE_HOME_DIR = None DATABASE_INCLUDE_DIR = None DATABASE_LIB_DIR = None DATABASE_LIB_NAME = None # These config parameters are not drawn from setup.cfg: CUSTOM_PREPROCESSOR_DEFS = [] PLATFORM_SPECIFIC_INCLUDE_DIRS = [] PLATFORM_SPECIFIC_LIB_DIRS = [] PLATFORM_SPECIFIC_LIB_NAMES = [] PLATFORM_SPECIFIC_EXTRA_COMPILER_ARGS = [] PLATFORM_SPECIFIC_EXTRA_LINKER_ARGS = [] # See if the user manually specified various build options in the setup config # file. If so, skip autodetection for the options that the user has specified. config = ConfigParser.ConfigParser() config.read(CONFIG_FILE) if config.has_section('manual_config'): def _boolConfOpt(name): if config.has_option('manual_config', name): return config.getboolean('manual_config', name) else: return 0 CHECKED_BUILD = _boolConfOpt('checked_build') ENABLE_FIELD_PRECISION_DETERMINATION = _boolConfOpt('enable_field_precision_determination') DATABASE_IS_FIREBIRD = _boolConfOpt('database_is_firebird') if config.has_option('manual_config', 'database_home_dir'): DATABASE_HOME_DIR = config.get('manual_config', 'database_home_dir') if config.has_option('manual_config', 'database_include_dir'): DATABASE_INCLUDE_DIR = config.get('manual_config', 'database_include_dir') if config.has_option('manual_config', 'database_lib_dir'): DATABASE_LIB_DIR = config.get('manual_config', 'database_lib_dir') if config.has_option('manual_config', 'database_lib_name'): DATABASE_LIB_NAME = config.get('manual_config', 'database_lib_name') if DEBUG: print "*** CONFIG OPTIONS SPECIFIED IN %s SECTION 'manual_config' ***" % CONFIG_FILE for key in config.options('manual_config'): print '%s:' % (key) print ' %s' % (config.get('manual_config', key)) ALL_AUTODETECT_OPTIONS_MANUALLY_SPECIFIED = ( DATABASE_IS_FIREBIRD is not None and DATABASE_HOME_DIR and DATABASE_INCLUDE_DIR and DATABASE_LIB_DIR and DATABASE_LIB_NAME ) def verifyAutodetectedDatabaseIncludeDir(): if not os.path.exists(DATABASE_INCLUDE_DIR): sys.stderr.write( "%s\n" " Cannot autodetect the database header file directory.\n" " (Tried %s)\n" " Try specifying the 'database_include_dir' option in\n" " the 'manual_config' section of the setup config file (%s).\n" % (AUTODETECTION_ERROR_HEADER, DATABASE_INCLUDE_DIR, CONFIG_FILE) ) sys.exit(1) def verifyUserSpecifiedDatabaseIncludeDir(): if not os.path.exists(DATABASE_INCLUDE_DIR): sys.stderr.write( "%s\n" " The user-specified database header file directory\n" " %s\n" " does not exist.\n" " Try modifying the 'database_include_dir' option in\n" " the 'manual_config' section of the setup config file (%s),\n" " or comment out that option to force this script to\n" " to autodetect it.\n" % (MANUAL_SPECIFICATION_ERROR_HEADER, DATABASE_INCLUDE_DIR, CONFIG_FILE) ) sys.exit(1) def verifyAutodetectedDatabaseLibraryDir(): if not os.path.exists(DATABASE_LIB_DIR): sys.stderr.write( "%s\n" " Cannot autodetect the database lib directory.\n" " (Tried %s)\n" " Try specifying the 'database_include_dir' option in\n" " the 'manual_config' section of the setup config file (%s).\n" % (AUTODETECTION_ERROR_HEADER, DATABASE_LIB_DIR, CONFIG_FILE) ) sys.exit(1) def verifyUserSpecifiedDatabaseLibraryDir(): if not os.path.exists(DATABASE_LIB_DIR): sys.stderr.write( "%s\n" " The user-specified database lib directory\n" " %s\n" " does not exist.\n" " Try modifying the 'database_lib_dir' option in\n" " the 'manual_config' section of the setup config file (%s),\n" " or comment out that option to force this script to\n" " to autodetect it.\n" % (MANUAL_SPECIFICATION_ERROR_HEADER, DATABASE_LIB_DIR, CONFIG_FILE) ) sys.exit(1) def findSpacelessDirName(d): # On Windows, try to find the spaceless version of the provided directory # name. # This function was added on 2002.03.14 as part of an ugly hack to # surmount a bug in the distutils package. # Sometime distutils causes None to be fed to this function. if not d: return d d = os.path.normpath(os.path.abspath(d)) if ' ' not in d: return d # If d doesn't exist, its short equivalent can't be determined. # However, for the purposes of this program (which is solely for # convenience anyway) it's better just to risk feeding the # compiler/linker a path with a space in it than it is to raise # an exception when there's still a *chance* of success. if not os.path.isdir(d): return d try: import win32api return os.path.normcase(win32api.GetShortPathName(d)) except ImportError: # Since win32api is not available, we'll revert to a lame, # manual approximation of GetShortPathName. pass ds = d.split(os.sep) # Split into components. if DEBUG: print 'ds is', ds ds[0] = ds[0] + '\\' # Add slash back onto drive designation so that # it's like c:\ rather than just c: dsNoSpaces = [] # Will contain a version of the directory components # with all spaces removed. for x in range(len(ds)): dir = ds[x] if DEBUG: print 'dir is', dir if ' ' not in dir: shortDir = dir else: fullDir = apply(os.path.join, ds[:x + 1]) if DEBUG: print 'fullDir is', fullDir # Must deal with names like 'abc de' that have their space # before the sixth character. dirNoSpaces = dir.replace(' ', '') if len(dirNoSpaces) < 6: shortDirBase = dirNoSpaces else: shortDirBase = dirNoSpaces[:6] # Search for shortDirBase~i until we find it. shortDir = None i = 1 while i < 9: # This code doesn't handle situations where there are # more than nine directories with the same prefix. maybeShortDir = '%s~%d' % (shortDirBase, i) fullMaybeShortDir = os.path.join( os.path.dirname(fullDir), maybeShortDir) if not os.path.isdir(fullMaybeShortDir): continue # There follows a *really* lame approximation of # os.path.samefile, which is not available on Windows. if os.listdir(fullMaybeShortDir) == os.listdir(fullDir): shortDir = maybeShortDir break i = i + 1 if shortDir is None: raise Exception('Unable to find shortened version of' ' directory named %s' % d ) dsNoSpaces.append(shortDir) if DEBUG: print 'dsNoSpaces is', dsNoSpaces return os.path.normcase(apply(os.path.join, dsNoSpaces)) # Perform generic compilation parameter setup, then switch to platform- # specific. origWorkingDir = os.path.abspath(os.curdir) # Autodetect Python directory info. if DEBUG: print '*** PYTHON SETTINGS ***' pythonHomeDir = sys.exec_prefix pythonPkgDir = distutils.sysconfig.get_python_lib() if DEBUG: print '\tPython home dir:', pythonHomeDir print '\tPython package dir:', pythonPkgDir # Begin platform-specific compilation parameter setup: osIsWindows = sys.platform.lower().startswith('win') compilerIsMinGW = 0 compilerIsGCC = 0 if osIsWindows: ALL_AUTODETECT_WINDOWS_REGISTRY_OPTIONS_MANUALLY_SPECIFIED = ( DATABASE_HOME_DIR and DATABASE_INCLUDE_DIR and DATABASE_LIB_DIR and DATABASE_LIB_NAME ) CUSTOM_PREPROCESSOR_DEFS.append( ('WIN32', None) ) pyVersionSuffix = ''.join( [str(n) for n in sys.version_info[:2]] ) pyLibName = 'python%s.lib' % pyVersionSuffix # 2003.03.28: Accomodate source dists of Python on Windows (give the # PCBuild\pythonVV.lib file (if any) precedence over the # libs\pythonVV.lib file (if any)): pyLibsDir = os.path.join(pythonHomeDir, 'PCbuild') if not os.path.exists(os.path.join(pyLibsDir, pyLibName)): pyLibsDir = os.path.join(pythonHomeDir, 'libs') pyConventionalLibPath = os.path.join(pyLibsDir, pyLibName) # If this is a source distribution of Python, add a couple of necessary # include and lib directories. pcbuildDir = os.path.join( os.path.dirname(distutils.sysconfig.get_python_inc()), 'PCBuild' ) if os.path.exists(pcbuildDir): PLATFORM_SPECIFIC_LIB_DIRS.append(pcbuildDir) pySrcDistExtraIncludeDir = os.path.join( os.path.dirname(distutils.sysconfig.get_python_inc()), 'PC' ) PLATFORM_SPECIFIC_INCLUDE_DIRS.append(pySrcDistExtraIncludeDir) # Verify the various directories (such as include and library dirs) that # will be used during compilation. # Open the registry in preparation for reading various installation # directories from it. try: import _winreg except ImportError: # If the user has manually specified all of the options that would # require registry access to autodetect, we can proceed despite the # lack of _winreg. if not ALL_AUTODETECT_WINDOWS_REGISTRY_OPTIONS_MANUALLY_SPECIFIED: sys.stderr.write( "%s\n" " Cannot import the standard package '_winreg'.\n" " _winreg did not join the standard library until\n" " Python 2.0. If you are using a source distribution\n" " of Python 2.0 or later, you may have simply forgotten\n" " to compile the _winreg C extension.\n" " You can get around the lack of _winreg by manually\n" " specifying all of the configuration options in the\n" " 'manual_config' section of the setup config file (%s)." % (AUTODETECTION_ERROR_HEADER, CONFIG_FILE) ) sys.exit(1) if not ALL_AUTODETECT_WINDOWS_REGISTRY_OPTIONS_MANUALLY_SPECIFIED: try: r = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) except WindowsError, e: sys.stderr.write( "%s\n" " Unable to connect to the HKEY_LOCAL_MACHINE section of\n" " the Windows registry.\n" " The specific error encountered is:\n" " %s" % (AUTODETECTION_ERROR_HEADER, str(e)) ) sys.exit(1) if DEBUG: print '*** DATABASE SETTINGS ***' # Autodetect database directory info if the user did not specify it. if not DATABASE_HOME_DIR: def findDatabaseHomeDir(databaseInfoKey, databaseHomeValueName): databaseCurrentVersionKey = _winreg.OpenKey(r, databaseInfoKey) try: return _winreg.QueryValueEx( databaseCurrentVersionKey, databaseHomeValueName )[0] finally: _winreg.CloseKey(databaseCurrentVersionKey) # Try to find Firebird first; revert to Interbase only if necessary. try: try: # 2003.11.10: Firebird 1.5 RC7 changed the registry structure. DATABASE_HOME_DIR = findDatabaseHomeDir( r'SOFTWARE\Firebird Project\Firebird Server\Instances', 'DefaultInstance' ) DATABASE_IS_FIREBIRD = 1 except WindowsError: try: # Firebird 1.0-Firebird 1.5 RC6: DATABASE_HOME_DIR = findDatabaseHomeDir( r'SOFTWARE\FirebirdSQL\Firebird\CurrentVersion', 'RootDirectory' ) DATABASE_IS_FIREBIRD = 1 except WindowsError: # Revert to Interbase. DATABASE_IS_FIREBIRD = 0 DATABASE_HOME_DIR = findDatabaseHomeDir( r'SOFTWARE\Borland\InterBase\CurrentVersion', 'RootDirectory' ) except WindowsError, e: sys.stderr.write( "%s\n" " Unable to retrieve database directory from the Windows" " registry.\n" " Try specifying the 'database_home_dir' option in the\n" " 'manual_config' section of the setup config file (%s).\n" " The specific error encountered is:\n" " %s" % (AUTODETECTION_ERROR_HEADER, CONFIG_FILE, str(e)) ) sys.exit(1) if DATABASE_INCLUDE_DIR: verifyUserSpecifiedDatabaseIncludeDir() databaseSDKDir = os.path.dirname(DATABASE_INCLUDE_DIR) else: databaseSDKDir = os.path.join(DATABASE_HOME_DIR, 'SDK') if DATABASE_IS_FIREBIRD or not os.path.exists(databaseSDKDir): databaseSDKDir = DATABASE_HOME_DIR DATABASE_INCLUDE_DIR = os.path.join(databaseSDKDir, 'include') if DEBUG: print ( '\tDATABASE_INCLUDE_DIR exists at\n\t %s: %d' % (DATABASE_INCLUDE_DIR, os.path.exists(DATABASE_INCLUDE_DIR)) ) verifyAutodetectedDatabaseIncludeDir() if DATABASE_LIB_DIR: verifyUserSpecifiedDatabaseLibraryDir() else: DATABASE_LIB_DIR = os.path.join(databaseSDKDir, 'lib') verifyAutodetectedDatabaseLibraryDir() # Perform compiler-specific setup. if not DATABASE_LIB_NAME: DATABASE_LIB_NAME = 'gds32' # I should discover a way to ask distutils what compiler it's # configured to use--the current code would only detect a compiler # change via the command line. I've looked at the compiler subpackage # of distutils, and can't find any such facility (though it can be # queried for the system's default compiler). customCompilerName = 'msvc' match = re.search(r'--compiler=(?P\S+)', argJam) if match: customCompilerName = match.group('cname') else: match = re.search(r'-c\s*(?P\S+)', argJam) if match: customCompilerName = match.group('cname') compilerIsMinGW = customCompilerName.lower().startswith('mingw') if customCompilerName == 'msvc': # 2004.11.05: SF 1056684: # If MSVC is assumed to be the compiler (which it is, on Windows, # unless the user explicitly indicates otherwise), but we're not # actually compiling (as when this script is invoked with # 'setup.py install --skip-build'), skip the registry lookups that'll # break if MSVC is not installed. if argJam.find('--skip-build') == -1: # 2004.10.24: # Autodetect support files for "Microsoft Visual C++ Toolkit 2003": if sys.version_info[:2] >= (2,4): directoriesKey = _winreg.OpenKey(r, r'SOFTWARE\Microsoft\MicrosoftSDK\Directories' ) try: windowsSDKDir = _winreg.QueryValueEx( directoriesKey, 'Install Dir' )[0] finally: _winreg.CloseKey(directoriesKey) windowsSDKLibDir = os.path.join(windowsSDKDir, 'Lib') PLATFORM_SPECIFIC_LIB_DIRS.append(windowsSDKLibDir) # End "Microsoft Visual C++ Toolkit 2003" support code. else: # 2004.10.28: Better support for building with VStudio 6 when # "Register Environment Variables" was not selected during the # installation process (vcvars32.bat doesn't quite paper over # all of the differences). vcKey = _winreg.OpenKey(r, r'SOFTWARE\Microsoft\DevStudio\6.0\Products\Microsoft Visual C++' ) try: vcDir = _winreg.QueryValueEx(vcKey, 'ProductDir')[0] finally: _winreg.CloseKey(vcKey) PLATFORM_SPECIFIC_INCLUDE_DIRS.append(os.path.join(vcDir, 'Include')) PLATFORM_SPECIFIC_LIB_DIRS.append(os.path.join(vcDir, 'Lib')) if not DATABASE_IS_FIREBIRD: DATABASE_LIB_DIR = os.path.join(databaseSDKDir, r'lib_ms') if not os.path.exists(DATABASE_LIB_DIR): DATABASE_LIB_DIR = os.path.join(databaseSDKDir, r'lib') if not DATABASE_LIB_NAME or DATABASE_LIB_NAME == 'gds32': DATABASE_LIB_NAME = 'gds32_ms' elif customCompilerName == 'bcpp': print ' *** BCPP LIBRARY GENERATION : begin ***' COMPILER_EXE_NAME = 'bcc32.exe' # Try to find the home directory of the Borland compiler by searching # each directory listed in the PATH. # If I were willing to depend on win32all, win32api.FindExecutable # would replace this code. osPath = os.environ['PATH'].split(os.pathsep) bccHome = None for dir in osPath: if os.path.exists(os.path.join(dir, COMPILER_EXE_NAME)): bccHome = os.path.split(dir)[0] break else: # Couldn't find it. sys.stderr.write( "%s\n" " Unable to find the home directory of the Borland" " compiler.\n" " You must add the 'bin' subdirectory of the" " compiler's\n" " home directory to your PATH.\n" " One way to do this is to type a command of the" " format\n" " SET PATH=%%PATH%%;c:\\EXAMPLE_DIR\\bin\n" " in the same command prompt you use to run the" " kinterbasdb setup script." % (COMPILER_CONFIGURATION_ERROR_HEADER,) ) sys.exit(1) # Override the default behavior of distutils.bcppcompiler.BCPPCompiler # in order to force it to issue the correct command. from distutils.bcppcompiler import BCPPCompiler def _makeDirNameSpacless(kwargs, argName): x = kwargs.get(argName, None) if x is None: return elif isinstance(x, types.StringType): kwargs[argName] = findSpacelessDirName(x) else: # sequence of strings kwargs[argName] = [ findSpacelessDirName(d) for d in x ] class BCPP_UGLY_Hack(BCPPCompiler): def compile (self, *args, **kwargs): bccIncludePreargDir = findSpacelessDirName(r'%s\include' % bccHome) bccLibPreargDir = findSpacelessDirName(r'%s\lib' % bccHome) bccLibPSDKPreargDir = findSpacelessDirName(r'%s\lib\psdk' % bccHome) kwargs['extra_preargs'] = ( [ r'-I"%s"' % bccIncludePreargDir, r'-L"%s"' % bccLibPreargDir, r'-L"%s"' % bccLibPSDKPreargDir ] + kwargs.get('extra_preargs', []) ) for argName in ('output_dir', 'include_dirs'): _makeDirNameSpacless(kwargs, argName) return BCPPCompiler.compile(self, *args, **kwargs) def link (self, *args, **kwargs): ilinkLibPreargDir = findSpacelessDirName(r'%s\lib' % bccHome) ilinkLibPSDKPreargDir = findSpacelessDirName(r'%s\lib\psdk' % bccHome) myPreArgs = [ r'/L"%s"' % ilinkLibPreargDir, r'/L"%s"' % ilinkLibPSDKPreargDir ] if not kwargs.has_key('extra_preargs'): argsAsList = list(args) argsAsList[9] = myPreArgs # see distuils.ccompiler.py args = tuple(argsAsList) else: kwargs['extra_preargs'] = myPreArgs + kwargs.get['extra_preargs'] for argName in ( 'output_dir', 'library_dirs', 'runtime_library_dirs', 'build_temp' ): _makeDirNameSpacless(kwargs, argName) return BCPPCompiler.link(self, *args, **kwargs) # Force distutils to use our BCPP_UGLY_Hack class rather than the # default BCPPCompiler class. compilerSetupTuple = distutils.ccompiler.compiler_class['bcpp'] import distutils.bcppcompiler distutils.bcppcompiler.BCPP_UGLY_Hack = BCPP_UGLY_Hack distutils.ccompiler.compiler_class['bcpp'] = ( compilerSetupTuple[0], 'BCPP_UGLY_Hack', compilerSetupTuple[2] ) # Use the Borland command-line library conversion tool coff2omf to # create a Borland-compiler-compatible library file, # "pythonVV_bcpp.lib", from the standard "pythonVV.lib". libName = os.path.join(pyLibsDir, 'python%s_bcpp.lib' % pyVersionSuffix) if not os.path.exists(libName): print 'setup.py is trying to create %s' % libName coff2omfCommand = ('coff2omf %s %s' % (pyConventionalLibPath, libName)) os.system(coff2omfCommand) # Do this test instead of checking the return value of # os.system, which will not reliably indicate an error # condition on Win9x. if not os.path.exists(libName): sys.stderr.write( "%s\n" " Unable to create a Borland-compatible Python" " library file using the\n" " coff2omf utility.\n" " Tried command:\n" " %s" % (COMPILER_CONFIGURATION_ERROR_HEADER, coff2omfCommand) ) sys.exit(1) assert os.path.isfile(libName) print ' *** BCPP LIBRARY GENERATION : end ***' elif compilerIsMinGW: # 2003.08.05: print ' *** MINGW LIBRARY GENERATION : begin ***' # Use the MinGW tools pexports and dlltool to create a GCC-compatible # library file for Python. # Firebird 1.5 already includes a suitable library file (fbclient_ms.lib). if DATABASE_LIB_NAME != 'fbclient_ms': if DATABASE_LIB_NAME is not None: print ( '\tIgnoring your "%s" library name setting in favor of\n' '\t "fbclient_ms", which is the proper choice for MinGW.' % DATABASE_LIB_NAME ) DATABASE_LIB_NAME = 'fbclient_ms' winSysDir = determineWindowsSystemDir() # Python (pythonVV.lib -> libpythonVV.a): pyDLL = 'python%s.dll' % pyVersionSuffix pyDLLPathPossibilies = [os.path.join(d, pyDLL) for d in (pythonHomeDir, pcbuildDir, winSysDir) ] for pyDLLPath in pyDLLPathPossibilies: if os.path.isfile(pyDLLPath): break else: raise BuildError("""\n%s\n Can't find Python DLL "%s".""" % (LIBCONVERSION_ERROR_HEADER, pyDLL) ) libName = 'libpython%s.a' % pyVersionSuffix libUltimateDest = os.path.join(pyLibsDir, libName) defFilename = 'python%s.def' % pyVersionSuffix if os.path.isfile(libUltimateDest): print ('\tMinGW-compatible Python library already exists at:\n\t %s' % libUltimateDest ) else: print ( '\n\tsetup.py is trying to create MinGW-compatible Python' ' library at:\n' '\t "%s"' % libUltimateDest ) os.chdir(os.path.dirname(pyDLLPath)) try: doLibConvCmd('pexports %s > %s' % (pyDLL, defFilename)) doLibConvCmd( 'dlltool --dllname %s --def %s --output-lib %s' % (pyDLL, defFilename, libName) ) os.remove(defFilename) # With source builds of some versions of Python, the Python DLL # is located in the same directory that distutils declares to # be the "library directory", so the generated library file # shouldn't be moved. if os.path.dirname(libUltimateDest).lower() != os.path.abspath(os.curdir).lower(): shutil.copyfile(libName, libUltimateDest) os.remove(libName) finally: os.chdir(origWorkingDir) assert os.path.isfile(libUltimateDest) print ' *** MINGW LIBRARY GENERATION : end ***\n' if DEBUG: print '\tDATABASE_LIB_DIR exists at\n\t %s: %d' \ % (DATABASE_LIB_DIR, os.path.exists(DATABASE_LIB_DIR)) print '\tDATABASE_LIB_NAME is\n\t %s' % DATABASE_LIB_NAME elif sys.platform.lower() == 'cygwin': # 2003.08.05: print ' *** CYGWIN LIBRARY GENERATION : begin ***' if DATABASE_LIB_NAME != 'fbclient': if DATABASE_LIB_NAME is not None: print ( '\tIgnoring your "%s" library name setting in favor of\n' '\t "fbclient", which is the proper choice for Cygwin.' % DATABASE_LIB_NAME ) DATABASE_LIB_NAME = 'fbclient' winSysDir = determineWindowsSystemDir() # 2003.11.10: Switched to FB 1.5 RC7+ reg structure. # baseRegLoc = '/proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/FirebirdSQL/Firebird/CurrentVersion' regInstLoc = ( '/proc/registry/HKEY_LOCAL_MACHINE/SOFTWARE/Firebird Project' '/Firebird Server/Instances/DefaultInstance' ) # Read the location of Firebird from the Windows registry. try: fbDir = doLibConvCmd('cat "%s"' % regInstLoc)[:-1] # Trailing null byte. except BuildError: raise BuildError("\n%s\n Windows registry settings for Firebird 1.5" " were not found. Try running Firebird's instreg.exe utility." " (Firebird 1.5 before RC7, Firebird 1.0, and Interbase are not" " supported.)" % AUTODETECTION_ERROR_HEADER ) if fbDir.endswith('\\'): fbDir = fbDir[:-1] # Trailing backslash. fbDir = fbDir.replace('\\', '/') fbDir = doLibConvCmd('cygpath --unix %s' % fbDir)[:-1] # Trailing null byte. if DATABASE_INCLUDE_DIR: verifyUserSpecifiedDatabaseIncludeDir() else: DATABASE_INCLUDE_DIR = os.path.join(fbDir, 'include') verifyAutodetectedDatabaseIncludeDir() libUltimateDest = '/usr/lib/libfbclient.a' libUltimateDir, libName = os.path.split(libUltimateDest) if os.path.isfile(libUltimateDest): print ('\tCygwin-compatible Firebird library already exists at:\n\t %s' % libUltimateDest ) else: print ( '\n\tsetup.py is trying to create cygwin-compatible Firebird' ' library at:\n' '\t "%s"' % libUltimateDest ) fbClientLibFilename = os.path.join(fbDir, 'lib', 'fbclient_ms.lib') origCurDir = os.path.abspath(os.curdir) os.chdir(winSysDir) try: # Create def file containing symbols from fbclient_ms.lib. doLibConvCmd( '''(echo EXPORTS; nm %s | grep " T _"''' ''' | sed 's/.* T _//g' | sed 's/@.*$//g')''' ''' > fbclient.def''' % fbClientLibFilename ) # End lame pexports substitute. # Create lib file from DLL and the just-generated def file. doLibConvCmd( 'dlltool --dllname fbclient.dll --def fbclient.def --output-lib %s' % libName ) os.remove('fbclient.def') # Move the lib file to a location where cygwin-GCC's linker will # find it. shutil.copyfile(libName, libUltimateDest) os.remove(libName) finally: os.chdir(origCurDir) assert os.path.isfile(libUltimateDest) print ' *** CYGWIN LIBRARY GENERATION : end ***\n' elif sys.platform.lower() == 'darwin': # Based on Patch 909886 (Piet van oostrum) PLATFORM_SPECIFIC_EXTRA_LINKER_ARGS.extend(['-framework', 'Firebird']) # Don't override the include dir specified in setup.cfg, if any: if not DATABASE_INCLUDE_DIR: DATABASE_INCLUDE_DIR = '/Library/Frameworks/Firebird.framework/Headers' else: # not win32, cygwin, or darwin # If the platform isn't Linux, issue a warning. if not sys.platform.lower().startswith('linux'): sys.stderr.write("Warning: The kinterbasdb setup code was not" " specifically written to support your platform (%s), and" " may not work properly.\n" % sys.platform ) # Is libcrypt necessary on all POSIX OSes, or just Linux? # Until someone informs me otherwise, I'll assume all. if os.name == 'posix': PLATFORM_SPECIFIC_LIB_NAMES.append('crypt') # Verify the various directories (such as include and library dirs) that # will be used during compilation. # Assumption: # This is a Unix-like OS, where a proper installation routine would have # placed the database [header, library] files in system-wide dirs. # We have no way of knowing beyond the shadow of a doubt whether that # has happened (as opposed to the situation on Windows, where we can # consult the registry to determine where a binary installer placed its # files), so we'll just let the compiler complain if it can't find the # [header, library] files. # If, on the other hand, the user manually specified the directories, we # verify that they exist before invoking the compiler. if DATABASE_INCLUDE_DIR: # the user manually specified it verifyUserSpecifiedDatabaseIncludeDir() if DATABASE_LIB_DIR: # the user manually specified it verifyUserSpecifiedDatabaseLibraryDir() # 2003.04.12: # On FreeBSD 4, the header and library files apparently are not made # visible by default. # This script attempts to "autodetect" an installation at the default # location, but only if: # - no DATABASE_HOME_DIR has been manually specified # - the default installation directory actually exists # # This "autodetection" will probably work for some other Unixes as well. if not DATABASE_HOME_DIR: DEFAULT_FREEBSD_HOME_DIR = '/usr/local/firebird' if os.path.isdir(DEFAULT_FREEBSD_HOME_DIR): DATABASE_HOME_DIR = DEFAULT_FREEBSD_HOME_DIR if not DATABASE_INCLUDE_DIR: DATABASE_INCLUDE_DIR = os.path.join(DATABASE_HOME_DIR, 'include') if not DATABASE_LIB_DIR: DATABASE_LIB_DIR = os.path.join(DATABASE_HOME_DIR, 'lib') if not DATABASE_LIB_NAME: # 2003.07.29: If the user hasn't specified the name of the database # library, this script will now guess its way from the most recent # known library back to the oldest, most conservative option. # The goal of this smarter probing is to allow kinterbasdb to build out # of the box with Firebird 1.5, without *requiring* the user to modify # setup.cfg to specify the correct library name. # # YYY: This isn't the most proper way to probe for libraries using # distutils, but I must admit that propriety isn't my highest priority # in this setup script. import distutils.command.config as cmd_conf import distutils.dist as dist_dist class _ConfigUglyHack(cmd_conf.config): # _ConfigUglyHack circumvents a distutils problem brought to light # on Unix by this script's abuse of the distutils. def try_link(self, *args, **kwargs): self.compiler.exe_extension = '' # ('' rather than None) return cmd_conf.config.try_link(self, *args, **kwargs) cfg = _ConfigUglyHack(dist_dist.Distribution()) for possibleLib in ('fbclient', 'fbembed'): if cfg.check_lib(possibleLib): DATABASE_LIB_NAME = possibleLib break else: DATABASE_LIB_NAME = 'gds' # On any non-Windows platform, assume that GCC is the compiler: compilerIsGCC = ((compilerIsMinGW or not osIsWindows) and 1) or 0 if compilerIsGCC: # By default, distutils includes the -fno-strict-aliasing flag on *nix-GCC, # but not on MinGW-GCC. PLATFORM_SPECIFIC_EXTRA_COMPILER_ARGS.append('-fno-strict-aliasing') # Now finished with platform-specific compilation parameter setup. # Create a list of all INCLUDE dirs to be passed to setup(): allIncludeDirs = [] # Add Python include directory: allIncludeDirs.append(distutils.sysconfig.get_python_inc()) if len(PLATFORM_SPECIFIC_INCLUDE_DIRS) > 0: allIncludeDirs.extend(PLATFORM_SPECIFIC_INCLUDE_DIRS) if DATABASE_INCLUDE_DIR: allIncludeDirs.append(DATABASE_INCLUDE_DIR) # Create a list of all LIB names to be passed to setup(): allLibNames = [] if DATABASE_LIB_NAME: allLibNames.append(DATABASE_LIB_NAME) allLibNames.extend(PLATFORM_SPECIFIC_LIB_NAMES) # Create a list of all LIB directories to be passed to setup(): allLibDirs = [] if len(PLATFORM_SPECIFIC_LIB_DIRS) > 0: allLibDirs.extend(PLATFORM_SPECIFIC_LIB_DIRS) if DATABASE_LIB_DIR: allLibDirs.append(DATABASE_LIB_DIR) # Create a list of all macro definitions that must be passed to distutils. allMacroDefs = [] if len(CUSTOM_PREPROCESSOR_DEFS) > 0: allMacroDefs.extend(CUSTOM_PREPROCESSOR_DEFS) # Create a list of all extra options to pass to the compiler, linker. allExtraCompilerArgs = [] if CHECKED_BUILD: if osIsWindows and customCompilerName == 'msvc': checkEnabler = '/UNDEBUG' else: checkEnabler = '-UNDEBUG' allExtraCompilerArgs.append(checkEnabler) if ENABLE_FIELD_PRECISION_DETERMINATION: allMacroDefs.append(('DETERMINE_FIELD_PRECISION', '1')) if len(PLATFORM_SPECIFIC_EXTRA_COMPILER_ARGS) > 0: allExtraCompilerArgs.extend(PLATFORM_SPECIFIC_EXTRA_COMPILER_ARGS) allExtraLinkerArgs = [] if len(PLATFORM_SPECIFIC_EXTRA_LINKER_ARGS) > 0: allExtraLinkerArgs.extend(PLATFORM_SPECIFIC_EXTRA_LINKER_ARGS) extensionModules = [ distutils.core.Extension( "kinterbasdb._kinterbasdb", sources=["_kinterbasdb.c", "_kievents.c"], libraries=allLibNames, include_dirs=allIncludeDirs, library_dirs=allLibDirs, define_macros=allMacroDefs, extra_compile_args=allExtraCompilerArgs, extra_link_args=allExtraLinkerArgs ), ] allPythonModules = [ 'kinterbasdb.__init__', 'kinterbasdb.k_exceptions', 'kinterbasdb.typeconv_naked', 'kinterbasdb.typeconv_backcompat', 'kinterbasdb.typeconv_23plus', 'kinterbasdb.typeconv_fixed_stdlib', 'kinterbasdb.typeconv_fixed_fixedpoint', 'kinterbasdb.typeconv_datetime_naked', 'kinterbasdb.typeconv_datetime_stdlib', 'kinterbasdb.typeconv_datetime_mx', 'kinterbasdb.typeconv_text_unicode', # Python 2.1 compatibility hacks: 'kinterbasdb.typeconv_util_isinstance', 'kinterbasdb._py21incompat', ] if kinterbasdb_version >= '3.2': allPythonModules.extend([ # 2004.11.10: Python 2.4 decimal support: 'kinterbasdb.typeconv_24plus', 'kinterbasdb.typeconv_fixed_decimal', ]) # 2003.02.18: # Build somewhat differently if we're dealing with an IB version before 6.0. # (Innocent until proven guilty.) isIBLessThan_6_0 = 0 for incDir in allIncludeDirs: headerFilename = os.path.join(incDir, 'ibase.h') if os.path.exists(headerFilename): # Using the isc_decode_sql_time symbol as the detector is kinda arbitrary. if open(headerFilename).read().find('isc_decode_sql_time') == -1: isIBLessThan_6_0 = 1 break if isIBLessThan_6_0: print >> sys.stderr, \ 'WARNING: Not building the kinterbasdb._services module because' \ ' IB 5.5 does not support it.' else: # Only include the services module if dealing with >= IB 6.0. allPythonModules.append('kinterbasdb.services') extensionModules.append( distutils.core.Extension( "kinterbasdb._kiservices", sources=["_kiservices.c"], libraries=allLibNames, include_dirs=allIncludeDirs, library_dirs=allLibDirs, define_macros=allMacroDefs, extra_compile_args=allExtraCompilerArgs, extra_link_args=allExtraLinkerArgs ) ) # Now we've detected and verified all compilation parameters, and are ready to # compile. if DEBUG: print '*** SETTINGS DETECTION PHASE COMPLETE; READY FOR BUILD ***' print ("\tThe DEBUG flag is enabled, so the setup script stops\n" "\t before actually invoking the distutils setup procedure." ) sys.exit(0) # The MEAT: distutils.core.setup( name=kinterbasdb_name, version=kinterbasdb_version, author='''Originally by Alexander Kuznetsov ; rewritten and expanded by David Rushby with contributions from several others (see docs/license.txt for details).''', author_email='woodsplitter@rocketmail.com', url='http://kinterbasdb.sourceforge.net', description='Python DB API 2.0 extension for Firebird and Interbase', long_description= 'kinterbasdb allows Python to access the Firebird and Interbase\n' 'relational databases according to the interface defined by the\n' 'Python Database API Specification version 2.0.', license='see docs/license.txt', package_dir={'kinterbasdb': os.curdir}, py_modules=allPythonModules, ext_modules=extensionModules, data_files = [ # documentation: ( # This path will be interpreted by distutils as being relative to # sys.exec_prefix. ((osIsWindows and 'Lib') or 'lib/python'+sys.version[:3]) + '/site-packages/kinterbasdb/docs', [ 'docs/index.html', 'docs/installation-source.html', 'docs/installation-binary.html', 'docs/usage.html', 'docs/changelog.txt', 'docs/license.txt', 'docs/Python-DB-API-2.0.html', 'docs/links.html', 'docs/global.css', 'docs/w3c.png', ] ) ] )