diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3c8bb819b228..a83913d71af7 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1698,6 +1698,8 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ ext = target.get_option(OptionKey('language', machine=target.for_machine, lang='cython')) + pyx_sources = [] # Keep track of sources we're adding to build + for src in target.get_sources(): if src.endswith('.pyx'): output = os.path.join(self.get_target_private_dir(target), f'{src}.{ext}') @@ -1711,9 +1713,11 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ self.add_build(element) # TODO: introspection? cython_sources.append(output) + pyx_sources.append(element) else: static_sources[src.rel_to_builddir(self.build_to_src)] = src + header_deps = [] # Keep track of generated headers for those sources for gen in target.get_generated_sources(): for ssrc in gen.get_outputs(): if isinstance(gen, GeneratedList): @@ -1730,10 +1734,20 @@ def generate_cython_transpile(self, target: build.BuildTarget) -> \ [ssrc]) element.add_item('ARGS', args) self.add_build(element) + pyx_sources.append(element) # TODO: introspection? cython_sources.append(output) else: generated_sources[ssrc] = mesonlib.File.from_built_file(gen.get_subdir(), ssrc) + # Following logic in L883-900 where we determine whether to add generated source + # as a header(order-only) dep to the .so compilation rule + if not self.environment.is_source(ssrc) and \ + not self.environment.is_object(ssrc) and \ + not self.environment.is_library(ssrc) and \ + not modules.is_module_library(ssrc): + header_deps.append(ssrc) + for source in pyx_sources: + source.add_orderdep(header_deps) return static_sources, generated_sources, cython_sources diff --git a/test cases/cython/2 generated sources/includestuff.pyx b/test cases/cython/2 generated sources/includestuff.pyx new file mode 100644 index 000000000000..83f881b33738 --- /dev/null +++ b/test cases/cython/2 generated sources/includestuff.pyx @@ -0,0 +1 @@ +include "stuff.pxi" diff --git a/test cases/cython/2 generated sources/meson.build b/test cases/cython/2 generated sources/meson.build index cfe62602dacd..cffddd38ed6e 100644 --- a/test cases/cython/2 generated sources/meson.build +++ b/test cases/cython/2 generated sources/meson.build @@ -6,6 +6,7 @@ project( py_mod = import('python') py3 = py_mod.find_installation('python3') py3_dep = py3.dependency(required : false) +fs = import('fs') if not py3_dep.found() error('MESON_SKIP_TEST: Python library not found.') endif @@ -70,6 +71,25 @@ test( env : ['PYTHONPATH=' + meson.current_build_dir()] ) +stuff_pxi = fs.copyfile( + 'stuff.pxi.in', + 'stuff.pxi' +) + +# Need to copy the cython source to the build directory +# since meson can only generate the .pxi there +includestuff_pyx = fs.copyfile( + 'includestuff.pyx' +) + +stuff_pxi_dep = declare_dependency(sources: stuff_pxi) + +includestuff_ext = py3.extension_module( + 'includestuff', + includestuff_pyx, + dependencies: stuff_pxi_dep +) + subdir('libdir') test( diff --git a/test cases/cython/2 generated sources/stuff.pxi.in b/test cases/cython/2 generated sources/stuff.pxi.in new file mode 100644 index 000000000000..6417cbc85d92 --- /dev/null +++ b/test cases/cython/2 generated sources/stuff.pxi.in @@ -0,0 +1,2 @@ +def func(): + print("Hello world") diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 8b7859078b2c..351f79de21a0 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -833,6 +833,32 @@ def test_build_by_default(self): self.build(target=('barprog' + exe_suffix)) self.assertPathExists(exe2) + def test_build_generated_pyx_directly(self): + # Check that the transpile stage also includes + # dependencies for the compilation stage as dependencies + testdir = os.path.join("test cases/cython", '2 generated sources') + env = get_fake_env(testdir, self.builddir, self.prefix) + try: + detect_compiler_for(env, "cython", MachineChoice.HOST) + except EnvironmentException: + raise SkipTest("Cython is not installed") + self.init(testdir) + # Need to get the full target name of the pyx.c target + # (which is unfortunately not provided by introspection :( ) + # We'll need to dig into the generated sources + targets = self.introspect('--targets') + name = None + for target in targets: + for target_sources in target["target_sources"]: + for generated_source in target_sources["generated_sources"]: + if "includestuff.pyx.c" in generated_source: + name = generated_source + break + # Split the path (we only want the includestuff.cpython-blahblahblah) + name = os.path.normpath(name).split("/")[-2:] + name = "/".join(name) # Glue list into a string + self.build(target=name) + def test_internal_include_order(self): if mesonbuild.environment.detect_msys2_arch() and ('MESON_RSP_THRESHOLD' in os.environ): raise SkipTest('Test does not yet support gcc rsp files on msys2')