Skip to content

Commit

Permalink
Merge pull request #195 from fweimer-rh/c99
Browse files Browse the repository at this point in the history
distutils.ccompiler: Make has_function work with more C99 compilers
  • Loading branch information
jaraco authored Feb 6, 2023
2 parents 3944f4e + 56a5b33 commit 8bf0435
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 5 deletions.
40 changes: 36 additions & 4 deletions distutils/ccompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import sys
import os
import re
import warnings

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

if includes is None:
includes = []
else:
warnings.warn("includes is deprecated", DeprecationWarning)
if include_dirs is None:
include_dirs = []
else:
warnings.warn("include_dirs is deprecated", DeprecationWarning)
if libraries is None:
libraries = []
if library_dirs is None:
Expand All @@ -845,7 +860,24 @@ def has_function( # noqa: C901
f = os.fdopen(fd, "w")
try:
for incl in includes:
f.write("""#include "%s"\n""" % incl)
f.write("""#include %s\n""" % incl)
if not includes:
# Use "char func(void);" as the prototype to follow
# what autoconf does. This prototype does not match
# any well-known function the compiler might recognize
# as a builtin, so this ends up as a true link test.
# Without a fake prototype, the test would need to
# know the exact argument types, and the has_function
# interface does not provide that level of information.
f.write(
"""\
#ifdef __cplusplus
extern "C"
#endif
char %s(void);
"""
% funcname
)
f.write(
"""\
int main (int argc, char **argv) {
Expand Down
23 changes: 23 additions & 0 deletions distutils/tests/test_ccompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,26 @@ def test_set_include_dirs(c_file):
# do it again, setting include dirs after any initialization
compiler.set_include_dirs([python])
compiler.compile(_make_strs([c_file]))


def test_has_function_prototype():
# Issue https://github.com/pypa/setuptools/issues/3648
# Test prototype-generating behavior.

compiler = ccompiler.new_compiler()

# Every C implementation should have these.
assert compiler.has_function('abort')
assert compiler.has_function('exit')
with pytest.deprecated_call(match='includes is deprecated'):
# abort() is a valid expression with the <stdlib.h> prototype.
assert compiler.has_function('abort', includes=['<stdlib.h>'])
with pytest.deprecated_call(match='includes is deprecated'):
# But exit() is not valid with the actual prototype in scope.
assert not compiler.has_function('exit', includes=['<stdlib.h>'])
# And setuptools_does_not_exist is not declared or defined at all.
assert not compiler.has_function('setuptools_does_not_exist')
with pytest.deprecated_call(match='includes is deprecated'):
assert not compiler.has_function(
'setuptools_does_not_exist', includes=['<stdio.h>']
)
2 changes: 1 addition & 1 deletion distutils/tests/test_unixccompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,4 @@ def test_has_function(self):
# FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
self.cc.output_dir = 'scratch'
os.chdir(self.mkdtemp())
self.cc.has_function('abort', includes=['stdlib.h'])
self.cc.has_function('abort')

0 comments on commit 8bf0435

Please sign in to comment.