Merge pull request #8706 from dcbaker/wip/2021-04/cython-language

1st class Cython language support
pull/8844/head
Jussi Pakkanen 4 years ago committed by GitHub
commit 40e8a67a83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      docs/markdown/Cython.md
  2. 23
      docs/markdown/Reference-tables.md
  3. 18
      docs/markdown/snippets/first-class-cython.md
  4. 1
      docs/sitemap.txt
  5. 77
      mesonbuild/backend/ninjabackend.py
  6. 32
      mesonbuild/build.py
  7. 2
      mesonbuild/compilers/__init__.py
  8. 2
      mesonbuild/compilers/compilers.py
  9. 79
      mesonbuild/compilers/cython.py
  10. 28
      mesonbuild/environment.py
  11. 5
      mesonbuild/interpreter/interpreter.py
  12. 5
      run_project_tests.py
  13. 19
      test cases/cython/1 basic/cytest.py
  14. 9
      test cases/cython/1 basic/libdir/cstorer.pxd
  15. 8
      test cases/cython/1 basic/libdir/meson.build
  16. 24
      test cases/cython/1 basic/libdir/storer.c
  17. 8
      test cases/cython/1 basic/libdir/storer.h
  18. 16
      test cases/cython/1 basic/libdir/storer.pyx
  19. 20
      test cases/cython/1 basic/meson.build
  20. 2
      test cases/cython/2 generated sources/configure.pyx.in
  21. 2
      test cases/cython/2 generated sources/g.in
  22. 14
      test cases/cython/2 generated sources/gen.py
  23. 12
      test cases/cython/2 generated sources/generator.py
  24. 61
      test cases/cython/2 generated sources/meson.build
  25. 13
      test cases/cython/2 generated sources/test.py

@ -0,0 +1,33 @@
---
title: Cython
short-description: Support for Cython in Meson
...
# Cython
Meson provides native support for cython programs starting with version 0.59.0.
This means that you can include it as a normal language, and create targets like
any other supported language:
```meson
lib = static_library(
'foo',
'foo.pyx',
)
```
Generally Cython is most useful when combined with the python module's
extension_module method:
```meson
project('my project', 'cython')
py = import('python')
dep_py3 = py.dependency()
py.extension_module(
'foo',
'foo.pyx',
dependencies : dep_py,
)
```

