diff options
author | Evgeni Burovski <evgeny.burovskiy@gmail.com> | 2022-01-28 14:50:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-28 12:50:32 +0100 |
commit | cdad82d01479408701d6f29c6a8e271cb5169b36 (patch) | |
tree | c5b361431c994c10525d8788eba246508f3167c9 | |
parent | e30913d738d5d0d04379396ce98587f75b2bab1b (diff) | |
download | cython-cdad82d01479408701d6f29c6a8e271cb5169b36.tar.gz |
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
-rw-r--r-- | Cython/Build/Cythonize.py | 2 | ||||
-rw-r--r-- | Cython/Build/Dependencies.py | 24 | ||||
-rw-r--r-- | tests/build/depfile.srctree | 33 | ||||
-rw-r--r-- | tests/build/depfile_numpy.srctree | 49 | ||||
-rw-r--r-- | tests/build/depfile_package.srctree | 58 |
5 files changed, 166 insertions, 0 deletions
diff --git a/Cython/Build/Cythonize.py b/Cython/Build/Cythonize.py index 9de84d5c8..c85b6eaba 100644 --- a/Cython/Build/Cythonize.py +++ b/Cython/Build/Cythonize.py @@ -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: diff --git a/Cython/Build/Dependencies.py b/Cython/Build/Dependencies.py index e1c0ae833..7eb55e260 100644 --- a/Cython/Build/Dependencies.py +++ b/Cython/Build/Dependencies.py @@ -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: diff --git a/tests/build/depfile.srctree b/tests/build/depfile.srctree new file mode 100644 index 000000000..0c2194f75 --- /dev/null +++ b/tests/build/depfile.srctree @@ -0,0 +1,33 @@ +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 diff --git a/tests/build/depfile_numpy.srctree b/tests/build/depfile_numpy.srctree new file mode 100644 index 000000000..4a2461c64 --- /dev/null +++ b/tests/build/depfile_numpy.srctree @@ -0,0 +1,49 @@ +# 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) diff --git a/tests/build/depfile_package.srctree b/tests/build/depfile_package.srctree new file mode 100644 index 000000000..54dcad9f1 --- /dev/null +++ b/tests/build/depfile_package.srctree @@ -0,0 +1,58 @@ +''' +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 + |