Skip to content

Commit baba6e4

Browse files
henryiiijcfr
andcommitted
feat: deduce C/CXX if possible
Add tests `test_implicit_cxx` and `test_directive_cxx` Co-authored-by: Jean-Christophe Fillion-Robin <[email protected]>
1 parent a403534 commit baba6e4

File tree

2 files changed

+77
-8
lines changed

2 files changed

+77
-8
lines changed

src/cython_cmake/cmake/UseCython.cmake

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
# Options:
1717
#
1818
# ``LANGUAGE [C | CXX]``
19-
# Force the generation of either a C or C++ file. By default, a C file is
20-
# generated, unless the C language is not enabled for the project; in this
21-
# case, a C++ file is generated by default.
19+
# Force the generation of either a C or C++ file. Recommended; will attempt
20+
# to be deduced if not specified, defaults to C unless only CXX is enabled.
2221
#
2322
# ``CYTHON_ARGS <args>``
2423
# Specify additional arguments for the cythonization process. Will default to
@@ -176,7 +175,10 @@ function(Cython_compile_pyx)
176175
set(_language ${_args_LANGUAGE})
177176
if(NOT _language)
178177
get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
179-
if("C" IN_LIST _languages)
178+
if("C" IN_LIST _languages AND "CXX" IN_LIST _languages)
179+
# Try to compute language. Returns falsy if not found.
180+
_cython_compute_language(_language ${_source_file})
181+
elseif("C" IN_LIST _languages)
180182
# If only C is enabled globally, assume C
181183
set(_language "C")
182184
elseif("CXX" IN_LIST _languages)
@@ -185,10 +187,10 @@ function(Cython_compile_pyx)
185187
else()
186188
message(FATAL_ERROR "LANGUAGE keyword required if neither C nor CXX enabled globally")
187189
endif()
188-
else()
189-
if(NOT _language MATCHES "^(C|CXX)$")
190-
message(FATAL_ERROR "cython_compile_pyx LANGUAGE must be one of C or CXX")
191-
endif()
190+
endif()
191+
192+
if(NOT _language MATCHES "^(C|CXX)$")
193+
message(FATAL_ERROR "cython_compile_pyx LANGUAGE must be one of C or CXX")
192194
endif()
193195

194196
# Place the cython files in the current binary dir if no path given
@@ -209,3 +211,14 @@ function(Cython_compile_pyx)
209211
endif()
210212

211213
endfunction()
214+
215+
function(_cython_compute_language OUTPUT_VARIABLE FILENAME)
216+
file(READ "${FILENAME}" FILE_CONTENT)
217+
# Check for compiler directive similar to "# distutils: language = c++"
218+
# See https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html#declare-a-var-with-the-wrapped-c-class
219+
set(REGEX_PATTERN [=[^[[:space:]]*#[[:space:]]*distutils:.*language[[:space:]]*=[[:space:]]*(c\\+\\+|c)]=])
220+
string(REGEX MATCH "${REGEX_PATTERN}" MATCH_RESULT "${FILE_CONTENT}")
221+
string(TOUPPER "${MATCH_RESULT}" LANGUAGE_NAME)
222+
string(REPLACE "+" "X" LANGUAGE_NAME "${LANGUAGE_NAME}")
223+
set(${OUTPUT_VARIABLE} ${LANGUAGE_NAME} PARENT_SCOPE)
224+
endfunction()

tests/test_package.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,59 @@ def test_output_argument(monkeypatch, tmp_path, output_arg):
7171
build_files = {x.name for x in build_dir.iterdir()}
7272
assert f"{generated_file}.dep" in build_files
7373
assert generated_file in build_files
74+
75+
76+
def test_implicit_cxx(monkeypatch, tmp_path):
77+
package_dir = tmp_path / "pkg3"
78+
shutil.copytree(DIR / "packages/simple", package_dir)
79+
monkeypatch.chdir(package_dir)
80+
81+
cmakelists = Path("CMakeLists.txt")
82+
txt = (
83+
cmakelists.read_text()
84+
.replace("LANGUAGE C", "")
85+
.replace("LANGUAGES C", "LANGUAGES CXX")
86+
)
87+
cmakelists.write_text(txt)
88+
89+
wheel = build_wheel(
90+
str(tmp_path), {"build-dir": "build", "wheel.license-files": []}
91+
)
92+
93+
with zipfile.ZipFile(tmp_path / wheel) as f:
94+
file_names = set(f.namelist())
95+
assert len(file_names) == 4
96+
97+
build_files = {x.name for x in Path("build").iterdir()}
98+
assert "simple.cxx.dep" in build_files
99+
assert "simple.cxx" in build_files
100+
101+
102+
def test_directive_cxx(monkeypatch, tmp_path):
103+
package_dir = tmp_path / "pkg4"
104+
shutil.copytree(DIR / "packages/simple", package_dir)
105+
monkeypatch.chdir(package_dir)
106+
107+
cmakelists = Path("CMakeLists.txt")
108+
txt = (
109+
cmakelists.read_text()
110+
.replace("LANGUAGE C", "")
111+
.replace("LANGUAGES C", "LANGUAGES CXX")
112+
)
113+
cmakelists.write_text(txt)
114+
115+
simple = Path("simple.pyx")
116+
txt = simple.read_text()
117+
simple.write_text(f"# distutils: language=c++\n{txt}")
118+
119+
wheel = build_wheel(
120+
str(tmp_path), {"build-dir": "build", "wheel.license-files": []}
121+
)
122+
123+
with zipfile.ZipFile(tmp_path / wheel) as f:
124+
file_names = set(f.namelist())
125+
assert len(file_names) == 4
126+
127+
build_files = {x.name for x in Path("build").iterdir()}
128+
assert "simple.cxx.dep" in build_files
129+
assert "simple.cxx" in build_files

0 commit comments

Comments
 (0)