@ -34,6 +34,7 @@ These are return values of the `get_id` (Compiler family) and
| sun | Sun Fortran compiler | | | sun | Sun Fortran compiler | |
| valac | Vala compiler | | | valac | Vala compiler | |
| xc16 | Microchip XC16 C compiler | | | xc16 | Microchip XC16 C compiler | |
| cython | The Cython compiler | |
## Linker ids ## Linker ids
@ -160,6 +161,7 @@ These are the parameter names for passing language specific arguments to your bu
| Objective C++ | objcpp_args | objcpp_link_args | | Objective C++ | objcpp_args | objcpp_link_args |
| Rust | rust_args | rust_link_args | | Rust | rust_args | rust_link_args |
| Vala | vala_args | vala_link_args | | Vala | vala_args | vala_link_args |
| Cython | cython_args | cython_link_args |
All these `<lang>_*` options are specified per machine. See in All these `<lang>_*` options are specified per machine. See in
[specifying options per [specifying options per
@ -177,16 +179,17 @@ are many caveats to their use, especially when rebuilding the project.
It is **highly** recommended that you use [the command line It is **highly** recommended that you use [the command line
arguments](#language-arguments-parameter-names) instead. arguments](#language-arguments-parameter-names) instead.
| Name | Comment | | Name | Comment |
| ----- | ------- | | ----- | ------- |
| CFLAGS | Flags for the C compiler | | CFLAGS | Flags for the C compiler |
| CXXFLAGS | Flags for the C++ compiler | | CXXFLAGS | Flags for the C++ compiler |
| OBJCFLAGS | Flags for the Objective C compiler | | OBJCFLAGS | Flags for the Objective C compiler |
| FFLAGS | Flags for the Fortran compiler | | FFLAGS | Flags for the Fortran compiler |
| DFLAGS | Flags for the D compiler | | DFLAGS | Flags for the D compiler |
| VALAFLAGS | Flags for the Vala compiler | | VALAFLAGS | Flags for the Vala compiler |
| RUSTFLAGS | Flags for the Rust compiler | | RUSTFLAGS | Flags for the Rust compiler |
| LDFLAGS | The linker flags, used for all languages | | CYTHONFLAGS | Flags for the Cython compiler |
| LDFLAGS | The linker flags, used for all languages |
N.B. these settings are specified per machine, and so the environment N.B. these settings are specified per machine, and so the environment
varibles actually come in pairs. See the [environment variables per varibles actually come in pairs. See the [environment variables per

@ -0,0 +1,18 @@
## Cython as as first class language
Meson now supports Cython as a first class language. This means you can write:
```meson
project('my project', 'cython')
py = import('python')
dep_py3 = py.dependency()
py.extension_module(
'foo',
'foo.pyx',
dependencies : dep_py,
)
```
And avoid the step through a generator that was previously required.

@ -59,6 +59,7 @@ index.md
Java.md Java.md
Vala.md Vala.md
D.md D.md
Cython.md
IDE-integration.md IDE-integration.md
Custom-build-targets.md Custom-build-targets.md
Build-system-converters.md Build-system-converters.md

@ -749,6 +749,9 @@ int dummy;
# C/C++ sources, objects, generated libs, and unknown sources now. # C/C++ sources, objects, generated libs, and unknown sources now.
target_sources, generated_sources, \ target_sources, generated_sources, \
transpiled_sources = self.generate_vala_compile(target) transpiled_sources = self.generate_vala_compile(target)
elif 'cython' in target.compilers:
target_sources, generated_sources, \
transpiled_sources = self.generate_cython_transpile(target)
else: else:
target_sources = self.get_target_sources(target) target_sources = self.get_target_sources(target)
generated_sources = self.get_target_generated_sources(target) generated_sources = self.get_target_generated_sources(target)
@ -1544,6 +1547,66 @@ int dummy;
self.create_target_source_introspection(target, valac, args, all_files, []) self.create_target_source_introspection(target, valac, args, all_files, [])
return other_src[0], other_src[1], vala_c_src return other_src[0], other_src[1], vala_c_src
def generate_cython_transpile(self, target: build.BuildTarget) -> \
T.Tuple[T.MutableMapping[str, File], T.MutableMapping[str, File], T.List[str]]:
"""Generate rules for transpiling Cython files to C or C++
XXX: Currently only C is handled.
"""
static_sources: T.MutableMapping[str, File] = OrderedDict()
generated_sources: T.MutableMapping[str, File] = OrderedDict()
cython_sources: T.List[str] = []
cython = target.compilers['cython']
opt_proxy = self.get_compiler_options_for_target(target)
args: T.List[str] = []
args += cython.get_always_args()
args += cython.get_buildtype_args(self.get_option_for_target(OptionKey('buildtype'), target))
args += cython.get_debug_args(self.get_option_for_target(OptionKey('debug'), target))
args += cython.get_optimization_args(self.get_option_for_target(OptionKey('optimization'), target))
args += cython.get_option_compile_args(opt_proxy)
args += self.build.get_global_args(cython, target.for_machine)
args += self.build.get_project_args(cython, target.subproject, target.for_machine)
for src in target.get_sources():
if src.endswith('.pyx'):
output = os.path.join(self.get_target_private_dir(target), f'{src}.c')
args = args.copy()
args += cython.get_output_args(output)
element = NinjaBuildElement(
self.all_outputs, [output],
self.compiler_to_rule_name(cython),
[src.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())])
element.add_item('ARGS', args)
self.add_build(element)
# TODO: introspection?
cython_sources.append(output)
else:
static_sources[src.rel_to_builddir(self.build_to_src)] = src
for gen in target.get_generated_sources():
for ssrc in gen.get_outputs():
if isinstance(gen, GeneratedList):
ssrc = os.path.join(self.get_target_private_dir(target) , ssrc)
if ssrc.endswith('.pyx'):
args = args.copy()
output = os.path.join(self.get_target_private_dir(target), f'{ssrc}.c')
args += cython.get_output_args(output)
element = NinjaBuildElement(
self.all_outputs, [output],
self.compiler_to_rule_name(cython),
[ssrc])
element.add_item('ARGS', args)
self.add_build(element)
# TODO: introspection?
cython_sources.append(output)
else:
generated_sources[ssrc] = mesonlib.File.from_built_file(gen.subdir, ssrc)
return static_sources, generated_sources, cython_sources
def generate_rust_target(self, target: build.BuildTarget) -> None: def generate_rust_target(self, target: build.BuildTarget) -> None:
rustc = target.compilers['rust'] rustc = target.compilers['rust']
# Rust compiler takes only the main file as input and # Rust compiler takes only the main file as input and
@ -1889,10 +1952,7 @@ int dummy;
for for_machine in MachineChoice: for for_machine in MachineChoice:
complist = self.environment.coredata.compilers[for_machine] complist = self.environment.coredata.compilers[for_machine]
for langname, compiler in complist.items(): for langname, compiler in complist.items():
if langname == 'java' \ if langname in {'java', 'vala', 'rust', 'cs', 'cython'}:
or langname == 'vala' \
or langname == 'rust' \
or langname == 'cs':
continue continue
rule = '{}_LINKER{}'.format(langname, self.get_rule_suffix(for_machine)) rule = '{}_LINKER{}'.format(langname, self.get_rule_suffix(for_machine))
command = compiler.get_linker_exelist() command = compiler.get_linker_exelist()
@ -1940,6 +2000,12 @@ int dummy;
description = 'Compiling Vala source $in' description = 'Compiling Vala source $in'
self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1')) self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1'))
def generate_cython_compile_rules(self, compiler: 'Compiler') -> None:
rule = self.compiler_to_rule_name(compiler)
command = compiler.get_exelist() + ['$ARGS', '$in']
description = 'Compiling Cython source $in'
self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1'))
def generate_rust_compile_rules(self, compiler): def generate_rust_compile_rules(self, compiler):
rule = self.compiler_to_rule_name(compiler) rule = self.compiler_to_rule_name(compiler)
command = compiler.get_exelist() + ['$ARGS', '$in'] command = compiler.get_exelist() + ['$ARGS', '$in']
@ -2012,6 +2078,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
if self.environment.machines.matches_build_machine(compiler.for_machine): if self.environment.machines.matches_build_machine(compiler.for_machine):
self.generate_swift_compile_rules(compiler) self.generate_swift_compile_rules(compiler)
return return
if langname == 'cython':
self.generate_cython_compile_rules(compiler)
return
crstr = self.get_rule_suffix(compiler.for_machine) crstr = self.get_rule_suffix(compiler.for_machine)
if langname == 'fortran': if langname == 'fortran':
self.generate_fortran_dep_hack(crstr) self.generate_fortran_dep_hack(crstr)

@ -41,9 +41,10 @@ from .interpreterbase import FeatureNew
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ._typing import ImmutableListProtocol, ImmutableSetProtocol from ._typing import ImmutableListProtocol, ImmutableSetProtocol
from .interpreter.interpreter import Test, SourceOutputs from .interpreter.interpreter import Test, SourceOutputs, Interpreter
from .mesonlib import FileMode, FileOrString from .mesonlib import FileMode, FileOrString
from .backend.backends import Backend from .backend.backends import Backend
from .interpreter.interpreterobjects import GeneratorHolder
pch_kwargs = {'c_pch', 'cpp_pch'} pch_kwargs = {'c_pch', 'cpp_pch'}
@ -63,6 +64,7 @@ lang_arg_kwargs = {
'rust_args', 'rust_args',
'vala_args', 'vala_args',
'cs_args', 'cs_args',
'cython_args',
} }
vala_kwargs = {'vala_header', 'vala_gir', 'vala_vapi'} vala_kwargs = {'vala_header', 'vala_gir', 'vala_vapi'}
@ -810,7 +812,7 @@ class BuildTarget(Target):
# If all our sources are Vala, our target also needs the C compiler but # If all our sources are Vala, our target also needs the C compiler but
# it won't get added above. # it won't get added above.
if 'vala' in self.compilers and 'c' not in self.compilers: if ('vala' in self.compilers or 'cython' in self.compilers) and 'c' not in self.compilers:
self.compilers['c'] = compilers['c'] self.compilers['c'] = compilers['c']
def validate_sources(self): def validate_sources(self):
@ -1564,7 +1566,7 @@ class Generator:
raise InvalidArguments('Depends entries must be build targets.') raise InvalidArguments('Depends entries must be build targets.')
self.depends.append(d) self.depends.append(d)
def get_base_outnames(self, inname): def get_base_outnames(self, inname) -> T.List[str]:
plainname = os.path.basename(inname) plainname = os.path.basename(inname)
basename = os.path.splitext(plainname)[0] basename = os.path.splitext(plainname)[0]
bases = [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs] bases = [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs]
@ -1586,7 +1588,7 @@ class Generator:
relpath = pathlib.PurePath(trial).relative_to(parent) relpath = pathlib.PurePath(trial).relative_to(parent)
return relpath.parts[0] != '..' # For subdirs we can only go "down". return relpath.parts[0] != '..' # For subdirs we can only go "down".
def process_files(self, name, files, state, preserve_path_from=None, extra_args=None): def process_files(self, name, files, state: 'Interpreter', preserve_path_from=None, extra_args=None):
new = False new = False
output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else []) output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else [])
#XXX #XXX
@ -1621,14 +1623,14 @@ class Generator:
class GeneratedList: class GeneratedList:
def __init__(self, generator, subdir, preserve_path_from=None, extra_args=None): def __init__(self, generator: 'GeneratorHolder', subdir: str, preserve_path_from=None, extra_args=None):
self.generator = unholder(generator) self.generator = unholder(generator)
self.name = self.generator.exe self.name = self.generator.exe
self.depends = set() # Things this target depends on (because e.g. a custom target was used as input) self.depends = set() # Things this target depends on (because e.g. a custom target was used as input)
self.subdir = subdir self.subdir = subdir
self.infilelist = [] self.infilelist: T.List['File'] = []
self.outfilelist = [] self.outfilelist: T.List[str] = []
self.outmap = {} self.outmap: T.Dict['File', str] = {}
self.extra_depends = [] self.extra_depends = []
self.depend_files = [] self.depend_files = []
self.preserve_path_from = preserve_path_from self.preserve_path_from = preserve_path_from
@ -1642,17 +1644,17 @@ class GeneratedList:
# know the absolute path of # know the absolute path of
self.depend_files.append(File.from_absolute_file(path)) self.depend_files.append(File.from_absolute_file(path))
def add_preserved_path_segment(self, infile, outfiles, state): def add_preserved_path_segment(self, infile: 'File', outfiles: T.List[str], state: 'Interpreter') -> T.List[str]:
result = [] result: T.List[str] = []
in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir) in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir)
assert(os.path.isabs(self.preserve_path_from)) assert os.path.isabs(self.preserve_path_from)
rel = os.path.relpath(in_abs, self.preserve_path_from) rel = os.path.relpath(in_abs, self.preserve_path_from)
path_segment = os.path.dirname(rel) path_segment = os.path.dirname(rel)
for of in outfiles: for of in outfiles:
result.append(os.path.join(path_segment, of)) result.append(os.path.join(path_segment, of))
return result return result
def add_file(self, newfile, state): def add_file(self, newfile: 'File', state: 'Interpreter') -> None:
self.infilelist.append(newfile) self.infilelist.append(newfile)
outfiles = self.generator.get_base_outnames(newfile.fname) outfiles = self.generator.get_base_outnames(newfile.fname)
if self.preserve_path_from: if self.preserve_path_from:
@ -1660,16 +1662,16 @@ class GeneratedList:
self.outfilelist += outfiles self.outfilelist += outfiles
self.outmap[newfile] = outfiles self.outmap[newfile] = outfiles
def get_inputs(self): def get_inputs(self) -> T.List['File']:
return self.infilelist return self.infilelist
def get_outputs(self) -> T.List[str]: def get_outputs(self) -> T.List[str]:
return self.outfilelist return self.outfilelist
def get_outputs_for(self, filename): def get_outputs_for(self, filename: 'File') -> T.List[str]:
return self.outmap[filename] return self.outmap[filename]
def get_generator(self): def get_generator(self) -> 'Generator':
return self.generator return self.generator
def get_extra_args(self): def get_extra_args(self):

