From 078c81f641637ec949f5a120d4269f65828b783b Mon Sep 17 00:00:00 2001 From: Matthias Hochsteger Date: Mon, 17 Jun 2024 10:03:08 +0200 Subject: [PATCH] Use CMake and pyproject.toml from ngsolve-addon-template --- .github/workflows/pypi.yml | 79 ++++++++++++++++++ CMakeLists.txt | 125 ++++++++++++++++------------- demo/bessel.py | 4 +- ngsolve_addon.cmake | 97 ++++++++++++++++++++++ pyproject.toml | 46 +++++++++++ src/CMakeLists.txt | 23 ------ src/fetch_and_convert_slatec.cmake | 21 ----- src/specialcf.cpp | 2 +- 8 files changed, 294 insertions(+), 103 deletions(-) create mode 100644 .github/workflows/pypi.yml create mode 100644 ngsolve_addon.cmake create mode 100644 pyproject.toml delete mode 100644 src/CMakeLists.txt delete mode 100644 src/fetch_and_convert_slatec.cmake diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 0000000..c696735 --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,79 @@ +name: Build and upload to PyPI + +on: + push: + branches: + - master + - use_ngsolve_addon_template + tags: + - '*' + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-13] + # include: + # - os: windows-2019 + # cibw-arch: AMD64 + # cmake-generator: "Visual Studio 16 2019" + # cmake_generator_platform: "x64" + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + fetch-tags: 'true' + fetch-depth: 0 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.19.0 + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl + + build_sdist: + name: Build source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + fetch-tags: 'true' + fetch-depth: 0 + + - name: Build sdist + run: pipx run build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: cibw-sdist + path: dist/*.tar.gz + + upload_pypi: + if: false # remove this if you want to enable upload to pypi (you need to setup that on pypi first) + needs: [build_wheels, build_sdist] + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + #if: github.event_name == 'release' && github.event.action == 'published' + # or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this) + #if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/download-artifact@v4 + with: + # unpacks all CIBW artifacts into dist/ + pattern: cibw-* + path: dist + merge-multiple: true + + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip-existing: true + password: ${{ secrets.PYPI_API_TOKEN_2 }} + # To test: repository-url: https://test.pypi.org/legacy/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 6558511..0a8d4a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,66 +1,79 @@ -include (ExternalProject) - -# get command line cmake arguments -# MUST be done before call to 'project' -get_cmake_property(vars CACHE_VARIABLES) -foreach(var ${vars}) - get_property(currentHelpString CACHE "${var}" PROPERTY HELPSTRING) - if("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "") - # message("${var} = [${${var}}] -- ${currentHelpString}") # uncomment to see the variables being processed - list(APPEND CL_ARGS "-D${var}=${${var}}") - endif() -endforeach() - -project(ngs_special_functions_super) +cmake_minimum_required(VERSION 3.28) +include(FetchContent) -cmake_minimum_required(VERSION 3.8) +project(ngsolve_special_functions) +include(ngsolve_addon.cmake) -find_package(NGSolve CONFIG REQUIRED - HINTS /usr $ENV{NETGENDIR}/.. /opt/netgen/ /Applications/Netgen.app/Contents/Resources/CMake C:/netgen +# Fetch f2c utility and library +FetchContent_Declare( + f2c + GIT_REPOSITORY https://github.com/mhochsteger/f2c.git + GIT_TAG 96c2fbf + EXCLUDE_FROM_ALL ) -# check if CMAKE_INSTALL_PREFIX is set by user, if not install in NGSolve python dir -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX ${NGSOLVE_INSTALL_DIR}/${NGSOLVE_INSTALL_DIR_PYTHON} CACHE PATH "Install dir" FORCE) -endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) +FetchContent_MakeAvailable(f2c) +if(UNIX) + target_compile_options(f2c_exe PRIVATE -Wno-implicit-function-declaration) +endif() -################################ -# Build f2c utility and library -ExternalProject_Add( - project_f2c - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/f2c - GIT_REPOSITORY https://github.com/mhochsteger/f2c.git - GIT_TAG 747683f - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX=${NGSOLVE_INSTALL_DIR} - -DF2C_INSTALL_DIR_BIN=${NETGEN_INSTALL_DIR_BIN} - -DF2C_INSTALL_DIR_LIB=${NETGEN_INSTALL_DIR_LIB} - -DF2C_INSTALL_DIR_INCLUDE=${NETGEN_INSTALL_DIR_INCLUDE} - -DCMAKE_C_FLAGS="-Wno-implicit-function-declaration" -) +get_target_property(F2C_DIR f2c_exe BINARY_DIR) +if(WIN32) + set(F2C_COMMAND ${F2C_DIR}/Release/f2c.exe) +else() + set(F2C_COMMAND ${F2C_DIR}/f2c) +endif() + +set(BIN_DIR ${CMAKE_CURRENT_BINARY_DIR}/specialcf) + +macro( fetch_slatec_function slatec_function ) + file(DOWNLOAD http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=slatec%2Fsrc%2F${slatec_function}.f ${BIN_DIR}/${slatec_function}.tgz) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ${BIN_DIR}/${slatec_function}.tgz WORKING_DIRECTORY ${BIN_DIR}) +endmacro( fetch_slatec_function ) -ExternalProject_Add( - project_fetch_slatec - DEPENDS project_f2c - DOWNLOAD_COMMAND "" - CONFIGURE_COMMAND ${CMAKE_COMMAND} - -DBIN_DIR=${CMAKE_CURRENT_BINARY_DIR}/specialcf - -DNGSOLVE_BINARY_DIR=${NGSOLVE_BINARY_DIR} - -P ${CMAKE_CURRENT_SOURCE_DIR}/src/fetch_and_convert_slatec.cmake - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/specialcf/slatec/src - BUILD_COMMAND "" - INSTALL_COMMAND "" +fetch_slatec_function(zbesi) +fetch_slatec_function(zbesj) +fetch_slatec_function(zbesk) +fetch_slatec_function(gamln) +fetch_slatec_function(zbesh) + +file(DOWNLOAD http://www.netlib.org/blas/i1mach.f ${BIN_DIR}/slatec/src/i1mach.f) +file(DOWNLOAD http://www.netlib.org/blas/r1mach.f ${BIN_DIR}/slatec/src/r1mach.f) +file(DOWNLOAD http://www.netlib.org/blas/d1mach.f ${BIN_DIR}/slatec/src/d1mach.f) + +file(DOWNLOAD http://ab-initio.mit.edu/Faddeeva.cc ${BIN_DIR}/Faddeeva/Faddeeva.cc) +file(DOWNLOAD http://ab-initio.mit.edu/Faddeeva.hh ${BIN_DIR}/Faddeeva/Faddeeva.hh) + +file(GLOB SLATEC_SOURCES_FORTRAN ${BIN_DIR}/slatec/src/*.f ) + +set(SLATEC_SOURCES_C) +foreach(FORTRAN_FILE ${SLATEC_SOURCES_FORTRAN}) + string(REGEX REPLACE "\\.f$" ".c" C_FILE ${FORTRAN_FILE}) + list(APPEND SLATEC_SOURCES_C ${C_FILE}) +endforeach() +message(STATUS "C files: ${SLATEC_SOURCES_C}") + +add_custom_target(convert_sources ALL + DEPENDS ${SLATEC_SOURCES_FORTRAN} f2c_exe + COMMAND ${CMAKE_COMMAND} -E make_directory ${BIN_DIR}/slatec/src + COMMAND ${F2C_COMMAND} -a ${SLATEC_SOURCES_FORTRAN} WORKING_DIRECTORY ${BIN_DIR}/slatec/src + BYPRODUCTS ${SLATEC_SOURCES_C} + USES_TERMINAL ) -################################ -# Build actual project -ExternalProject_Add( - project_ngsolve_specialcfs - DEPENDS project_f2c project_fetch_slatec - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src - BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/specialcf - CMAKE_ARGS -DNGSolve_DIR=${NGSolve_DIR} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} ${CL_ARGS} -DCMAKE_C_FLAGS="-Wno-implicit-function-declaration" - BUILD_ALWAYS 1 - ) +add_ngsolve_addon(ngsolve_special_functions src/specialcf.cpp ${SLATEC_SOURCES_C} ${CMAKE_CURRENT_BINARY_DIR}/specialcf/Faddeeva/Faddeeva.cc) +add_custom_target(ngsolve_special_functions_convert ALL DEPENDS convert_sources) +target_sources(ngsolve_special_functions PRIVATE ${SLATEC_SOURCES_C}) +target_include_directories(ngsolve_special_functions PRIVATE ${BIN_DIR}/slatec/src ${BIN_DIR}/Faddeeva inc ${f2c_SOURCE_DIR}/include ${f2c_BINARY_DIR}) +target_compile_definitions(ngsolve_special_functions PRIVATE -DSPECIALCF_LIBRARY_NAME="$") +if(UNIX) + target_compile_options(ngsolve_special_functions PRIVATE -Wno-implicit-function-declaration) +endif() +target_link_libraries(ngsolve_special_functions PRIVATE f2c) +install(TARGETS ngsolve_special_functions DESTINATION ${ADDON_INSTALL_DIR_PYTHON}) +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(ngsolve_special_functions PRIVATE -Wno-return-type-c-linkage) +endif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + diff --git a/demo/bessel.py b/demo/bessel.py index 2cd21f6..5954d90 100644 --- a/demo/bessel.py +++ b/demo/bessel.py @@ -1,3 +1,4 @@ +import ngsolve_special_functions from ngsolve import * from netgen.geom2d import unit_square import scipy.special as sp @@ -6,11 +7,10 @@ mesh = Mesh(unit_square.GenerateMesh(maxh=0.2)) -import ngsolve.special_functions n = 0 while n>=0: - bessel = ngsolve.special_functions.jv(z=(x-0.5)+1j*(y-0.5), v=n) + bessel = ngsolve_special_functions.jv(z=(x-0.5)+1j*(y-0.5), v=n) Draw(bessel, mesh, 'bessel', sd=5) n = float(input('Set order (<0 to abort): ')) diff --git a/ngsolve_addon.cmake b/ngsolve_addon.cmake new file mode 100644 index 0000000..72a03ee --- /dev/null +++ b/ngsolve_addon.cmake @@ -0,0 +1,97 @@ +############################################################################### +# This file was taken from https://github.com/NGSolve/ngsolve-addon-template +# Make sure to check for updates regularly. +# Don't change anything here (unless you know what you are doing!) +############################################################################### +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Find NGSolve and Netgen using python +if(CMAKE_VERSION VERSION_LESS "3.18") + find_package(Python3 REQUIRED COMPONENTS Interpreter Development) +else() + find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module) +endif() + + +set(Netgen_DIR "" CACHE PATH "Path to directory containing NetgenConfig.cmake") +set(NGSolve_DIR "" CACHE PATH "Path to directory containing NGSolveConfig.cmake") + +execute_process(COMMAND ${Python3_EXECUTABLE} -m netgen.config OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE Netgen_DIR) +execute_process(COMMAND ${Python3_EXECUTABLE} -m ngsolve.config OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE NGSolve_DIR) + +find_package(NGSolve CONFIG REQUIRED) + +macro(add_ngsolve_addon module_name) + # Create the module + add_library(${module_name} SHARED ${ARGN}) + target_link_libraries(${module_name} PUBLIC ngsolve Python3::Module) + set_target_properties(${module_name} PROPERTIES PREFIX "" CXX_STANDARD 17) + + # Python does not recognize .dll (Windows) and .dylib (MacOS) file endings as modules + if(WIN32) + set_target_properties(${module_name} PROPERTIES SUFFIX ".pyd" ) + else(WIN32) + set_target_properties(${module_name} PROPERTIES SUFFIX ".so") + endif(WIN32) +endmacro() + +execute_process(COMMAND ${Python3_EXECUTABLE} -c "import sys,sysconfig,os.path; print(os.path.relpath(sysconfig.get_path('platlib'), sys.prefix))" + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE python3_library_dir +) + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + if(NETGEN_BULID_FOR_CONDA) + # Netgen is assumed to be installed as python package + # Set install prefix to user-base if a user site is available, sys.prefix otherwise + execute_process(COMMAND ${Python3_EXECUTABLE} -c "import sys; print(sys.prefix)" + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE install_prefix + ) + execute_process(COMMAND ${Python3_EXECUTABLE} -m site --user-base + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE user_base RESULT_VARIABLE ret + ) + if (ret EQUAL 0) + set(install_prefix ${user_base}) + endif() + set(CMAKE_INSTALL_PREFIX ${install_prefix}/${python3_library_dir} CACHE PATH "Install dir" FORCE) + else() + # Netgen is self-compiled -> install into Netgen directory + set(CMAKE_INSTALL_PREFIX ${NETGEN_DIR} CACHE PATH "Install prefix" FORCE) + endif() + set(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OFF) +endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + +if(SKBUILD_PLATLIB_DIR) + set(ADDON_INSTALL_DIR_PYTHON ${SKBUILD_PLATLIB_DIR}) + set(ADDON_INSTALL_DIR_BIN ${SKBUILD_DATA_DIR}/bin) + set(ADDON_INSTALL_DIR_INCLUDE ${SKBUILD_DATA_DIR}/include) + set(ADDON_INSTALL_DIR_LIB ${SKBUILD_DATA_DIR}/lib) + set(ADDON_INSTALL_DIR_RES ${SKBUILD_DATA_DIR}/share) + set(ADDON_INSTALL_DIR_CMAKE ${SKBUILD_DATA_DIR}/lib/cmake) + set(stubgen_working_dir ${SKBUILD_PLATLIB_DIR}) +elseif(NETGEN_BUILD_FOR_CONDA) + set(stubgen_working_dir ${CMAKE_INSTALL_PREFIX}) + set(ADDON_INSTALL_DIR_PYTHON ${python3_library_dir}) + set(ADDON_INSTALL_DIR_BIN bin) + set(ADDON_INSTALL_DIR_INCLUDE include) + set(ADDON_INSTALL_DIR_LIB lib) + set(ADDON_INSTALL_DIR_RES share) + set(ADDON_INSTALL_DIR_CMAKE lib/cmake) +else() + set(stubgen_working_dir ${CMAKE_INSTALL_PREFIX}/${NETGEN_INSTALL_DIR_PYTHON}) + set(ADDON_INSTALL_DIR_PYTHON ${NETGEN_INSTALL_DIR_PYTHON}) + set(ADDON_INSTALL_DIR_BIN ${NETGEN_INSTALL_DIR_BIN}) + set(ADDON_INSTALL_DIR_INCLUDE ${NETGEN_INSTALL_DIR_INCLUDE}) + set(ADDON_INSTALL_DIR_LIB ${NETGEN_INSTALL_DIR_LIB}) + set(ADDON_INSTALL_DIR_RES ${NETGEN_INSTALL_DIR_RES}) + set(ADDON_INSTALL_DIR_CMAKE ${NETGEN_INSTALL_DIR_CMAKE}) +endif() + +macro(ngsolve_generate_stub_files module_name) + set(stubgen_generation_code "execute_process(WORKING_DIRECTORY ${stubgen_working_dir} COMMAND ${Python3_EXECUTABLE} -m pybind11_stubgen --ignore-all-errors -o ${CMAKE_CURRENT_BINARY_DIR}/stubs ${module_name})") + set(stubgen_directory "${CMAKE_CURRENT_BINARY_DIR}/stubs/${module_name}/") + + install(CODE ${stubgen_generation_code}) + install(DIRECTORY ${stubgen_directory} DESTINATION ${ADDON_INSTALL_DIR_PYTHON}/${module_name}) +endmacro() + +message(STATUS "Install dir: ${CMAKE_INSTALL_PREFIX}") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8558a8e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +# Here is the project metadata, adapt it to your needs +[project] +name = "ngsolve_special_functions" +description="Additional coefficient functions based on slatec" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] # ngsolve will be added automatically +dynamic = ["version", "dependencies"] +classifiers = [ + "License :: OSI Approved :: MIT License", +] + +# Update NGSolve when you rely on newer features, but keep the '>=' to allow also newer versions +# Maybe you also need to add other requirements to build your package +[build-system] +requires = ["ngsolve>=6.2.2404.post16", "scikit-build-core>=0.9.0", "pybind11_stubgen", "cmake", "toml"] +build-backend = "scikit_build_core.build" + +########################################################################## +# Settings for cibuildwheel to build .whl files for Windows/Mac/Linxu +# DO NOT CHANGE THIS UNLESS YOU KNOW WHAT YOU ARE DOING +########################################################################## + +[tool.scikit-build] +experimental = true +metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" +metadata.dependencies.provider="ngsolve._scikit_build_core_dependencies" + +[tool.setuptools_scm] +local_scheme = "no-local-version" + +[tool.cibuildwheel] +skip = """ + pp* + *_i686 + *musllinux* + *win32 +""" + +[tool.cibuildwheel.linux] +repair-wheel-command = "mv {wheel} {dest_dir} && rename linux_x86_64 manylinux_2_17_x86_64.manylinux2014_x86_64 {dest_dir}/*-linux_x86_64*.whl" + +[tool.cibuildwheel.macos] +environment = {MACOSX_DEPLOYMENT_TARGET="10.15"} +repair-wheel-command = "" +archs = ["universal2"] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index cbd0353..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -project(ngs_special_functions) - -cmake_minimum_required(VERSION 3.8) -set(CMAKE_CXX_STANDARD 17) - -find_package(NGSolve CONFIG REQUIRED) - -find_library(F2C_LIBRARY f2c REQUIRED HINTS ${NGSOLVE_LIBRARY_DIR}) - -file(GLOB SLATEC_SOURCES_C ${CMAKE_CURRENT_BINARY_DIR}/slatec/src/*.c ) - -add_ngsolve_python_module(special_functions - specialcf.cpp ${SLATEC_SOURCES_C} ${CMAKE_CURRENT_BINARY_DIR}/Faddeeva/Faddeeva.cc -) - -target_include_directories(special_functions PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../inc ${CMAKE_CURRENT_BINARY_DIR}/Faddeeva) -target_link_libraries(special_functions PRIVATE ${F2C_LIBRARY}) -target_compile_definitions(special_functions PRIVATE -DSPECIALCF_LIBRARY_NAME="${NETGEN_PYTHON_DIR}/ngsolve/$") -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_compile_options(special_functions PRIVATE -Wno-return-type-c-linkage) -endif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - -install(TARGETS special_functions DESTINATION ngsolve) diff --git a/src/fetch_and_convert_slatec.cmake b/src/fetch_and_convert_slatec.cmake deleted file mode 100644 index 81d3e42..0000000 --- a/src/fetch_and_convert_slatec.cmake +++ /dev/null @@ -1,21 +0,0 @@ -find_program(F2C_COMMAND f2c REQUIRED HINTS ${NGSOLVE_BINARY_DIR}) - -macro( fetch_and_convert_slatec_sources slatec_function ) - file(DOWNLOAD http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=slatec%2Fsrc%2F${slatec_function}.f ${BIN_DIR}/${slatec_function}.tgz) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ${BIN_DIR}/${slatec_function}.tgz WORKING_DIRECTORY ${BIN_DIR}) -endmacro( fetch_and_convert_slatec_sources ) - -fetch_and_convert_slatec_sources(zbesi) -fetch_and_convert_slatec_sources(zbesj) -fetch_and_convert_slatec_sources(zbesk) -fetch_and_convert_slatec_sources(gamln) -fetch_and_convert_slatec_sources(zbesh) -file(DOWNLOAD http://www.netlib.org/blas/i1mach.f ${BIN_DIR}/slatec/src/i1mach.f) -file(DOWNLOAD http://www.netlib.org/blas/r1mach.f ${BIN_DIR}/slatec/src/r1mach.f) -file(DOWNLOAD http://www.netlib.org/blas/d1mach.f ${BIN_DIR}/slatec/src/d1mach.f) - -file(DOWNLOAD http://ab-initio.mit.edu/Faddeeva.cc ${BIN_DIR}/Faddeeva/Faddeeva.cc) -file(DOWNLOAD http://ab-initio.mit.edu/Faddeeva.hh ${BIN_DIR}/Faddeeva/Faddeeva.hh) - -file(GLOB SLATEC_SOURCES_FORTRAN ${BIN_DIR}/slatec/src/*.f ) -execute_process(COMMAND ${F2C_COMMAND} -a ${SLATEC_SOURCES_FORTRAN} WORKING_DIRECTORY ${BIN_DIR}/slatec/src) diff --git a/src/specialcf.cpp b/src/specialcf.cpp index e53b545..da9f0ad 100644 --- a/src/specialcf.cpp +++ b/src/specialcf.cpp @@ -96,7 +96,7 @@ namespace ngfem { -PYBIND11_MODULE(special_functions, m) { +PYBIND11_MODULE(ngsolve_special_functions, m) { const char * doc_string = "Same as in scipy.special"; ExportPythonSpecialCF(m, "gammaln", gammaln, doc_string);