setup.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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:
  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. pass
  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. ]
  56. + fasttext_src_cc,
  57. include_dirs=[
  58. # Path to pybind11 headers
  59. get_pybind_include(),
  60. get_pybind_include(user=True),
  61. # Path to fasttext source code
  62. FASTTEXT_SRC,
  63. ],
  64. language="c++",
  65. extra_compile_args=[
  66. "-O0 -fno-inline -fprofile-arcs -pthread -march=native"
  67. if coverage
  68. else "-O3 -funroll-loops -pthread -march=native"
  69. ],
  70. ),
  71. ]
  72. # As of Python 3.6, CCompiler has a `has_flag` method.
  73. # cf http://bugs.python.org/issue26689
  74. def has_flag(compiler, flags):
  75. """Return a boolean indicating whether a flag name is supported on
  76. the specified compiler.
  77. """
  78. import tempfile
  79. with tempfile.NamedTemporaryFile("w", suffix=".cpp") as f:
  80. f.write("int main (int argc, char **argv) { return 0; }")
  81. try:
  82. compiler.compile([f.name], extra_postargs=flags)
  83. except setuptools.distutils.errors.CompileError:
  84. return False
  85. return True
  86. def cpp_flag(compiler):
  87. """Return the -std=c++17 compiler flag."""
  88. standards = ["-std=c++17"]
  89. for standard in standards:
  90. if has_flag(compiler, [standard]):
  91. return standard
  92. raise RuntimeError("Unsupported compiler -- at least C++17 support " "is needed!")
  93. class BuildExt(build_ext):
  94. """A custom build extension for adding compiler-specific options."""
  95. c_opts = {
  96. "msvc": ["/EHsc"],
  97. "unix": [],
  98. }
  99. def build_extensions(self):
  100. if sys.platform == "darwin":
  101. mac_osx_version = float(".".join(platform.mac_ver()[0].split(".")[:2]))
  102. os.environ["MACOSX_DEPLOYMENT_TARGET"] = str(mac_osx_version)
  103. all_flags = ["-stdlib=libc++", "-mmacosx-version-min=10.7"]
  104. if has_flag(self.compiler, [all_flags[0]]):
  105. self.c_opts["unix"] += [all_flags[0]]
  106. elif has_flag(self.compiler, all_flags):
  107. self.c_opts["unix"] += all_flags
  108. else:
  109. raise RuntimeError(
  110. "libc++ is needed! Failed to compile with {} and {}.".format(
  111. " ".join(all_flags), all_flags[0]
  112. )
  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('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version())
  128. for ext in self.extensions:
  129. ext.extra_compile_args = opts
  130. ext.extra_link_args = extra_link_args
  131. build_ext.build_extensions(self)
  132. def _get_readme():
  133. """
  134. Use pandoc to generate rst from md.
  135. pandoc --from=markdown --to=rst --output=python/README.rst python/README.md
  136. """
  137. with io.open("python/README.rst", encoding="utf-8") as fid:
  138. return fid.read()
  139. setup(
  140. name="fasttext",
  141. version=__version__,
  142. author="Onur Celebi",
  143. author_email="[email protected]",
  144. description="fasttext Python bindings",
  145. long_description=_get_readme(),
  146. ext_modules=ext_modules,
  147. url="https://github.com/facebookresearch/fastText",
  148. license="MIT",
  149. classifiers=[
  150. "Development Status :: 3 - Alpha",
  151. "Intended Audience :: Developers",
  152. "Intended Audience :: Science/Research",
  153. "License :: OSI Approved :: MIT License",
  154. "Programming Language :: Python :: 2.7",
  155. "Programming Language :: Python :: 3.4",
  156. "Programming Language :: Python :: 3.5",
  157. "Programming Language :: Python :: 3.6",
  158. "Topic :: Software Development",
  159. "Topic :: Scientific/Engineering",
  160. "Operating System :: Microsoft :: Windows",
  161. "Operating System :: POSIX",
  162. "Operating System :: Unix",
  163. "Operating System :: MacOS",
  164. ],
  165. install_requires=["pybind11>=2.2", "setuptools >= 0.7.0", "numpy"],
  166. cmdclass={"build_ext": BuildExt},
  167. packages=[
  168. str("fasttext"),
  169. str("fasttext.util"),
  170. str("fasttext.tests"),
  171. ],
  172. package_dir={str(""): str("python/fasttext_module")},
  173. zip_safe=False,
  174. )