1
0

setup.py 6.1 KB

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