From 68a2e3fe2fb19262174e2fdf51935887904b0ff9 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Sun, 25 Aug 2024 21:22:04 +0200 Subject: [PATCH 1/2] [WIP] Cython language --- .../cmake/CMakeCythonCompiler.cmake.in | 25 +++ .../cmake/CMakeCythonInformation.cmake | 28 +++ .../cmake/CMakeDetermineCythonCompiler.cmake | 42 +++++ .../cmake/CMakeTestCythonCompiler.cmake | 27 +++ src/cython_cmake/cmake/TestCythonCompiler.pyx | 4 + tests/packages/simple_language/.gitignore | 176 ++++++++++++++++++ tests/packages/simple_language/CMakeLists.txt | 13 ++ tests/packages/simple_language/pyproject.toml | 7 + tests/packages/simple_language/simple.pyx | 4 + tests/test_package.py | 17 ++ 10 files changed, 343 insertions(+) create mode 100644 src/cython_cmake/cmake/CMakeCythonCompiler.cmake.in create mode 100644 src/cython_cmake/cmake/CMakeCythonInformation.cmake create mode 100644 src/cython_cmake/cmake/CMakeDetermineCythonCompiler.cmake create mode 100644 src/cython_cmake/cmake/CMakeTestCythonCompiler.cmake create mode 100644 src/cython_cmake/cmake/TestCythonCompiler.pyx create mode 100644 tests/packages/simple_language/.gitignore create mode 100644 tests/packages/simple_language/CMakeLists.txt create mode 100644 tests/packages/simple_language/pyproject.toml create mode 100644 tests/packages/simple_language/simple.pyx diff --git a/src/cython_cmake/cmake/CMakeCythonCompiler.cmake.in b/src/cython_cmake/cmake/CMakeCythonCompiler.cmake.in new file mode 100644 index 0000000..4573887 --- /dev/null +++ b/src/cython_cmake/cmake/CMakeCythonCompiler.cmake.in @@ -0,0 +1,25 @@ +message(WARNING "Entering CMakeCythonCompiler.cmake") + +#set(CMAKE_Cython_COMPILER "@CMAKE_Cython_COMPILER@") +#set(CMAKE_Cython_COMPILER_ID "@CMAKE_Cython_COMPILER_ID@") +#set(CMAKE_Cython_COMPILER_VERSION "@CMAKE_Cython_COMPILER_VERSION@") + +set(CMAKE_Cython_COMPILER "@CYTHON_EXE@") +set(CMAKE_Cython_COMPILER_ID "dummy2") +set(CMAKE_Cython_COMPILER_VERSION "dummy3") + +set(CMAKE_Cython_COMPILER_LOADED 1) +#set(CMAKE_Cython_COMPILER_WORKS "@CMAKE_Cython_COMPILER_WORKS@") +set(CMAKE_Cython_COMPILER_WORKS 1 CACHE INTERNAL "") + +#set(CMAKE_Cython_COMPILER_ID_RUN "@CMAKE_Cython_COMPILER_ID_RUN@") +set(CMAKE_Cython_COMPILER_ID_RUN "dummy4") +## Not sure how to ensure the correct order of enable_language so that this works +#set(CMAKE_INCLUDE_FLAG_Cython "${CMAKE_INCLUDE_FLAG_C}") +set(CMAKE_INCLUDE_FLAG_Cython "-I") +## Approach 1 +#set(CMAKE_Cython_OUTPUT_EXTENSION ".c") +## Approach 2 +set(CMAKE_Cython_OUTPUT_EXTENSION ".o") +set(CMAKE_Cython_IGNORE_EXTENSIONS "c;o;O;obj;OBJ") +set(CMAKE_Cython_SOURCE_FILE_EXTENSIONS "pyx;py") diff --git a/src/cython_cmake/cmake/CMakeCythonInformation.cmake b/src/cython_cmake/cmake/CMakeCythonInformation.cmake new file mode 100644 index 0000000..2179864 --- /dev/null +++ b/src/cython_cmake/cmake/CMakeCythonInformation.cmake @@ -0,0 +1,28 @@ +message(WARNING "Entering CMakeCythonInformation.cmake") + +#set(CMAKE_Cython_CREATE_SHARED_LIBRARY) +#set(CMAKE_Cython_CREATE_SHARED_MODULE) +#set(CMAKE_Cython_CREATE_STATIC_LIBRARY) +#set(CMAKE_Cython_COMPILE_OBJECT) +#set(CMAKE_Cython_LINK_EXECUTABLE) + +if(NOT CMAKE_Cython_COMPILE_OBJECT) + ## Typical definition +# set(CMAKE_Cython_COMPILE_OBJECT +# " -c -o ") + ## Approach 1 +# set(CMAKE_Cython_COMPILE_OBJECT +# " -o ") + ## Approach 2 + string(REPLACE "" ".c" CMAKE_C_COMPILE_OBJECT_replaced "${CMAKE_C_COMPILE_OBJECT}") + set(CMAKE_Cython_COMPILE_OBJECT + " -o .c " + "${CMAKE_C_COMPILE_OBJECT_replaced}" + ) +endif() + +set(CMAKE_Cython_CREATE_SHARED_LIBRARY ${CMAKE_C_CREATE_SHARED_LIBRARY}) +set(CMAKE_Cython_CREATE_SHARED_MODULE ${CMAKE_C_CREATE_SHARED_MODULE}) +set(CMAKE_Cython_LINK_EXECUTABLE ${CMAKE_C_LINK_EXECUTABLE}) + +set(CMAKE_Cython_INFORMATION_LOADED 1) diff --git a/src/cython_cmake/cmake/CMakeDetermineCythonCompiler.cmake b/src/cython_cmake/cmake/CMakeDetermineCythonCompiler.cmake new file mode 100644 index 0000000..15badb6 --- /dev/null +++ b/src/cython_cmake/cmake/CMakeDetermineCythonCompiler.cmake @@ -0,0 +1,42 @@ +message(WARNING "Entering CMakeDetermineCythonCompiler.cmake") + +find_package(Python 3.8 REQUIRED Interpreter Development.Module) +find_program(CYTHON_EXE + cython + REQUIRED +) + +#include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) +##include(Platform/${CMAKE_SYSTEM_NAME}-Determine-Cython OPTIONAL) +##include(Platform/${CMAKE_SYSTEM_NAME}-Cython OPTIONAL) +#if(NOT CMAKE_Cython_COMPILER_NAMES) +# set(CMAKE_Cython_COMPILER_NAMES cython) +#endif() + +## Build a small source file to identify the compiler. +#if(NOT CMAKE_Cython_COMPILER_ID_RUN) +# set(CMAKE_Cython_COMPILER_ID_RUN 1) +# +# # Try to identify the compiler. +# set(CMAKE_Cython_COMPILER_ID) +# include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake) +# CMAKE_DETERMINE_COMPILER_ID(Cython CYTHONFLAGS CMakeCythonCompilerId.pyx) +# +# execute_process(COMMAND "${CMAKE_Cython_COMPILER}" "/help /preferreduilang:en-US" OUTPUT_VARIABLE output) +# string(REPLACE "\n" ";" output "${output}") +# foreach(line ${output}) +# string(TOUPPER ${line} line) +# string(REGEX REPLACE "^.*COMPILER.*VERSION[^\\.0-9]*([\\.0-9]+).*$" "\\1" version "${line}") +# if(version AND NOT "x${line}" STREQUAL "x${version}") +# set(CMAKE_Cython_COMPILER_VERSION ${version}) +# break() +# endif() +# endforeach() +# message(STATUS "The Cython compiler version is ${CMAKE_Cython_COMPILER_VERSION}") +#endif() + +# configure variables set in this file for fast reload later on +configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeCythonCompiler.cmake.in + ${CMAKE_PLATFORM_INFO_DIR}/CMakeCythonCompiler.cmake + @ONLY + ) diff --git a/src/cython_cmake/cmake/CMakeTestCythonCompiler.cmake b/src/cython_cmake/cmake/CMakeTestCythonCompiler.cmake new file mode 100644 index 0000000..d02bcac --- /dev/null +++ b/src/cython_cmake/cmake/CMakeTestCythonCompiler.cmake @@ -0,0 +1,27 @@ +message(WARNING "Entering CMakeTestCythonCompiler.cmake") + +if(CMAKE_Cython_COMPILER_FORCED) + # The compiler configuration was forced by the user. + # Assume the user has configured all compiler information. + set(CMAKE_Cython_COMPILER_WORKS TRUE) + return() +endif() + +include(CMakeTestCompilerCommon) + +unset(CMAKE_Cython_COMPILER_WORKS CACHE) + +if(NOT CMAKE_Cython_COMPILER_WORKS) + message(WARNING "Running tests: CMakeTestCythonCompiler.cmake") + PrintTestCompilerStatus(Cython) + unset(CMAKE_Cython_COMPILER_WORKS) + try_compile(CMAKE_Cython_COMPILER_WORKS + SOURCES ${CMAKE_CURRENT_LIST_DIR}/TestCythonCompiler.pyx + OUTPUT_VARIABLE __CMAKE_Cython_COMPILER_OUTPUT + ) + message(WARNING "__CMAKE_Cython_COMPILER_OUTPUT=${__CMAKE_Cython_COMPILER_OUTPUT}") + unset(__TestCompiler_testCythonCompilerSource) + set(CMAKE_Cython_COMPILER_WORKS ${CMAKE_Cython_COMPILER_WORKS}) + unset(CMAKE_Cython_COMPILER_WORKS CACHE) + set(Cython_TEST_WAS_RUN 1) +endif() diff --git a/src/cython_cmake/cmake/TestCythonCompiler.pyx b/src/cython_cmake/cmake/TestCythonCompiler.pyx new file mode 100644 index 0000000..c9d9004 --- /dev/null +++ b/src/cython_cmake/cmake/TestCythonCompiler.pyx @@ -0,0 +1,4 @@ +# cython: language_level=3 + +def square(float x): + return x * x diff --git a/tests/packages/simple_language/.gitignore b/tests/packages/simple_language/.gitignore new file mode 100644 index 0000000..ad4a1f1 --- /dev/null +++ b/tests/packages/simple_language/.gitignore @@ -0,0 +1,176 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/tests/packages/simple_language/CMakeLists.txt b/tests/packages/simple_language/CMakeLists.txt new file mode 100644 index 0000000..1acd98e --- /dev/null +++ b/tests/packages/simple_language/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.15...3.29) +set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "") +project(${SKBUILD_PROJECT_NAME} LANGUAGES C Cython) + +#find_package( +# Python +# COMPONENTS Interpreter Development.Module +# REQUIRED) + +python_add_library(simple MODULE WITH_SOABI) +target_sources(simple PRIVATE simple.pyx) + +install(TARGETS simple DESTINATION .) diff --git a/tests/packages/simple_language/pyproject.toml b/tests/packages/simple_language/pyproject.toml new file mode 100644 index 0000000..a0f7d59 --- /dev/null +++ b/tests/packages/simple_language/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = ["scikit-build-core", "cython", "cython-cmake"] +build-backend = "scikit_build_core.build" + +[project] +name = "example" +version = "0.0.1" diff --git a/tests/packages/simple_language/simple.pyx b/tests/packages/simple_language/simple.pyx new file mode 100644 index 0000000..c9d9004 --- /dev/null +++ b/tests/packages/simple_language/simple.pyx @@ -0,0 +1,4 @@ +# cython: language_level=3 + +def square(float x): + return x * x diff --git a/tests/test_package.py b/tests/test_package.py index 8764efd..2d8a617 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -34,6 +34,23 @@ def test_simple(monkeypatch, tmp_path): assert "simple.c" in build_files +def test_simple_language(monkeypatch, tmp_path): + monkeypatch.chdir(DIR / "packages/simple_language") + build_dir = tmp_path / "build" + + wheel = build_wheel( + str(tmp_path), {"build-dir": str(build_dir), "wheel.license-files": []} + ) + + with zipfile.ZipFile(tmp_path / wheel) as f: + file_names = set(f.namelist()) + assert len(file_names) == 4 + + build_files = {x.name for x in build_dir.iterdir()} + assert "simple.c.dep" in build_files + assert "simple.c" in build_files + + @pytest.mark.parametrize("output_arg", ["empty", "relative", "absolute"]) def test_output_argument(monkeypatch, tmp_path, output_arg): package_dir = tmp_path / "pkg2" From a6ad971443347161e9a06df989f29c78fac50094 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Sun, 25 Aug 2024 21:59:02 +0200 Subject: [PATCH 2/2] [WIP] Try to resolve linking issue --- .../cmake/CMakeCythonInformation.cmake | 81 ++++++++++++++++++- tests/packages/simple_language/CMakeLists.txt | 1 + tests/test_package.py | 4 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/cython_cmake/cmake/CMakeCythonInformation.cmake b/src/cython_cmake/cmake/CMakeCythonInformation.cmake index 2179864..7fef71c 100644 --- a/src/cython_cmake/cmake/CMakeCythonInformation.cmake +++ b/src/cython_cmake/cmake/CMakeCythonInformation.cmake @@ -6,6 +6,85 @@ message(WARNING "Entering CMakeCythonInformation.cmake") #set(CMAKE_Cython_COMPILE_OBJECT) #set(CMAKE_Cython_LINK_EXECUTABLE) +if(NOT CMAKE_Cython_COMPILE_OPTIONS_PIC) + set(CMAKE_Cython_COMPILE_OPTIONS_PIC ${CMAKE_C_COMPILE_OPTIONS_PIC}) +endif() + +if(NOT CMAKE_Cython_COMPILE_OPTIONS_PIE) + set(CMAKE_Cython_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE}) +endif() +if(NOT CMAKE_Cython_LINK_OPTIONS_PIE) + set(CMAKE_Cython_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE}) +endif() +if(NOT CMAKE_Cython_LINK_OPTIONS_NO_PIE) + set(CMAKE_Cython_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE}) +endif() + +if(NOT CMAKE_Cython_COMPILE_OPTIONS_DLL) + set(CMAKE_Cython_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_CREATE_Cython_FLAGS) + set(CMAKE_SHARED_LIBRARY_CREATE_Cython_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_Cython_FLAGS) + set(CMAKE_SHARED_LIBRARY_Cython_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Cython_FLAGS) + set(CMAKE_SHARED_LIBRARY_LINK_Cython_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_Cython_FLAG) + set(CMAKE_SHARED_LIBRARY_RUNTIME_Cython_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_Cython_FLAG_SEP) + set(CMAKE_SHARED_LIBRARY_RUNTIME_Cython_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_RPATH_LINK_Cython_FLAG) + set(CMAKE_SHARED_LIBRARY_RPATH_LINK_Cython_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) +endif() + +if(NOT DEFINED CMAKE_EXE_EXPORTS_Cython_FLAG) + set(CMAKE_EXE_EXPORTS_Cython_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_Cython_FLAG) + set(CMAKE_SHARED_LIBRARY_SONAME_Cython_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG}) +endif() + + +if(NOT DEFINED CMAKE_SHARED_MODULE_CREATE_Cython_FLAGS) + set(CMAKE_SHARED_MODULE_CREATE_Cython_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS}) +endif() + +if(NOT DEFINED CMAKE_SHARED_MODULE_Cython_FLAGS) + set(CMAKE_SHARED_MODULE_Cython_FLAGS ${CMAKE_SHARED_MODULE_C_FLAGS}) +endif() + +if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_Cython_FLAG) + set(CMAKE_EXECUTABLE_RUNTIME_Cython_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_Cython_FLAG}) +endif() + +if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_Cython_FLAG_SEP) + set(CMAKE_EXECUTABLE_RUNTIME_Cython_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_Cython_FLAG_SEP}) +endif() + +if(NOT DEFINED CMAKE_EXECUTABLE_RPATH_LINK_Cython_FLAG) + set(CMAKE_EXECUTABLE_RPATH_LINK_Cython_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_Cython_FLAG}) +endif() + +if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Cython_WITH_RUNTIME_PATH) + set(CMAKE_SHARED_LIBRARY_LINK_Cython_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH}) +endif() + +if(NOT CMAKE_INCLUDE_FLAG_Cython) + set(CMAKE_INCLUDE_FLAG_Cython ${CMAKE_INCLUDE_FLAG_C}) +endif() + if(NOT CMAKE_Cython_COMPILE_OBJECT) ## Typical definition # set(CMAKE_Cython_COMPILE_OBJECT @@ -16,7 +95,7 @@ if(NOT CMAKE_Cython_COMPILE_OBJECT) ## Approach 2 string(REPLACE "" ".c" CMAKE_C_COMPILE_OBJECT_replaced "${CMAKE_C_COMPILE_OBJECT}") set(CMAKE_Cython_COMPILE_OBJECT - " -o .c " + " -o .c " "${CMAKE_C_COMPILE_OBJECT_replaced}" ) endif() diff --git a/tests/packages/simple_language/CMakeLists.txt b/tests/packages/simple_language/CMakeLists.txt index 1acd98e..904973d 100644 --- a/tests/packages/simple_language/CMakeLists.txt +++ b/tests/packages/simple_language/CMakeLists.txt @@ -9,5 +9,6 @@ project(${SKBUILD_PROJECT_NAME} LANGUAGES C Cython) python_add_library(simple MODULE WITH_SOABI) target_sources(simple PRIVATE simple.pyx) +target_link_libraries(simple PRIVATE Python::Module) install(TARGETS simple DESTINATION .) diff --git a/tests/test_package.py b/tests/test_package.py index 2d8a617..731a1b4 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -47,8 +47,8 @@ def test_simple_language(monkeypatch, tmp_path): assert len(file_names) == 4 build_files = {x.name for x in build_dir.iterdir()} - assert "simple.c.dep" in build_files - assert "simple.c" in build_files + # assert "simple.c.dep" in build_files + # assert "simple.c" in build_files @pytest.mark.parametrize("output_arg", ["empty", "relative", "absolute"])