@ -107,6 +107,7 @@ __all__ = [
'VisualStudioCCompiler', 'VisualStudioCCompiler',
'VisualStudioCPPCompiler', 'VisualStudioCPPCompiler',
'CLikeCompiler', 'CLikeCompiler',
'CythonCompiler',
] ]
# Bring symbols from each module into compilers sub-package namespace # Bring symbols from each module into compilers sub-package namespace
@ -213,3 +214,4 @@ from .mixins.gnu import GnuCompiler, GnuLikeCompiler
from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler
from .mixins.clang import ClangCompiler from .mixins.clang import ClangCompiler
from .mixins.clike import CLikeCompiler from .mixins.clike import CLikeCompiler
from .cython import CythonCompiler

@ -64,6 +64,7 @@ lang_suffixes = {
'cs': ('cs',), 'cs': ('cs',),
'swift': ('swift',), 'swift': ('swift',),
'java': ('java',), 'java': ('java',),
'cython': ('pyx', ),
} # type: T.Dict[str, T.Tuple[str, ...]] } # type: T.Dict[str, T.Tuple[str, ...]]
all_languages = lang_suffixes.keys() all_languages = lang_suffixes.keys()
cpp_suffixes = lang_suffixes['cpp'] + ('h',) # type: T.Tuple[str, ...] cpp_suffixes = lang_suffixes['cpp'] + ('h',) # type: T.Tuple[str, ...]
@ -97,6 +98,7 @@ CFLAGS_MAPPING: T.Mapping[str, str] = {
'd': 'DFLAGS', 'd': 'DFLAGS',
'vala': 'VALAFLAGS', 'vala': 'VALAFLAGS',
'rust': 'RUSTFLAGS', 'rust': 'RUSTFLAGS',
'cython': 'CYTHONFLAGS',
} }
CEXE_MAPPING: T.Mapping = { CEXE_MAPPING: T.Mapping = {

@ -0,0 +1,79 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright © 2021 Intel Corporation
"""Abstraction for Cython language compilers."""
import typing as T
from .. import coredata
from ..mesonlib import EnvironmentException, OptionKey
from .compilers import Compiler
if T.TYPE_CHECKING:
from ..coredata import KeyedOptionDictType
from ..environment import Environment
class CythonCompiler(Compiler):
"""Cython Compiler."""
language = 'cython'
id = 'cython'
def needs_static_linker(self) -> bool:
# We transpile into C, so we don't need any linker
return False
def get_always_args(self) -> T.List[str]:
return ['--fast-fail']
def get_werror_args(self) -> T.List[str]:
return ['-Werror']
def get_output_args(self, outputname: str) -> T.List[str]:
return ['-o', outputname]
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
# Cython doesn't have optimization levels itself, the underlying
# compiler might though
return []
def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
code = 'print("hello world")'
with self.cached_compile(code, environment.coredata) as p:
if p.returncode != 0:
raise EnvironmentException(f'Cython compiler {self.id!r} cannot compile programs')
def get_buildtype_args(self, buildtype: str) -> T.List[str]:
# Cython doesn't implement this, but Meson requires an implementation
return []
def get_pic_args(self) -> T.List[str]:
# We can lie here, it's fine
return []
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
build_dir: str) -> T.List[str]:
new: T.List[str] = []
for i in parameter_list:
new.append(i)
return new
def get_options(self) -> 'KeyedOptionDictType':
opts = super().get_options()
opts.update({
OptionKey('version', machine=self.for_machine, lang=self.language): coredata.UserComboOption(
'Python version to target',
['2', '3'],
'3',
)
})
return opts
def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
args: T.List[str] = []
key = options[OptionKey('version', machine=self.for_machine, lang=self.language)]
args.append(f'-{key.value}')
return args

@ -87,6 +87,7 @@ from .compilers import (
ClangObjCPPCompiler, ClangObjCPPCompiler,
ClangClCCompiler, ClangClCCompiler,
ClangClCPPCompiler, ClangClCPPCompiler,
CythonCompiler,
FlangFortranCompiler, FlangFortranCompiler,
G95FortranCompiler, G95FortranCompiler,
GnuCCompiler, GnuCCompiler,
@ -732,6 +733,7 @@ class Environment:
self.default_rust = ['rustc'] self.default_rust = ['rustc']
self.default_swift = ['swiftc'] self.default_swift = ['swiftc']
self.default_vala = ['valac'] self.default_vala = ['valac']
self.default_cython = [['cython']]
self.default_static_linker = ['ar', 'gar'] self.default_static_linker = ['ar', 'gar']
self.default_strip = ['strip'] self.default_strip = ['strip']
self.vs_static_linker = ['lib'] self.vs_static_linker = ['lib']
@ -1757,6 +1759,30 @@ class Environment:
self._handle_exceptions(popen_exceptions, compilers) self._handle_exceptions(popen_exceptions, compilers)
def detect_cython_compiler(self, for_machine: MachineChoice) -> CythonCompiler:
"""Search for a cython compiler."""
compilers = self.lookup_binary_entry(for_machine, 'cython')
is_cross = self.is_cross_build(for_machine)
info = self.machines[for_machine]
if compilers is None:
# TODO support fallback
compilers = [self.default_cython[0]]
popen_exceptions: T.Dict[str, Exception] = {}
for comp in compilers:
try:
err = Popen_safe(comp + ['-V'])[2]
except OSError as e:
popen_exceptions[' '.join(comp + ['-V'])] = e
continue
version = search_version(err)
if 'Cython' in err:
comp_class = CythonCompiler
self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
return comp_class(comp, version, for_machine, info, is_cross=is_cross)
self._handle_exceptions(popen_exceptions, compilers)
def detect_vala_compiler(self, for_machine): def detect_vala_compiler(self, for_machine):
exelist = self.lookup_binary_entry(for_machine, 'vala') exelist = self.lookup_binary_entry(for_machine, 'vala')
is_cross = self.is_cross_build(for_machine) is_cross = self.is_cross_build(for_machine)
@ -2023,6 +2049,8 @@ class Environment:
comp = self.detect_fortran_compiler(for_machine) comp = self.detect_fortran_compiler(for_machine)
elif lang == 'swift': elif lang == 'swift':
comp = self.detect_swift_compiler(for_machine) comp = self.detect_swift_compiler(for_machine)
elif lang == 'cython':
comp = self.detect_cython_compiler(for_machine)
else: else:
comp = None comp = None
return comp return comp

@ -1193,8 +1193,9 @@ external dependencies (including libraries) must go to "dependencies".''')
args = [a.lower() for a in args] args = [a.lower() for a in args]
langs = set(self.coredata.compilers[for_machine].keys()) langs = set(self.coredata.compilers[for_machine].keys())
langs.update(args) langs.update(args)
if 'vala' in langs and 'c' not in langs: if ('vala' in langs or 'cython' in langs) and 'c' not in langs:
FeatureNew('Adding Vala language without C', '0.59.0').use(self.subproject) if 'vala' in langs:
FeatureNew.single_use('Adding Vala language without C', '0.59.0', self.subproject)
args.append('c') args.append('c')
success = True success = True

@ -54,8 +54,8 @@ from run_tests import guess_backend
ALL_TESTS = ['cmake', 'common', 'native', 'warning-meson', 'failing-meson', 'failing-build', 'failing-test', ALL_TESTS = ['cmake', 'common', 'native', 'warning-meson', 'failing-meson', 'failing-build', 'failing-test',
'keyval', 'platform-osx', 'platform-windows', 'platform-linux', 'keyval', 'platform-osx', 'platform-windows', 'platform-linux',
'java', 'C#', 'vala', 'rust', 'd', 'objective c', 'objective c++', 'java', 'C#', 'vala', 'cython', 'rust', 'd', 'objective c', 'objective c++',
'fortran', 'swift', 'cuda', 'python3', 'python', 'fpga', 'frameworks', 'nasm', 'wasm' 'fortran', 'swift', 'cuda', 'python3', 'python', 'fpga', 'frameworks', 'nasm', 'wasm',
] ]
@ -1016,6 +1016,7 @@ def detect_tests_to_run(only: T.Dict[str, T.List[str]], use_tmp: bool) -> T.List
TestCategory('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), TestCategory('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()),
TestCategory('C#', 'csharp', skip_csharp(backend)), TestCategory('C#', 'csharp', skip_csharp(backend)),
TestCategory('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))), TestCategory('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))),
TestCategory('cython', 'cython', backend is not Backend.ninja or not shutil.which(os.environ.get('CYTHON', 'cython'))),
TestCategory('rust', 'rust', should_skip_rust(backend)), TestCategory('rust', 'rust', should_skip_rust(backend)),
TestCategory('d', 'd', backend is not Backend.ninja or not have_d_compiler()), TestCategory('d', 'd', backend is not Backend.ninja or not have_d_compiler()),
TestCategory('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or not have_objc_compiler(options.use_tmpdir)), TestCategory('objective c', 'objc', backend not in (Backend.ninja, Backend.xcode) or not have_objc_compiler(options.use_tmpdir)),

@ -0,0 +1,19 @@
#!/usr/bin/env python3
from storer import Storer
s = Storer()
if s.get_value() != 0:
raise SystemExit('Initial value incorrect.')
s.set_value(42)
if s.get_value() != 42:
raise SystemExit('Setting value failed.')
try:
s.set_value('not a number')
raise SystemExit('Using wrong argument type did not fail.')
except TypeError:
pass

@ -0,0 +1,9 @@
cdef extern from "storer.h":
ctypedef struct Storer:
pass
Storer* storer_new();
void storer_destroy(Storer *s);
int storer_get_value(Storer *s);
void storer_set_value(Storer *s, int v);

@ -0,0 +1,8 @@
slib = py3.extension_module(
'storer',
'storer.pyx',
'storer.c',
dependencies : py3_dep
)
pydir = meson.current_build_dir()

@ -0,0 +1,24 @@
#include"storer.h"
#include<stdlib.h>
struct _Storer {
int value;
};
Storer* storer_new() {
Storer *s = malloc(sizeof(struct _Storer));
s->value = 0;
return s;
}
void storer_destroy(Storer *s) {
free(s);
}
int storer_get_value(Storer *s) {
return s->value;
}
void storer_set_value(Storer *s, int v) {
s->value = v;
}

@ -0,0 +1,8 @@
#pragma once
typedef struct _Storer Storer;
Storer* storer_new();
void storer_destroy(Storer *s);
int storer_get_value(Storer *s);
void storer_set_value(Storer *s, int v);

@ -0,0 +1,16 @@
cimport cstorer
cdef class Storer:
cdef cstorer.Storer* _c_storer
def __cinit__(self):
self._c_storer = cstorer.storer_new()
def __dealloc__(self):
cstorer.storer_destroy(self._c_storer)
cpdef int get_value(self):
return cstorer.storer_get_value(self._c_storer)
cpdef set_value(self, int value):
cstorer.storer_set_value(self._c_storer, value)

@ -0,0 +1,20 @@
project(
'basic cython project',
['cython', 'c'],
default_options : ['warning_level=3']
)
py_mod = import('python')
py3 = py_mod.find_installation()
py3_dep = py3.dependency(required : false)
if not py3_dep.found()
error('MESON_SKIP_TEST: Python library not found.')
endif
subdir('libdir')
test('cython tester',
py3,
args : files('cytest.py'),
env : ['PYTHONPATH=' + pydir]
)

@ -0,0 +1,2 @@
cpdef func():
return "Hello, World!"

@ -0,0 +1,2 @@
cpdef func():
return "Hello, World!"

@ -0,0 +1,14 @@
# SPDX-License-Identifier: Apache-2.0
import argparse
import textwrap
parser = argparse.ArgumentParser()
parser.add_argument('output')
args = parser.parse_args()
with open(args.output, 'w') as f:
f.write(textwrap.dedent('''\
cpdef func():
return "Hello, World!"
'''))

@ -0,0 +1,12 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
args = parser.parse_args()
with open(args.input, 'r') as i, open(args.output, 'w') as o:
o.write(i.read())

@ -0,0 +1,61 @@
project(
'generated cython sources',
['cython'],
)
py_mod = import('python')
py3 = py_mod.find_installation('python3')
py3_dep = py3.dependency(required : false)
if not py3_dep.found()
error('MESON_SKIP_TEST: Python library not found.')
endif
ct = custom_target(
'ct',
input : 'gen.py',
output : 'ct.pyx',
command : [py3, '@INPUT@', '@OUTPUT@'],
)
ct_ext = py3.extension_module('ct', ct, dependencies : py3_dep)
test(
'custom target',
py3,
args : [files('test.py'), 'ct'],
env : ['PYTHONPATH=' + meson.current_build_dir()]
)
cf = configure_file(
input : 'configure.pyx.in',
output : 'cf.pyx',
copy : true,
)
cf_ext = py3.extension_module('cf', cf, dependencies : py3_dep)
test(
'configure file',
py3,
args : [files('test.py'), 'cf'],
env : ['PYTHONPATH=' + meson.current_build_dir()]
)
gen = generator(
find_program('generator.py'),
arguments : ['@INPUT@', '@OUTPUT@'],
output : '@BASENAME@.pyx',
)
g_ext = py3.extension_module(
'g',
gen.process('g.in'),
dependencies : py3_dep,
)
test(
'generator',
py3,
args : [files('test.py'), 'g'],
env : ['PYTHONPATH=' + meson.current_build_dir()]
)

@ -0,0 +1,13 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
import argparse
import importlib
parser = argparse.ArgumentParser()
parser.add_argument('mod')
args = parser.parse_args()
mod = importlib.import_module(args.mod)
assert mod.func() == 'Hello, World!'
Loading…
Cancel
Save