Commit cdad82d0 authored by Evgeni Burovski's avatar Evgeni Burovski Committed by GitHub

Backport GH-4563 : generate dependency files (GH-4576)

Add a new command line option so that
$ cythonize -M foo.pyx
produces a file `foo.c.dep` with the dependencies of foo.pyx, in addition to `foo.c`.
Try to write relative paths as much as possible.

Backport of https://github.com/cython/cython/pull/4563
parent e30913d7
......@@ -103,6 +103,7 @@ def cython_compile(path_pattern, options):
compile_time_env=options.compile_time_env,
force=options.force,
quiet=options.quiet,
depfile=options.depfile,
**options.options)
if ext_modules and options.build:
......@@ -194,6 +195,7 @@ def parse_args(args):
help='increase Python compatibility by ignoring some compile time errors')
parser.add_option('-k', '--keep-going', dest='keep_going', action='store_true',
help='compile as much as possible, ignore compilation failures')
parser.add_option('-M', '--depfile', action='store_true', help='produce depfiles for the sources')
options, args = parser.parse_args(args)
if not args:
......
......@@ -944,6 +944,8 @@ def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False,
:param compiler_directives: Allow to set compiler directives in the ``setup.py`` like this:
``compiler_directives={'embedsignature': True}``.
See :ref:`compiler-directives`.
:param depfile: produce depfiles for the sources if True.
"""
if exclude is None:
exclude = []
......@@ -952,6 +954,8 @@ def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False,
if 'common_utility_include_dir' in options:
safe_makedirs(options['common_utility_include_dir'])
depfile = options.pop('depfile', None)
if pythran is None:
pythran_options = None
else:
......@@ -1023,6 +1027,26 @@ def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False,
dir = os.path.dirname(c_file)
safe_makedirs_once(dir)
# write out the depfile, if requested
if depfile:
dependencies = deps.all_dependencies(source)
src_base_dir, _ = os.path.split(source)
if not src_base_dir.endswith(os.sep):
src_base_dir += os.sep
# paths below the base_dir are relative, otherwise absolute
paths = []
for fname in dependencies:
if (fname.startswith(src_base_dir) or
fname.startswith('.' + os.path.sep)):
paths.append(os.path.relpath(fname, src_base_dir))
else:
paths.append(os.path.abspath(fname))
depline = os.path.split(c_file)[1] + ": \\\n "
depline += " \\\n ".join(paths) + "\n"
with open(c_file+'.dep', 'w') as outfile:
outfile.write(depline)
if os.path.exists(c_file):
c_timestamp = os.path.getmtime(c_file)
else:
......
PYTHON -m Cython.Build.Cythonize -M foo.pyx
PYTHON check.py
######## foo.pyx ########
from bar cimport empty
include "baz.pxi"
empty()
print(foo())
######## baz.pxi ########
def foo():
return "foo"
######## bar.pxd ########
cdef inline void empty():
print("empty")
######## check.py ########
import os
with open("foo.c.dep", "r") as f:
contents = f.read().replace("\n", " ").replace("\\", "")
assert sorted(contents.split()) == ['bar.pxd', 'baz.pxi', 'foo.c:', 'foo.pyx'], contents
# tag: numpy
PYTHON -m Cython.Build.Cythonize -M dep_np.pyx
PYTHON check_np.py
######## dep_np.pyx ########
cimport numpy as np
np.import_array()
######## check_np.py ########
import os
import re
import numpy as np
import Cython
with open("dep_np.c.dep", "r") as f:
contents = f.read().replace("\n", " ").replace("\\", "")
contents = contents.split()
cy_prefix, _ = os.path.split(Cython.__file__)
contents = [fname.replace(cy_prefix, "cy_prefix") for fname in contents]
np_prefix, _ = os.path.split(np.__file__)
contents = [fname.replace(np_prefix, "np_prefix") for fname in contents]
expected = ['cy_prefix/Includes/cpython/buffer.pxd',
'cy_prefix/Includes/cpython/mem.pxd',
'cy_prefix/Includes/cpython/object.pxd',
'cy_prefix/Includes/cpython/ref.pxd',
'cy_prefix/Includes/cpython/type.pxd',
'cy_prefix/Includes/libc/stdio.pxd',
'cy_prefix/Includes/libc/string.pxd',
'dep_np.c:',
'dep_np.pyx',]
# Also account for legacy numpy versions, which do not ship
# `__init__.pxd` hence the fallback is used:
if 'cy_prefix/Includes/numpy/__init__.pxd' in contents:
expected.append('cy_prefix/Includes/numpy/__init__.pxd')
else:
expected.append('np_prefix/__init__.pxd')
assert sorted(contents) == sorted(expected), sorted(contents)
'''
PYTHON -m Cython.Build.Cythonize -i pkg --depfile
PYTHON package_test.py
'''
######## package_test.py ########
import os
with open("pkg/test.c.dep", "r") as f:
contents = f.read().replace("\n", " ").replace("\\", "")
assert sorted(contents.split()) == sorted(['test.c:', 'sub/incl.pxi', 'test.pxd', 'test.pyx']), contents
with open("pkg/sub/test.c.dep", "r") as f:
contents = f.read().replace("\n", " ").replace("\\", "")
contents = [os.path.relpath(entry, '.')
if os.path.isabs(entry) else entry for entry in contents.split()]
assert sorted(contents) == sorted(['test.c:', 'incl.pxi', 'test.pyx', '../test.pxd']), contents
######## pkg/__init__.py ########
######## pkg/test.pyx ########
TEST = "pkg.test"
include "sub/incl.pxi"
cdef object get_str():
return TEST
######## pkg/test.pxd ########
cdef object get_str()
######## pkg/sub/__init__.py ########
######## pkg/sub/test.pyx ########
# cython: language_level=3
from ..test cimport get_str
include 'incl.pxi'
TEST = 'pkg.sub.test'
######## pkg/sub/incl.pxi ########
pass
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment