1
0

setup.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #!/usr/bin/env python
  2. # Copyright (c) 2017-present, Facebook, Inc.
  3. # All rights reserved.
  4. #
  5. # This source code is licensed under the MIT license found in the
  6. # LICENSE file in the root directory of this source tree.
  7. #
  8. from __future__ import absolute_import
  9. from __future__ import division
  10. from __future__ import print_function
  11. from __future__ import unicode_literals
  12. from setuptools import setup, Extension
  13. from setuptools.command.build_ext import build_ext
  14. import sys
  15. import setuptools
  16. import os
  17. import subprocess
  18. import platform
  19. import io
  20. __version__ = '0.9.2'
  21. FASTTEXT_SRC = "src"
  22. # Based on https://github.com/pybind/python_example
  23. class get_pybind_include(object):
  24. """Helper class to determine the pybind11 include path
  25. The purpose of this class is to postpone importing pybind11
  26. until it is actually installed, so that the ``get_include()``
  27. method can be invoked. """
  28. def __init__(self, user=False):
  29. try:
  30. import pybind11
  31. except ImportError:
  32. if subprocess.call([sys.executable, '-m', 'pip', 'install', 'pybind11']):
  33. raise RuntimeError('pybind11 install failed.')
  34. self.user = user
  35. def __str__(self):
  36. import pybind11
  37. return pybind11.get_include(self.user)
  38. try:
  39. coverage_index = sys.argv.index('--coverage')
  40. except ValueError:
  41. coverage = False
  42. else:
  43. del sys.argv[coverage_index]
  44. coverage = True
  45. fasttext_src_files = map(str, os.listdir(FASTTEXT_SRC))
  46. fasttext_src_cc = list(filter(lambda x: x.endswith('.cc'), fasttext_src_files))
  47. fasttext_src_cc = list(
  48. map(lambda x: str(os.path.join(FASTTEXT_SRC, x)), fasttext_src_cc)
  49. )
  50. ext_modules = [
  51. Extension(
  52. str('fasttext_pybind'),
  53. [
  54. str('python/fasttext_module/fasttext/pybind/fasttext_pybind.cc'),
  55. ] + fasttext_src_cc,
  56. include_dirs=[
  57. # Path to pybind11 headers
  58. get_pybind_include(),
  59. get_pybind_include(user=True),
  60. # Path to fasttext source code
  61. FASTTEXT_SRC,
  62. ],
  63. language='c++',
  64. extra_compile_args=["-O0 -fno-inline -fprofile-arcs -pthread -march=native" if coverage else
  65. "-O3 -funroll-loops -pthread -march=native"],
  66. ),
  67. ]
  68. # As of Python 3.6, CCompiler has a `has_flag` method.
  69. # cf http://bugs.python.org/issue26689
  70. def has_flag(compiler, flags):
  71. """Return a boolean indicating whether a flag name is supported on
  72. the specified compiler.
  73. """
  74. import tempfile
  75. with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f:
  76. f.write('int main (int argc, char **argv) { return 0; }')
  77. try:
  78. compiler.compile([f.name], extra_postargs=flags)
  79. except setuptools.distutils.errors.CompileError:
  80. return False
  81. return True
  82. def cpp_flag(compiler):
  83. """Return the -std=c++[11/14] compiler flag.
  84. The c++14 is preferred over c++11 (when it is available).
  85. """
  86. standards = ['-std=c++11']
  87. for standard in standards:
  88. if has_flag(compiler, [standard]):
  89. return standard
  90. raise RuntimeError(
  91. 'Unsupported compiler -- at least C++11 support '
  92. 'is needed!'
  93. )
  94. class BuildExt(build_ext):
  95. """A custom build extension for adding compiler-specific options."""
  96. c_opts = {
  97. 'msvc': ['/EHsc'],
  98. 'unix': [],
  99. }
  100. def build_extensions(self):
  101. if sys.platform == 'darwin':
  102. mac_osx_version = float('.'.join(platform.mac_ver()[0].split('.')[:2]))
  103. os.environ['MACOSX_DEPLOYMENT_TARGET'] = str(mac_osx_version)
  104. all_flags = ['-stdlib=libc++', '-mmacosx-version-min=10.7']
  105. if has_flag(self.compiler, [all_flags[0]]):
  106. self.c_opts['unix'] += [all_flags[0]]
  107. elif has_flag(self.compiler, all_flags):
  108. self.c_opts['unix'] += all_flags
  109. else:
  110. raise RuntimeError(
  111. 'libc++ is needed! Failed to compile with {} and {}.'.
  112. format(" ".join(all_flags), all_flags[0])
  113. )
  114. ct = self.compiler.compiler_type
  115. opts = self.c_opts.get(ct, [])
  116. extra_link_args = []
  117. if coverage:
  118. coverage_option = '--coverage'
  119. opts.append(coverage_option)
  120. extra_link_args.append(coverage_option)
  121. if ct == 'unix':
  122. opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version())
  123. opts.append(cpp_flag(self.compiler))
  124. if has_flag(self.compiler, ['-fvisibility=hidden']):
  125. opts.append('-fvisibility=hidden')
  126. elif ct == 'msvc':
  127. opts.append(
  128. '/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()
  129. )
  130. for ext in self.extensions:
  131. ext.extra_compile_args = opts
  132. ext.extra_link_args = extra_link_args
  133. build_ext.build_extensions(self)
  134. def _get_readme():
  135. """
  136. Use pandoc to generate rst from md.
  137. pandoc --from=markdown --to=rst --output=python/README.rst python/README.md
  138. """
  139. with io.open("python/README.rst", encoding='utf-8') as fid:
  140. return fid.read()
  141. setup(
  142. name='fasttext',
  143. version=__version__,
  144. author='Onur Celebi',
  145. author_email='[email protected]',
  146. description='fasttext Python bindings',
  147. long_description=_get_readme(),
  148. ext_modules=ext_modules,
  149. url='https://github.com/facebookresearch/fastText',
  150. license='MIT',
  151. classifiers=[
  152. 'Development Status :: 3 - Alpha',
  153. 'Intended Audience :: Developers',
  154. 'Intended Audience :: Science/Research',
  155. 'License :: OSI Approved :: MIT License',
  156. 'Programming Language :: Python :: 2.7',
  157. 'Programming Language :: Python :: 3.4',
  158. 'Programming Language :: Python :: 3.5',
  159. 'Programming Language :: Python :: 3.6',
  160. 'Topic :: Software Development',
  161. 'Topic :: Scientific/Engineering',
  162. 'Operating System :: Microsoft :: Windows',
  163. 'Operating System :: POSIX',
  164. 'Operating System :: Unix',
  165. 'Operating System :: MacOS',
  166. ],
  167. install_requires=['pybind11>=2.2', "setuptools >= 0.7.0", "numpy"],
  168. cmdclass={'build_ext': BuildExt},
  169. packages=[
  170. str('fasttext'),
  171. str('fasttext.util'),
  172. str('fasttext.tests'),
  173. ],
  174. package_dir={str(''): str('python/fasttext_module')},
  175. zip_safe=False,
  176. )