Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

distutils.ccompiler: Make has_function work with more C99 compilers #3723

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.d/3723.change.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Updated ``CCompiler.has_function`` to work with more C99 compilers.
Deprecated the ```includes`` and ``include_dirs`` keyword arguments.
39 changes: 35 additions & 4 deletions setuptools/_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("The include argument is deprecated")
if include_dirs is None:
include_dirs = []
else:
warnings.warn("The include_dirs argument is deprecated")
if libraries is None:
libraries = []
if library_dirs is None:
Expand All @@ -845,7 +860,23 @@ 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
16 changes: 16 additions & 0 deletions setuptools/_distutils/tests/test_unixccompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,19 @@ def test_has_function(self):
self.cc.output_dir = 'scratch'
os.chdir(self.mkdtemp())
self.cc.has_function('abort', includes=['stdlib.h'])

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

# Every C implementation should have these.
assert self.cc.has_function('abort')
assert self.cc.has_function('exit')
# abort() is a valid expression with the <stdlib.h> prototype.
assert self.cc.has_function('abort', includes=['<stdlib.h>'])
# But exit() is not valid with the actual prototype in scope.
assert not self.cc.has_function('exit', includes=['<stdlib.h>'])
# And setuptools_does_not_exist is not declared or defined at all.
assert not self.cc.has_function('setuptools_does_not_exist')
assert not self.cc.has_function('setuptools_does_not_exist',
includes=['<stdio.h>'])