From e2fa80f7f674207cbc92b6fcc62b10ec0a95d97a Mon Sep 17 00:00:00 2001 From: David Huggins-Daines Date: Thu, 9 Jun 2022 16:43:24 -0400 Subject: [PATCH] build: make a real python module using cmake_build_extension --- CMakeLists.txt | 31 ++++++++++++--------- pyproject.toml | 8 ++++++ setup.cfg | 16 +++++++++++ setup.py | 43 +++++++++++++++++++++++++++++ src/CMakeLists.txt | 5 +++- swig/CMakeLists.txt | 13 +++++++-- swig/python/CMakeLists.txt | 55 +++++++++++++++++++++++++++++++------- 7 files changed, 145 insertions(+), 26 deletions(-) create mode 100644 pyproject.toml create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/CMakeLists.txt b/CMakeLists.txt index b4d27503..bda8cc02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,11 @@ include(CheckLibraryExists) include(TestBigEndian) include(GNUInstallDirs) +# Statically link the Python package +if(CALL_FROM_SETUP_PY) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif() + CHECK_INCLUDE_FILE(unistd.h HAVE_UNISTD_H) CHECK_INCLUDE_FILE(sys/types.h HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILE(sys/stat.h HAVE_SYS_STAT_H) @@ -65,22 +70,22 @@ if(NOT DEFAULT_RADIX) endif() cmake_print_variables(FIXED_POINT DEFAULT_RADIX) -# Maybe not a great idea, but it does work on both Windows and Linux -set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - configure_file(config.h.in config.h) configure_file(sphinx_config.h.in include/sphinxbase/sphinx_config.h) add_definitions(-DHAVE_CONFIG_H) add_subdirectory(src) -add_subdirectory(include) -add_subdirectory(model) -add_subdirectory(programs) -add_subdirectory(doc) -add_subdirectory(test) -add_subdirectory(swig) +# Only build SWIG and Python if we are building the package +if(CALL_FROM_SETUP_PY) + add_subdirectory(swig) +else() + # Don't build or install these in Python + add_subdirectory(model) + add_subdirectory(doc) + add_subdirectory(include) + add_subdirectory(programs) + add_subdirectory(test) + configure_file(pocketsphinx.pc.in pocketsphinx.pc @ONLY) + install(FILES ${CMAKE_BINARY_DIR}/pocketsphinx.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() -configure_file(pocketsphinx.pc.in pocketsphinx.pc @ONLY) -install(FILES ${CMAKE_BINARY_DIR}/pocketsphinx.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..cce2feac --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,8 @@ +[build-system] +requires = [ + "wheel", + "setuptools>=45", + "setuptools_scm[toml]>=6.0", + "cmake_build_extension", +] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..65a71350 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,16 @@ +[metadata] +name = pocketsphinx5 +version = 5.0.0rc0 +description = Official Python bindings for PocketSphinx5 +long_description = file: README.md +long_description_content_type = text/markdown +author = David Huggins-Daines +author_email = dhdaines@gmail.com +license = MIT +platforms = any +url = https://github.com/cmusphinx/pocketsphinx +project_urls = + Source = https://github.com/cmusphinx/pocketsphinx + Tracker = https://github.com/cmusphinx/pocketsphinx/issues +keywords = +classifiers = diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..de7bea22 --- /dev/null +++ b/setup.py @@ -0,0 +1,43 @@ +import inspect +import os +import sys +from pathlib import Path + +import cmake_build_extension +import setuptools + +init_py = """ +from .bindings import * +""" + +# This example is compliant with PEP517 and PEP518. It uses the setup.cfg file to store +# most of the package metadata. However, build extensions are not supported and must be +# configured in the setup.py. +setuptools.setup( + ext_modules=[ + cmake_build_extension.CMakeExtension( + # This could be anything you like, it is used to create build folders + name="PocketSphinx5", + # Name of the resulting package name (import mymath_swig) + install_prefix="pocketsphinx5", + write_top_level_init=init_py, + # Selects the folder where the main CMakeLists.txt is stored + # (it could be a subfolder) + source_dir=str(Path(__file__).parent.absolute()), + cmake_configure_options=[ + # This option points CMake to the right Python interpreter, and helps + # the logic of FindPython3.cmake to find the active version + f"-DPython3_ROOT_DIR={Path(sys.prefix)}", + "-DCALL_FROM_SETUP_PY:BOOL=ON", + "-DBUILD_SHARED_LIBS:BOOL=OFF", + ] + ), + ], + cmdclass=dict( + # Enable the CMakeExtension entries defined above + build_ext=cmake_build_extension.BuildExtension, + # If the setup.py or setup.cfg are in a subfolder wrt the main CMakeLists.txt, + # you can use the following custom command to create the source distribution. + # sdist=cmake_build_extension.GitSdistFolder + ), +) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98b70768..1210b527 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -108,4 +108,7 @@ set_property(TARGET pocketsphinx PROPERTY COMPILE_DEFINITIONS POCKETSPHINX_EXPORTS;SPHINXBASE_EXPORTS ) -install(TARGETS pocketsphinx LIBRARY) +# No, do not install this when building Python +if(NOT CALL_FROM_SETUP_PY) + install(TARGETS pocketsphinx LIBRARY) +endif() diff --git a/swig/CMakeLists.txt b/swig/CMakeLists.txt index 3fe5d36e..a6476c60 100644 --- a/swig/CMakeLists.txt +++ b/swig/CMakeLists.txt @@ -1,7 +1,16 @@ -find_package(SWIG) - +# Set SWIG policies +cmake_policy(SET CMP0078 NEW) +cmake_policy(SET CMP0086 NEW) +find_package(SWIG 4.0) if(SWIG_FOUND) + set(UseSWIG_MODULE_VERSION 2) # mysteriously necessary include(${SWIG_USE_FILE}) add_subdirectory(python) endif() + +set_property( + SOURCE pocketsphinx.i + PROPERTY SWIG_DEPENDS + ps_decoder.i ps_lattice.i typemaps.i iterators.i + cmd_ln.i fe.i feat.i fsg_model.i jsgf.i ngram_model.i logmath.i) diff --git a/swig/python/CMakeLists.txt b/swig/python/CMakeLists.txt index 54871bb6..3927c41d 100644 --- a/swig/python/CMakeLists.txt +++ b/swig/python/CMakeLists.txt @@ -1,15 +1,50 @@ find_package(Python3 COMPONENTS Interpreter Development) -set_property(SOURCE ../pocketsphinx.i PROPERTY SWIG_MODULE_NAME pocketsphinx) -swig_add_library(pocketsphinx_python + +# Handle where to install the resulting Python package +if(CALL_FROM_SETUP_PY) + # The CMakeExtension will set CMAKE_INSTALL_PREFIX to the root + # of the resulting wheel archive + set(POCKETSPHINX_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) +else() + message(FATAL_ERROR + "Please do not try to build the Python module outside of setup.py") +endif() + +# Set the Python module name (.py file) properly +set_property(SOURCE ../pocketsphinx.i PROPERTY SWIG_MODULE_NAME bindings) +# SWIG-ify and compile the bindings +swig_add_library(bindings + TYPE MODULE LANGUAGE python + OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/pocketsphinx5 + OUTFILE_DIR ${CMAKE_CURRENT_BINARY_DIR} SOURCES ../pocketsphinx.i ) -swig_link_libraries(pocketsphinx_python pocketsphinx Python3::Python) -set_property(SOURCE ../sphinxbase.i PROPERTY SWIG_MODULE_NAME sphinxbase) -swig_add_library(sphinxbase_python - LANGUAGE python - SOURCES - ../sphinxbase.i - ) -swig_link_libraries(sphinxbase_python pocketsphinx Python3::Python) +# Link the bindings with pocketsphinx and Python +target_link_libraries(bindings PRIVATE pocketsphinx Python3::Python) +# Build the library in the proper place and not the weird random CMake place +set_target_properties(bindings PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pocketsphinx5) +# This does something, not sure what +set_property(TARGET bindings PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE) +# Enable parsing the doxygen comments (FIXME: does this work?) +set_property(TARGET bindings PROPERTY SWIG_COMPILE_OPTIONS -doxygen) + +# Installation stuff +# Get the autogenerated Python file +get_property(WRAPPER_PY_FILE + TARGET bindings + PROPERTY SWIG_SUPPORT_FILES) +# Install the autogenerated Python file +install( + FILES ${WRAPPER_PY_FILE} + DESTINATION ${POCKETSPHINX_INSTALL_PREFIX} + COMPONENT bindings) +# Install the SWIG library +install( + TARGETS bindings + COMPONENT bindings + LIBRARY DESTINATION ${POCKETSPHINX_INSTALL_PREFIX} + ARCHIVE DESTINATION ${POCKETSPHINX_INSTALL_PREFIX} + RUNTIME DESTINATION ${POCKETSPHINX_INSTALL_PREFIX})