Skip to content

Commit 8bf0435

Browse files
authored
Merge pull request #195 from fweimer-rh/c99
distutils.ccompiler: Make has_function work with more C99 compilers
2 parents 3944f4e + 56a5b33 commit 8bf0435

File tree

3 files changed

+60
-5
lines changed

3 files changed

+60
-5
lines changed

distutils/ccompiler.py

+36-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sys
77
import os
88
import re
9+
import warnings
910

1011
from .errors import (
1112
CompileError,
@@ -824,9 +825,19 @@ def has_function( # noqa: C901
824825
libraries=None,
825826
library_dirs=None,
826827
):
827-
"""Return a boolean indicating whether funcname is supported on
828-
the current platform. The optional arguments can be used to
829-
augment the compilation environment.
828+
"""Return a boolean indicating whether funcname is provided as
829+
a symbol on the current platform. The optional arguments can
830+
be used to augment the compilation environment.
831+
832+
The libraries argument is a list of flags to be passed to the
833+
linker to make additional symbol definitions available for
834+
linking.
835+
836+
The includes and include_dirs arguments are deprecated.
837+
Usually, supplying include files with function declarations
838+
will cause function detection to fail even in cases where the
839+
symbol is available for linking.
840+
830841
"""
831842
# this can't be included at module scope because it tries to
832843
# import math which might not be available at that point - maybe
@@ -835,8 +846,12 @@ def has_function( # noqa: C901
835846

836847
if includes is None:
837848
includes = []
849+
else:
850+
warnings.warn("includes is deprecated", DeprecationWarning)
838851
if include_dirs is None:
839852
include_dirs = []
853+
else:
854+
warnings.warn("include_dirs is deprecated", DeprecationWarning)
840855
if libraries is None:
841856
libraries = []
842857
if library_dirs is None:
@@ -845,7 +860,24 @@ def has_function( # noqa: C901
845860
f = os.fdopen(fd, "w")
846861
try:
847862
for incl in includes:
848-
f.write("""#include "%s"\n""" % incl)
863+
f.write("""#include %s\n""" % incl)
864+
if not includes:
865+
# Use "char func(void);" as the prototype to follow
866+
# what autoconf does. This prototype does not match
867+
# any well-known function the compiler might recognize
868+
# as a builtin, so this ends up as a true link test.
869+
# Without a fake prototype, the test would need to
870+
# know the exact argument types, and the has_function
871+
# interface does not provide that level of information.
872+
f.write(
873+
"""\
874+
#ifdef __cplusplus
875+
extern "C"
876+
#endif
877+
char %s(void);
878+
"""
879+
% funcname
880+
)
849881
f.write(
850882
"""\
851883
int main (int argc, char **argv) {

distutils/tests/test_ccompiler.py

+23
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,26 @@ def test_set_include_dirs(c_file):
5353
# do it again, setting include dirs after any initialization
5454
compiler.set_include_dirs([python])
5555
compiler.compile(_make_strs([c_file]))
56+
57+
58+
def test_has_function_prototype():
59+
# Issue https://github.com/pypa/setuptools/issues/3648
60+
# Test prototype-generating behavior.
61+
62+
compiler = ccompiler.new_compiler()
63+
64+
# Every C implementation should have these.
65+
assert compiler.has_function('abort')
66+
assert compiler.has_function('exit')
67+
with pytest.deprecated_call(match='includes is deprecated'):
68+
# abort() is a valid expression with the <stdlib.h> prototype.
69+
assert compiler.has_function('abort', includes=['<stdlib.h>'])
70+
with pytest.deprecated_call(match='includes is deprecated'):
71+
# But exit() is not valid with the actual prototype in scope.
72+
assert not compiler.has_function('exit', includes=['<stdlib.h>'])
73+
# And setuptools_does_not_exist is not declared or defined at all.
74+
assert not compiler.has_function('setuptools_does_not_exist')
75+
with pytest.deprecated_call(match='includes is deprecated'):
76+
assert not compiler.has_function(
77+
'setuptools_does_not_exist', includes=['<stdio.h>']
78+
)

distutils/tests/test_unixccompiler.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,4 @@ def test_has_function(self):
303303
# FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
304304
self.cc.output_dir = 'scratch'
305305
os.chdir(self.mkdtemp())
306-
self.cc.has_function('abort', includes=['stdlib.h'])
306+
self.cc.has_function('abort')

0 commit comments

Comments
 (0)