aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2021-08-24 09:20:03 -0700
committerJussi Pakkanen <jpakkane@gmail.com>2021-09-24 22:56:46 +0300
commit68c23a61203fc35dd11c7a0b1cc13f7cc2c5cf8c (patch)
tree5afe315e41b55e8b12f3b1b56d75bfd766620cae
parent524a95fa62a6e0cb77e53d7b38d5c2d59a75e449 (diff)
downloadmeson-68c23a61203fc35dd11c7a0b1cc13f7cc2c5cf8c.zip
meson-68c23a61203fc35dd11c7a0b1cc13f7cc2c5cf8c.tar.gz
meson-68c23a61203fc35dd11c7a0b1cc13f7cc2c5cf8c.tar.bz2
Add option to to transpile Cython to C++
This patch adds a new meson built-in option for cython, allowing it to target C++ instead of C as the intermediate language. This can, of course, be done on a per-target basis using the `override_options` keyword argument, or for the entire project in the project function. There are some things in this patch that are less than ideal. One of them is that we have to add compilers in the build layer, but there isn't a better place to do it because of per target override_options. There's also some design differences between Meson and setuptools, in that Meson only allows options on a per-target rather than a per-file granularity. Fixes #9015
-rw-r--r--docs/markdown/Cython.md29
-rw-r--r--docs/markdown/snippets/cython-c++-intermediate.md22
-rw-r--r--mesonbuild/backend/ninjabackend.py6
-rw-r--r--mesonbuild/build.py36
-rw-r--r--mesonbuild/compilers/cython.py8
-rw-r--r--mesonbuild/interpreter/interpreter.py11
-rw-r--r--test cases/cython/1 basic/libdir/storer.h8
-rw-r--r--test cases/cython/1 basic/test.json10
8 files changed, 123 insertions, 7 deletions
diff --git a/docs/markdown/Cython.md b/docs/markdown/Cython.md
index 1d30c1f..3042750 100644
--- a/docs/markdown/Cython.md
+++ b/docs/markdown/Cython.md
@@ -31,3 +31,32 @@ py.extension_module(
dependencies : dep_py,
)
```
+
+## C++ intermediate support
+
+*(New in 0.60.0)*
+
+An option has been added to control this, called `cython_language`. This can be
+either `'c'` or `'cpp'`.
+
+For those coming from setuptools/distutils, they will find two things. First,
+meson ignores `# distutils: language = c++` inline directives. Second that Meson
+allows options only on a per-target granularity. This means that if you need to mix
+cython files being transpiled to C and to C++ you need two targets:
+
+```meson
+project('my project', 'cython')
+
+cython_cpp_lib = static_library(
+ 'helper_lib',
+ 'foo_cpp.pyx', # will be transpiled to C++
+ override_options : ['cython_language=cpp'],
+)
+
+py.extension_module(
+ 'foo',
+ 'foo.pyx', # will be transpiled to C
+ link_with : [cython_cpp_lib],
+ dependencies : dep_py,
+)
+```
diff --git a/docs/markdown/snippets/cython-c++-intermediate.md b/docs/markdown/snippets/cython-c++-intermediate.md
new file mode 100644
index 0000000..0a1c35f
--- /dev/null
+++ b/docs/markdown/snippets/cython-c++-intermediate.md
@@ -0,0 +1,22 @@
+## Cython can now transpile to C++ as an intermediate language
+
+Built-in cython support currently only allows C as an intermediate language, now
+C++ is also allowed. This can be set via the `cython_language` option, either on
+the command line, or in the meson.build files.
+
+```meson
+project(
+ 'myproject',
+ 'cython',
+ default_options : ['cython_language=cpp'],
+)
+```
+
+or on a per target basis with:
+```meson
+python.extension_module(
+ 'mod',
+ 'mod.pyx',
+ override_options : ['cython_language=cpp'],
+)
+```
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 844c612..7c97ca3 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1598,9 +1598,11 @@ class NinjaBackend(backends.Backend):
args += self.build.get_global_args(cython, target.for_machine)
args += self.build.get_project_args(cython, target.subproject, target.for_machine)
+ ext = opt_proxy[OptionKey('language', machine=target.for_machine, lang='cython')].value
+
for src in target.get_sources():
if src.endswith('.pyx'):
- output = os.path.join(self.get_target_private_dir(target), f'{src}.c')
+ output = os.path.join(self.get_target_private_dir(target), f'{src}.{ext}')
args = args.copy()
args += cython.get_output_args(output)
element = NinjaBuildElement(
@@ -1622,7 +1624,7 @@ class NinjaBackend(backends.Backend):
ssrc = os.path.join(gen.get_subdir(), ssrc)
if ssrc.endswith('.pyx'):
args = args.copy()
- output = os.path.join(self.get_target_private_dir(target), f'{ssrc}.c')
+ output = os.path.join(self.get_target_private_dir(target), f'{ssrc}.{ext}')
args += cython.get_output_args(output)
element = NinjaBuildElement(
self.all_outputs, [output],
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index b5fa8ea..1498415 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -37,7 +37,7 @@ from .mesonlib import (
)
from .compilers import (
Compiler, is_object, clink_langs, sort_clink, lang_suffixes,
- is_known_suffix, detect_static_linker
+ is_known_suffix, detect_static_linker, detect_compiler_for
)
from .linkers import StaticLinker
from .interpreterbase import FeatureNew
@@ -833,8 +833,40 @@ class BuildTarget(Target):
# If all our sources are Vala, our target also needs the C compiler but
# it won't get added above.
- if ('vala' in self.compilers or 'cython' in self.compilers) and 'c' not in self.compilers:
+ if 'vala' in self.compilers and 'c' not in self.compilers:
self.compilers['c'] = compilers['c']
+ if 'cython' in self.compilers:
+ key = OptionKey('language', machine=self.for_machine, lang='cython')
+ if key in self.option_overrides_compiler:
+ value = self.option_overrides_compiler[key]
+ else:
+ value = self.environment.coredata.options[key].value
+
+ try:
+ self.compilers[value] = compilers[value]
+ except KeyError:
+ # TODO: it would be nice to not have to do this here, but we
+ # have two problems to work around:
+ # 1. If this is set via an override we have no way to know
+ # before now that we need a compiler for the non-default language
+ # 2. Because Cython itself initializes the `cython_language`
+ # option, we have no good place to insert that you need it
+ # before now, so we just have to do it here.
+ comp = detect_compiler_for(self.environment, value, self.for_machine)
+
+ # This is copied verbatim from the interpreter
+ if self.for_machine == MachineChoice.HOST or self.environment.is_cross_build():
+ logger_fun = mlog.log
+ else:
+ logger_fun = mlog.debug
+ logger_fun(comp.get_display_language(), 'compiler for the', self.for_machine.get_lower_case_name(), 'machine:',
+ mlog.bold(' '.join(comp.get_exelist())), comp.get_version_string())
+ if comp.linker is not None:
+ logger_fun(comp.get_display_language(), 'linker for the', self.for_machine.get_lower_case_name(), 'machine:',
+ mlog.bold(' '.join(comp.linker.get_exelist())), comp.linker.id, comp.linker.version)
+ if comp is None:
+ raise MesonException(f'Cannot find required compiler {value}')
+ self.compilers[value] = comp
def validate_sources(self):
if not self.sources:
diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py
index 513f079..34ddff1 100644
--- a/mesonbuild/compilers/cython.py
+++ b/mesonbuild/compilers/cython.py
@@ -68,6 +68,11 @@ class CythonCompiler(Compiler):
'Python version to target',
['2', '3'],
'3',
+ ),
+ OptionKey('language', machine=self.for_machine, lang=self.language): coredata.UserComboOption(
+ 'Output C or C++ files',
+ ['c', 'cpp'],
+ 'c',
)
})
return opts
@@ -76,4 +81,7 @@ class CythonCompiler(Compiler):
args: T.List[str] = []
key = options[OptionKey('version', machine=self.for_machine, lang=self.language)]
args.append(f'-{key.value}')
+ lang = options[OptionKey('language', machine=self.for_machine, lang=self.language)]
+ if lang.value == 'cpp':
+ args.append('--cplus')
return args
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index 4d4e5da..2d04d2d 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -1241,9 +1241,14 @@ external dependencies (including libraries) must go to "dependencies".''')
args = [a.lower() for a in args]
langs = set(self.coredata.compilers[for_machine].keys())
langs.update(args)
- if ('vala' in langs or 'cython' in langs) and 'c' not in langs:
- if 'vala' in langs:
- FeatureNew.single_use('Adding Vala language without C', '0.59.0', self.subproject)
+ # We'd really like to add cython's default language here, but it can't
+ # actually be done because the cython compiler hasn't been initialized,
+ # so we can't actually get the option yet. Because we can't know what
+ # compiler to add by default, and we don't want to add unnecessary
+ # compilers we don't add anything for cython here, and instead do it
+ # When the first cython target using a particular language is used.
+ if 'vala' in langs and 'c' not in langs:
+ FeatureNew.single_use('Adding Vala language without C', '0.59.0', self.subproject)
args.append('c')
success = True
diff --git a/test cases/cython/1 basic/libdir/storer.h b/test cases/cython/1 basic/libdir/storer.h
index 4f71917..6f5bc6f 100644
--- a/test cases/cython/1 basic/libdir/storer.h
+++ b/test cases/cython/1 basic/libdir/storer.h
@@ -1,8 +1,16 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
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);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/test cases/cython/1 basic/test.json b/test cases/cython/1 basic/test.json
new file mode 100644
index 0000000..ed7ac2b
--- /dev/null
+++ b/test cases/cython/1 basic/test.json
@@ -0,0 +1,10 @@
+{
+ "matrix": {
+ "options": {
+ "cython_language": [
+ { "val": "c" },
+ { "val": "cpp" }
+ ]
+ }
+ }
+}