setup.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. """Distutils script for cx_Oracle.
  2. Windows platforms:
  3. python setup.py build --compiler=mingw32 install
  4. Unix platforms
  5. python setup.py build install
  6. """
  7. import distutils.command
  8. try:
  9. import distutils.command.bdist_msi
  10. except ImportError:
  11. distutils.command.bdist_msi = None
  12. try:
  13. import distutils.command.bdist_wininst
  14. except ImportError:
  15. distutils.command.bdist_wininst = None
  16. import distutils.command.bdist_rpm
  17. import distutils.command.build
  18. import distutils.core
  19. import distutils.dist
  20. import distutils.util
  21. import os
  22. import re
  23. import struct
  24. import sys
  25. import glob
  26. from distutils.errors import DistutilsSetupError
  27. # if setuptools is detected, use it to add support for eggs
  28. try:
  29. from setuptools import setup, Extension
  30. except:
  31. from distutils.core import setup
  32. from distutils.extension import Extension
  33. # define build constants
  34. BUILD_VERSION = "5.2.1"
  35. # define the list of files to be included as documentation for Windows
  36. dataFiles = None
  37. if sys.platform in ("win32", "cygwin"):
  38. baseName = "cx_Oracle-doc"
  39. dataFiles = [ (baseName,
  40. [ "BUILD.txt", "README.txt"]) ]
  41. for dir in ("samples", "test"):
  42. files = []
  43. fullDirName = "%s/%s" % (baseName, dir)
  44. for name in os.listdir(dir):
  45. if name.startswith("."):
  46. continue
  47. if os.path.isdir(os.path.join(dir, name)):
  48. continue
  49. fullName = "%s/%s" % (dir, name)
  50. files.append(fullName)
  51. dataFiles.append((fullDirName, files))
  52. # define the list of files to be included as documentation for bdist_rpm
  53. docFiles = "README.txt BUILD.txt samples test"
  54. # method for checking a potential Oracle home
  55. def CheckOracleHome(directoryToCheck):
  56. global oracleHome, oracleVersion, oracleLibDir
  57. import os
  58. import struct
  59. import sys
  60. if sys.platform in ("win32", "cygwin"):
  61. subDirs = ["bin"]
  62. filesToCheck = [
  63. ("12c", "oraocci12.dll"),
  64. ("11g", "oraocci11.dll"),
  65. ("10g", "oraocci10.dll")
  66. ]
  67. elif sys.platform == "darwin":
  68. subDirs = ["lib"]
  69. filesToCheck = [
  70. ("12c", "libclntsh.dylib.12.1"),
  71. ("11g", "libclntsh.dylib.11.1"),
  72. ("10g", "libclntsh.dylib.10.1")
  73. ]
  74. else:
  75. if struct.calcsize("P") == 4:
  76. subDirs = ["lib", "lib32"]
  77. else:
  78. subDirs = ["lib", "lib64"]
  79. filesToCheck = [
  80. ("12c", "libclntsh.so.12.1"),
  81. ("11g", "libclntsh.so.11.1"),
  82. ("10g", "libclntsh.so.10.1")
  83. ]
  84. for version, baseFileName in filesToCheck:
  85. fileName = os.path.join(directoryToCheck, baseFileName)
  86. if os.path.exists(fileName):
  87. if os.path.basename(directoryToCheck).lower() == "bin":
  88. oracleHome = os.path.dirname(directoryToCheck)
  89. else:
  90. oracleHome = directoryToCheck
  91. oracleLibDir = directoryToCheck
  92. oracleVersion = version
  93. return True
  94. for subDir in subDirs:
  95. fileName = os.path.join(directoryToCheck, subDir, baseFileName)
  96. if os.path.exists(fileName):
  97. oracleHome = directoryToCheck
  98. oracleLibDir = os.path.join(directoryToCheck, subDir)
  99. oracleVersion = version
  100. return True
  101. dirName = os.path.dirname(directoryToCheck)
  102. fileName = os.path.join(dirName, subDir, baseFileName)
  103. if os.path.exists(fileName):
  104. oracleHome = dirName
  105. oracleLibDir = os.path.join(dirName, subDir)
  106. oracleVersion = version
  107. return True
  108. oracleHome = oracleVersion = oracleLibDir = None
  109. return False
  110. # Look for the highest version Instant Client "basic" or "basiclite" RPM
  111. # Newer Instant Client dirs have the form:
  112. # /usr/lib/oracle/12.1/client[64]/lib
  113. # Older Instant Client dirs have the form:
  114. # /usr/lib/oracle/10.2.0.5/client[64]/lib
  115. def FindInstantClientRPMLib():
  116. versions = []
  117. for path in glob.glob(os.path.join(rpmBaseLibDir, "[0-9.]*")):
  118. versions.append(os.path.basename(path))
  119. versions.sort(key = lambda x: [int(s) for s in x.split(".")])
  120. versions.reverse()
  121. for version in versions:
  122. path = os.path.join(rpmBaseLibDir, version, rpmClientDir, "lib")
  123. if os.path.exists(path) and CheckOracleHome(path):
  124. return path
  125. # If the lib dir appears to be an Instant Client RPM dir, then look only
  126. # for matching SDK headers
  127. def FindInstantClientRPMInclude(libDir):
  128. version = os.path.basename(os.path.dirname(os.path.dirname(libDir)))
  129. includeDir = os.path.join("/usr/include/oracle", version, rpmClientDir)
  130. if os.path.isfile(os.path.join(includeDir, "oci.h")):
  131. return [includeDir]
  132. raise DistutilsSetupError("cannot locate Oracle Instant Client " \
  133. "SDK RPM header files")
  134. # define Linux Instant Client RPM path components
  135. # Assume 64 bit builds if the platform is 64 bit
  136. rpmBaseLibDir = "/usr/lib/oracle"
  137. if struct.calcsize("P") == 4:
  138. rpmClientDir = "client"
  139. else:
  140. rpmClientDir = "client64"
  141. instantClientRPMLib = None
  142. # try to determine the Oracle home
  143. userOracleHome = os.environ.get("ORACLE_HOME", os.environ.get("ORACLE_INSTANTCLIENT_HOME"))
  144. if userOracleHome is not None:
  145. if not CheckOracleHome(userOracleHome):
  146. messageFormat = "Oracle home (%s) does not refer to an " \
  147. "10g, 11g or 12c installation."
  148. raise DistutilsSetupError(messageFormat % userOracleHome)
  149. else:
  150. for path in os.environ["PATH"].split(os.pathsep):
  151. if CheckOracleHome(path):
  152. break
  153. if oracleHome is None and sys.platform.startswith("linux"):
  154. instantClientRPMLib = FindInstantClientRPMLib()
  155. if oracleHome is None:
  156. print >>sys.stderr, "cannot locate an Oracle software installation. skipping"
  157. sys.exit(0)
  158. # define some variables
  159. if sys.platform == "win32":
  160. libDirs = [os.path.join(oracleHome, "bin"), oracleHome,
  161. os.path.join(oracleHome, "oci", "lib", "msvc"),
  162. os.path.join(oracleHome, "sdk", "lib", "msvc")]
  163. possibleIncludeDirs = ["oci/include", "rdbms/demo", "sdk/include"]
  164. includeDirs = []
  165. for dir in possibleIncludeDirs:
  166. path = os.path.normpath(os.path.join(oracleHome, dir))
  167. if os.path.isdir(path):
  168. includeDirs.append(path)
  169. if not includeDirs:
  170. message = "cannot locate Oracle include files in %s" % oracleHome
  171. raise DistutilsSetupError(message)
  172. libs = ["oci"]
  173. elif sys.platform == "cygwin":
  174. includeDirs = ["/usr/include", "rdbms/demo", "rdbms/public", \
  175. "network/public", "oci/include"]
  176. libDirs = ["bin", "lib"]
  177. for i in range(len(includeDirs)):
  178. includeDirs[i] = os.path.join(oracleHome, includeDirs[i])
  179. for i in range(len(libDirs)):
  180. libDirs[i] = os.path.join(oracleHome, libDirs[i])
  181. libs = ["oci"]
  182. else:
  183. libDirs = [oracleLibDir]
  184. libs = ["clntsh"]
  185. if instantClientRPMLib is not None:
  186. includeDirs = FindInstantClientRPMInclude(instantClientRPMLib)
  187. else:
  188. possibleIncludeDirs = ["rdbms/demo", "rdbms/public", "network/public",
  189. "sdk/include"]
  190. if sys.platform == "darwin":
  191. possibleIncludeDirs.append("plsql/public")
  192. includeDirs = []
  193. for dir in possibleIncludeDirs:
  194. path = os.path.join(oracleHome, dir)
  195. if os.path.isdir(path):
  196. includeDirs.append(path)
  197. if not includeDirs:
  198. path = os.path.join(oracleLibDir, "include")
  199. if os.path.isdir(path):
  200. includeDirs.append(path)
  201. if not includeDirs:
  202. path = re.sub("lib(64)?", "include", oracleHome)
  203. if os.path.isdir(path):
  204. includeDirs.append(path)
  205. if not includeDirs:
  206. raise DistutilsSetupError("cannot locate Oracle include files")
  207. # NOTE: on HP-UX Itanium with Oracle 10g you need to add the library "ttsh10"
  208. # to the list of libraries along with "clntsh"; since I am unable to test, I'll
  209. # leave this as a comment until someone can verify when this is required
  210. # without making other cases where sys.platform == "hp-ux11" stop working
  211. # setup extra link and compile args
  212. extraCompileArgs = ["-DBUILD_VERSION=%s" % BUILD_VERSION]
  213. extraLinkArgs = []
  214. if sys.platform == "aix4":
  215. extraCompileArgs.append("-qcpluscmt")
  216. elif sys.platform == "aix5":
  217. extraCompileArgs.append("-DAIX5")
  218. elif sys.platform == "cygwin":
  219. extraCompileArgs.append("-mno-cygwin")
  220. extraLinkArgs.append("-Wl,--enable-runtime-pseudo-reloc")
  221. elif sys.platform == "darwin":
  222. extraLinkArgs.append("-shared-libgcc")
  223. # force the inclusion of an RPATH linker directive if desired; this will
  224. # eliminate the need for setting LD_LIBRARY_PATH but it also means that this
  225. # location will be the only location searched for the Oracle client library
  226. if "FORCE_RPATH" in os.environ or instantClientRPMLib:
  227. extraLinkArgs.append("-Wl,-rpath,%s" % oracleLibDir)
  228. # tweak distribution full name to include the Oracle version
  229. class Distribution(distutils.dist.Distribution):
  230. def get_fullname_with_oracle_version(self):
  231. name = self.metadata.get_fullname()
  232. return "%s-%s" % (name, oracleVersion)
  233. # tweak the RPM build command to include the Python and Oracle version
  234. class bdist_rpm(distutils.command.bdist_rpm.bdist_rpm):
  235. def run(self):
  236. distutils.command.bdist_rpm.bdist_rpm.run(self)
  237. specFile = os.path.join(self.rpm_base, "SPECS",
  238. "%s.spec" % self.distribution.get_name())
  239. queryFormat = "%{name}-%{version}-%{release}.%{arch}.rpm"
  240. command = "rpm -q --qf '%s' --specfile %s" % (queryFormat, specFile)
  241. origFileName = os.popen(command).read()
  242. parts = origFileName.split("-")
  243. parts.insert(2, oracleVersion)
  244. parts.insert(3, "py%s%s" % sys.version_info[:2])
  245. newFileName = "-".join(parts)
  246. self.move_file(os.path.join("dist", origFileName),
  247. os.path.join("dist", newFileName))
  248. # tweak the build directories to include the Oracle version
  249. class build(distutils.command.build.build):
  250. def finalize_options(self):
  251. import distutils.util
  252. import os
  253. import sys
  254. platSpecifier = ".%s-%s-%s" % \
  255. (distutils.util.get_platform(), sys.version[0:3],
  256. oracleVersion)
  257. if self.build_platlib is None:
  258. self.build_platlib = os.path.join(self.build_base,
  259. "lib%s" % platSpecifier)
  260. if self.build_temp is None:
  261. self.build_temp = os.path.join(self.build_base,
  262. "temp%s" % platSpecifier)
  263. distutils.command.build.build.finalize_options(self)
  264. class test(distutils.core.Command):
  265. description = "run the test suite for the extension"
  266. user_options = []
  267. def finalize_options(self):
  268. pass
  269. def initialize_options(self):
  270. pass
  271. def run(self):
  272. self.run_command("build")
  273. buildCommand = self.distribution.get_command_obj("build")
  274. sys.path.insert(0, os.path.abspath("test"))
  275. sys.path.insert(0, os.path.abspath(buildCommand.build_lib))
  276. if sys.version_info[0] < 3:
  277. execfile(os.path.join("test", "test.py"))
  278. else:
  279. fileName = os.path.join("test", "test3k.py")
  280. exec(open(fileName).read())
  281. commandClasses = dict(build = build, bdist_rpm = bdist_rpm, test = test)
  282. # tweak the Windows installer names to include the Oracle version
  283. if distutils.command.bdist_msi is not None:
  284. class bdist_msi(distutils.command.bdist_msi.bdist_msi):
  285. def run(self):
  286. origMethod = self.distribution.get_fullname
  287. self.distribution.get_fullname = \
  288. self.distribution.get_fullname_with_oracle_version
  289. distutils.command.bdist_msi.bdist_msi.run(self)
  290. self.distribution.get_fullname = origMethod
  291. commandClasses["bdist_msi"] = bdist_msi
  292. if distutils.command.bdist_wininst is not None:
  293. class bdist_wininst(distutils.command.bdist_wininst.bdist_wininst):
  294. def run(self):
  295. origMethod = self.distribution.get_fullname
  296. self.distribution.get_fullname = \
  297. self.distribution.get_fullname_with_oracle_version
  298. distutils.command.bdist_wininst.bdist_wininst.run(self)
  299. self.distribution.get_fullname = origMethod
  300. commandClasses["bdist_wininst"] = bdist_wininst
  301. # define classifiers for the package index
  302. classifiers = [
  303. "Development Status :: 6 - Mature",
  304. "Intended Audience :: Developers",
  305. "License :: OSI Approved :: Python Software Foundation License",
  306. "Natural Language :: English",
  307. "Operating System :: OS Independent",
  308. "Programming Language :: C",
  309. "Programming Language :: Python",
  310. "Programming Language :: Python :: 2",
  311. "Programming Language :: Python :: 3",
  312. "Topic :: Database"
  313. ]
  314. # setup the extension
  315. extension = Extension(
  316. name = "cx_Oracle",
  317. include_dirs = includeDirs,
  318. libraries = libs,
  319. library_dirs = libDirs,
  320. extra_compile_args = extraCompileArgs,
  321. extra_link_args = extraLinkArgs,
  322. sources = ["cx_Oracle.c"],
  323. depends = ["Buffer.c", "Callback.c", "Connection.c", "Cursor.c",
  324. "CursorVar.c", "DateTimeVar.c", "Environment.c", "Error.c",
  325. "ExternalLobVar.c", "ExternalObjectVar.c", "IntervalVar.c",
  326. "LobVar.c", "LongVar.c", "NumberVar.c", "ObjectType.c",
  327. "ObjectVar.c", "SessionPool.c", "StringVar.c",
  328. "Subscription.c", "TimestampVar.c", "Transforms.c",
  329. "Variable.c"])
  330. # perform the setup
  331. setup(
  332. name = "cx_Oracle",
  333. version = BUILD_VERSION,
  334. distclass = Distribution,
  335. description = "Python interface to Oracle",
  336. data_files = dataFiles,
  337. cmdclass = commandClasses,
  338. options = dict(bdist_rpm = dict(doc_files = docFiles)),
  339. long_description = \
  340. "Python interface to Oracle conforming to the Python DB API 2.0 "
  341. "specification.\n"
  342. "See http://www.python.org/topics/database/DatabaseAPI-2.0.html.",
  343. author = "Anthony Tuininga",
  344. author_email = "anthony.tuininga@gmail.com",
  345. url = "http://cx-oracle.sourceforge.net",
  346. ext_modules = [extension],
  347. keywords = "Oracle",
  348. license = "Python Software Foundation License",
  349. classifiers = classifiers)