setup.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import glob
  2. import os
  3. import os.path as osp
  4. import re
  5. import shutil
  6. import sys
  7. from distutils.command.clean import clean as _clean
  8. from distutils import sysconfig
  9. from os.path import join as pjoin
  10. from setuptools import setup, Extension, Distribution
  11. from setuptools.command.build_ext import build_ext as _build_ext
  12. # Check if we're running 64-bit Python
  13. is_64_bit = sys.maxsize > 2**32
  14. class clean(_clean):
  15. def run(self):
  16. _clean.run(self)
  17. for x in []:
  18. try:
  19. os.remove(x)
  20. except OSError:
  21. pass
  22. class build_ext(_build_ext):
  23. def run(self):
  24. self._run_cmake()
  25. # adapted from cmake_build_ext in dynd-python
  26. # github.com/libdynd/dynd-python
  27. description = "Build the C-extensions for arrow"
  28. user_options = ([('extra-cmake-args=', None, 'extra arguments for CMake'),
  29. ('build-type=', None, 'build type (Debug or Release)')] +
  30. _build_ext.user_options)
  31. def initialize_options(self):
  32. _build_ext.initialize_options(self)
  33. self.extra_cmake_args = os.environ.get('XLNTPYARROW_CMAKE_OPTIONS', '')
  34. self.build_type = os.environ.get('XLNTPYARROW_BUILD_TYPE', 'Debug')
  35. self.cmake_cxxflags = os.environ.get('XLNTPYARROW_CXXFLAGS', '')
  36. if sys.platform == 'win32':
  37. # Cannot do debug builds in Windows unless Python itself is a debug
  38. # build
  39. if not hasattr(sys, 'gettotalrefcount'):
  40. self.build_type = 'Release'
  41. def _run_cmake(self):
  42. # The directory containing this setup.py
  43. source = osp.dirname(osp.abspath(__file__))
  44. # The staging directory for the module being built
  45. build_temp = pjoin(os.getcwd(), self.build_temp)
  46. build_lib = os.path.join(os.getcwd(), self.build_lib)
  47. # Change to the build directory
  48. saved_cwd = os.getcwd()
  49. if not os.path.isdir(self.build_temp):
  50. self.mkpath(self.build_temp)
  51. os.chdir(self.build_temp)
  52. # Detect if we built elsewhere
  53. if os.path.isfile('CMakeCache.txt'):
  54. cachefile = open('CMakeCache.txt', 'r')
  55. cachedir = re.search('CMAKE_CACHEFILE_DIR:INTERNAL=(.*)',
  56. cachefile.read()).group(1)
  57. cachefile.close()
  58. if (cachedir != build_temp):
  59. return
  60. static_lib_option = ''
  61. cmake_options = [
  62. '-DPYTHON_EXECUTABLE=%s' % sys.executable,
  63. static_lib_option,
  64. ]
  65. if len(self.cmake_cxxflags) > 0:
  66. cmake_options.append('-DPYARROW_CXXFLAGS="{0}"'
  67. .format(self.cmake_cxxflags))
  68. cmake_options.append('-DCMAKE_BUILD_TYPE={0}'
  69. .format(self.build_type))
  70. if 'CMAKE_GENERATOR' in os.environ:
  71. cmake_options.append('-G{}'
  72. .format(os.environ['CMAKE_GENERATOR']))
  73. if sys.platform != 'win32':
  74. cmake_options.append('-DCMAKE_INSTALL_PREFIX={0}'
  75. .format(os.environ['PREFIX']))
  76. cmake_command = (['cmake', self.extra_cmake_args] +
  77. cmake_options + [source])
  78. print("-- Runnning cmake for xlntpyarrow")
  79. self.spawn(cmake_command)
  80. print("-- Finished cmake for xlntpyarrow")
  81. args = ['make']
  82. if os.environ.get('XLNTPYARROW_BUILD_VERBOSE', '0') == '1':
  83. args.append('VERBOSE=1')
  84. if 'XLNTPYARROW_PARALLEL' in os.environ:
  85. args.append('-j{0}'.format(os.environ['XLNTPYARROW_PARALLEL']))
  86. args.append('install')
  87. print("-- Running cmake --build for xlntpyarrow")
  88. self.spawn(args)
  89. print("-- Finished cmake --build for xlntpyarrow")
  90. else:
  91. import shlex
  92. if not is_64_bit:
  93. raise RuntimeError('Not supported on 32-bit Windows')
  94. cmake_options.append('-DCMAKE_INSTALL_PREFIX={0}'
  95. .format(os.environ['LIBRARY_PREFIX']))
  96. extra_cmake_args = shlex.split(self.extra_cmake_args)
  97. cmake_command = (['cmake'] + extra_cmake_args +
  98. cmake_options + [source])
  99. print("-- Runnning cmake for xlntpyarrow")
  100. self.spawn(cmake_command)
  101. print("-- Finished cmake for xlntpyarrow")
  102. # Do the build
  103. print("-- Running cmake --build for xlntpyarrow")
  104. self.spawn(['cmake', '--build', '.', '--config', self.build_type, '--target', 'INSTALL'])
  105. print("-- Finished cmake --build for xlntpyarrow")
  106. if self.inplace:
  107. # a bit hacky
  108. build_lib = saved_cwd
  109. # Move the libraries to the place expected by the Python
  110. # build
  111. shared_library_prefix = 'lib'
  112. if sys.platform == 'darwin':
  113. shared_library_suffix = '.dylib'
  114. elif sys.platform == 'win32':
  115. shared_library_suffix = '.dll'
  116. shared_library_prefix = ''
  117. else:
  118. shared_library_suffix = '.so'
  119. try:
  120. os.makedirs(pjoin(build_lib, 'xlntpyarrow'))
  121. except OSError:
  122. pass
  123. self._found_names = []
  124. built_path = self.get_ext_built('lib')
  125. if not os.path.exists(built_path):
  126. raise RuntimeError('xlntpyarrow C-extension failed to build:',
  127. os.path.abspath(built_path))
  128. ext_path = pjoin(build_lib, self._get_cmake_ext_path('lib'))
  129. if os.path.exists(ext_path):
  130. os.remove(ext_path)
  131. self.mkpath(os.path.dirname(ext_path))
  132. print('Moving built C-extension', built_path,
  133. 'to build path', ext_path)
  134. shutil.move(self.get_ext_built('lib'), ext_path)
  135. self._found_names.append('lib')
  136. os.chdir(saved_cwd)
  137. def _failure_permitted(self, name):
  138. return False
  139. def _get_inplace_dir(self):
  140. pass
  141. def _get_cmake_ext_path(self, name):
  142. # Get the package directory from build_py
  143. build_py = self.get_finalized_command('build_py')
  144. package_dir = build_py.get_package_dir('xlntpyarrow')
  145. # This is the name of the arrow C-extension
  146. suffix = sysconfig.get_config_var('EXT_SUFFIX')
  147. if suffix is None:
  148. suffix = sysconfig.get_config_var('SO')
  149. filename = name + suffix
  150. return pjoin(package_dir, filename)
  151. def get_ext_built(self, name):
  152. if sys.platform == 'win32':
  153. head, tail = os.path.split(name)
  154. suffix = sysconfig.get_config_var('SO')
  155. return pjoin(head, self.build_type, tail + suffix)
  156. else:
  157. suffix = sysconfig.get_config_var('SO')
  158. print('suffix is', suffix)
  159. return name + suffix
  160. def get_names(self):
  161. return self._found_names
  162. def get_outputs(self):
  163. # Just the C extensions
  164. # regular_exts = _build_ext.get_outputs(self)
  165. return [self._get_cmake_ext_path(name)
  166. for name in self.get_names()]
  167. long_description = """
  168. xlntpyarrow allows Apache Arrow tables to be written to and read from an XLSX
  169. file efficiently using the C++ library xlnt.
  170. """.strip()
  171. classifiers = [
  172. 'Development Status :: 5 - Production/Stable',
  173. 'Environment :: Plugins',
  174. 'Intended Audience :: Science/Research',
  175. 'License :: OSI Approved :: MIT License',
  176. 'Natural Language :: English',
  177. 'Operating System :: Microsoft :: Windows',
  178. 'Operating System :: MacOS :: MacOS X',
  179. 'Operating System :: POSIX :: Linux',
  180. 'Programming Language :: C',
  181. 'Programming Language :: C++',
  182. 'Programming Language :: Python :: 2.7',
  183. 'Programming Language :: Python :: 3.6',
  184. 'Programming Language :: Python :: Implementation :: CPython',
  185. 'Topic :: Database',
  186. 'Topic :: Office/Business :: Financial :: Spreadsheet',
  187. 'Topic :: Scientific/Engineering :: Information Analysis',
  188. 'Topic :: Software Development :: Libraries :: Python Modules'
  189. ]
  190. class BinaryDistribution(Distribution):
  191. def has_ext_modules(foo):
  192. return True
  193. setup(
  194. name = 'xlntpyarrow',
  195. version = '1.1.0',
  196. description = 'Python library for converting Apache Arrow tables<->XLSX files',
  197. long_description = long_description,
  198. author = 'Thomas Fussell',
  199. author_email = 'thomas.fussell@gmail.com',
  200. maintainer = 'Thomas Fussell',
  201. maintainer_email = 'thomas.fussell@gmail.com',
  202. url = 'https://github.com/tfussell/xlnt',
  203. download_url = 'https://github.com/tfussell/xlnt/releases',
  204. packages = ['xlntpyarrow'],
  205. ext_modules = [Extension('xlntpyarrow.lib', [])],
  206. cmdclass = {
  207. 'clean': clean,
  208. 'build_ext': build_ext
  209. },
  210. classifiers = classifiers,
  211. distclass = BinaryDistribution,
  212. zip_safe = False
  213. )