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 #195

Merged
merged 1 commit into from
Feb 6, 2023
Merged
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
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')