Skip to content

Commit

Permalink
feat: deduce C/CXX if possible
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <[email protected]>
  • Loading branch information
henryiii committed Jun 25, 2024
1 parent 458b89f commit 26b1889
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 8 deletions.
47 changes: 40 additions & 7 deletions src/cython_cmake/cmake/UseCython.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,54 @@
#=============================================================================

if(CMAKE_VERSION VERSION_LESS "3.20")
message(SEND_ERROR "CMake 3.20 required")
message(FATAL_ERROR "CMake 3.20 required")
endif()


function(Cython_compile_pyx INPUT)
function(Cython_compile_pyx)
cmake_parse_arguments(
PARSE_ARGV 1
PARSE_ARGV 0
CYTHON
""
"OUTPUT;LANGUAGE;OUTPUT_VARIABLE"
"CYTHON_ARGS"
)
set(ALL_INPUT ${CYTHON_UNPARSED_ARGUMENTS})
list(LENGTH ALL_INPUT INPUT_LENGTH)
if(NOT INPUT_LENGTH EQUAL 1)
message(FATAL_ERROR "One and only one input file must be specified, got '${ALL_INPUT}'")
endif()
list(GET ALL_INPUT 0 INPUT)

# Set target language (required)
# Set target language
if(NOT CYTHON_LANGUAGE)
message(SEND_ERROR "cython_compile_pyx LANGUAGE keyword is required")
get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)

if("C" IN_LIST _langauges AND "CXX" IN_LIST _languages)
# Try to compute language. Returns falsy if not found.
_cython_compute_language(CYTHON_LANGUAGE ${INPUT})
message(STATUS "${CYTHON_LANGUAGE}")
elseif("C" IN_LIST _languages)
# If only C is enabled globally, assume C
set(CYTHON_LANGUAGE C)
elseif("CXX" IN_LIST _languages)
# Likewise for CXX
set(CYTHON_LANGUAGE "CXX")
else()
message(FATAL_ERROR "LANGUAGE keyword required if neither C nor CXX enabled globally")
endif()
endif()

if(NOT CYTHON_LANGUAGE)
message(FATAL_ERROR "LANGUAGE keyword or `# distutils: language=...` required if C and CXX are enabled globally")
elseif(CYTHON_LANGUAGE STREQUAL C)
set(language_arg "")
set(langauge_ext ".c")
elseif(CYTHON_LANGUAGE STREQUAL CXX)
set(language_arg "--cplus")
set(langauge_ext ".cxx")
else()
message(SEND_ERROR "cython_compile_pyx LANGUAGE must be one of C or CXX")
message(FATAL_ERROR "cython_compile_pyx LANGUAGE must be one of C or CXX")
endif()

# Place the cython files in the current binary dir if no path given
Expand Down Expand Up @@ -135,7 +159,7 @@ function(Cython_compile_pyx INPUT)
--depfile
"${INPUT}"
--output-file "${CYTHON_OUTPUT}"
DEPENDS
MAIN_DEPENDENCY
"${INPUT}"
DEPFILE
"${depfile_path}"
Expand All @@ -145,3 +169,12 @@ function(Cython_compile_pyx INPUT)
)

endfunction()

function(_cython_compute_language OUTPUT_VARIABLE FILENAME)
file(READ "${FILENAME}" FILE_CONTENT)
set(REGEX_PATTERN [=[^[[:space:]]*#[[:space:]]*distutils:.*language[[:space:]]*=[[:space:]]*(c\\+\\+|c)]=])
string(REGEX MATCH "${REGEX_PATTERN}" MATCH_RESULT "${FILE_CONTENT}")
string(TOUPPER "${MATCH_RESULT}" LANGUAGE_NAME)
string(REPLACE "+" "X" LANGUAGE_NAME "${LANGUAGE_NAME}")
set(${OUTPUT_VARIABLE} ${LANGUAGE_NAME} PARENT_SCOPE)
endfunction()
59 changes: 58 additions & 1 deletion tests/test_package.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import importlib.metadata
import shutil
import zipfile
from pathlib import Path

Expand All @@ -15,7 +16,7 @@ def test_version():
assert importlib.metadata.version("cython_cmake") == m.__version__


def test_scikit_build_core(monkeypatch, tmp_path):
def test_simple(monkeypatch, tmp_path):
monkeypatch.chdir(DIR / "packages/simple")
build_dir = tmp_path / "build"

Expand All @@ -30,3 +31,59 @@ def test_scikit_build_core(monkeypatch, tmp_path):
build_files = {x.name for x in build_dir.iterdir()}
assert "simple.c.dep" in build_files
assert "simple.c" in build_files


def test_implicit_cxx(monkeypatch, tmp_path):
package_dir = tmp_path / "pkg2"
shutil.copytree(DIR / "packages/simple", package_dir)
monkeypatch.chdir(package_dir)

cmakelists = Path("CMakeLists.txt")
txt = (
cmakelists.read_text()
.replace("LANGUAGE C", "")
.replace("LANGUAGES C", "LANGUAGES CXX")
)
cmakelists.write_text(txt)

wheel = build_wheel(
str(tmp_path), {"build-dir": "build", "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 Path("build").iterdir()}
assert "simple.cxx.dep" in build_files
assert "simple.cxx" in build_files


def test_directive_cxx(monkeypatch, tmp_path):
package_dir = tmp_path / "pkg3"
shutil.copytree(DIR / "packages/simple", package_dir)
monkeypatch.chdir(package_dir)

cmakelists = Path("CMakeLists.txt")
txt = (
cmakelists.read_text()
.replace("LANGUAGE C", "")
.replace("LANGUAGES C", "LANGUAGES CXX")
)
cmakelists.write_text(txt)

simple = Path("simple.pyx")
txt = simple.read_text()
simple.write_text(f"# distutils: language=c++\n{txt}")

wheel = build_wheel(
str(tmp_path), {"build-dir": "build", "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 Path("build").iterdir()}
assert "simple.cxx.dep" in build_files
assert "simple.cxx" in build_files

0 comments on commit 26b1889

Please sign in to comment.