aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/backend/ninjabackend.py77
-rw-r--r--mesonbuild/build.py32
-rw-r--r--mesonbuild/compilers/__init__.py2
-rw-r--r--mesonbuild/compilers/compilers.py2
-rw-r--r--mesonbuild/compilers/cython.py79
-rw-r--r--mesonbuild/environment.py28
-rw-r--r--mesonbuild/interpreter/interpreter.py5
7 files changed, 204 insertions, 21 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index f69f64f..8efb01b 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -749,6 +749,9 @@ int dummy;
# C/C++ sources, objects, generated libs, and unknown sources now.
target_sources, generated_sources, \
transpiled_sources = self.generate_vala_compile(target)
+ elif 'cython' in target.compilers:
+ target_sources, generated_sources, \
+ transpiled_sources = self.generate_cython_transpile(target)
else:
target_sources = self.get_target_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, [])
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:
rustc = target.compilers['rust']
# Rust compiler takes only the main file as input and
@@ -1889,10 +1952,7 @@ int dummy;
for for_machine in MachineChoice:
complist = self.environment.coredata.compilers[for_machine]
for langname, compiler in complist.items():
- if langname == 'java' \
- or langname == 'vala' \
- or langname == 'rust' \
- or langname == 'cs':
+ if langname in {'java', 'vala', 'rust', 'cs', 'cython'}:
continue
rule = '{}_LINKER{}'.format(langname, self.get_rule_suffix(for_machine))
command = compiler.get_linker_exelist()
@@ -1940,6 +2000,12 @@ int dummy;
description = 'Compiling Vala source $in'
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):
rule = self.compiler_to_rule_name(compiler)
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):
self.generate_swift_compile_rules(compiler)
return
+ if langname == 'cython':
+ self.generate_cython_compile_rules(compiler)
+ return
crstr = self.get_rule_suffix(compiler.for_machine)
if langname == 'fortran':
self.generate_fortran_dep_hack(crstr)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index a4d14d0..bc86fe3 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -41,9 +41,10 @@ from .interpreterbase import FeatureNew
if T.TYPE_CHECKING:
from ._typing import ImmutableListProtocol, ImmutableSetProtocol
- from .interpreter.interpreter import Test, SourceOutputs
+ from .interpreter.interpreter import Test, SourceOutputs, Interpreter
from .mesonlib import FileMode, FileOrString
from .backend.backends import Backend
+ from .interpreter.interpreterobjects import GeneratorHolder
pch_kwargs = {'c_pch', 'cpp_pch'}
@@ -63,6 +64,7 @@ lang_arg_kwargs = {
'rust_args',
'vala_args',
'cs_args',
+ 'cython_args',
}
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
# 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']
def validate_sources(self):
@@ -1564,7 +1566,7 @@ class Generator:
raise InvalidArguments('Depends entries must be build targets.')
self.depends.append(d)
- def get_base_outnames(self, inname):
+ def get_base_outnames(self, inname) -> T.List[str]:
plainname = os.path.basename(inname)
basename = os.path.splitext(plainname)[0]
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)
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
output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else [])
#XXX
@@ -1621,14 +1623,14 @@ class Generator:
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.name = self.generator.exe
self.depends = set() # Things this target depends on (because e.g. a custom target was used as input)
self.subdir = subdir
- self.infilelist = []
- self.outfilelist = []
- self.outmap = {}
+ self.infilelist: T.List['File'] = []
+ self.outfilelist: T.List[str] = []
+ self.outmap: T.Dict['File', str] = {}
self.extra_depends = []
self.depend_files = []
self.preserve_path_from = preserve_path_from
@@ -1642,17 +1644,17 @@ class GeneratedList:
# know the absolute path of
self.depend_files.append(File.from_absolute_file(path))
- def add_preserved_path_segment(self, infile, outfiles, state):
- result = []
+ def add_preserved_path_segment(self, infile: 'File', outfiles: T.List[str], state: 'Interpreter') -> T.List[str]:
+ result: T.List[str] = []
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)
path_segment = os.path.dirname(rel)
for of in outfiles:
result.append(os.path.join(path_segment, of))
return result
- def add_file(self, newfile, state):
+ def add_file(self, newfile: 'File', state: 'Interpreter') -> None:
self.infilelist.append(newfile)
outfiles = self.generator.get_base_outnames(newfile.fname)
if self.preserve_path_from:
@@ -1660,16 +1662,16 @@ class GeneratedList:
self.outfilelist += outfiles
self.outmap[newfile] = outfiles
- def get_inputs(self):
+ def get_inputs(self) -> T.List['File']:
return self.infilelist
def get_outputs(self) -> T.List[str]:
return self.outfilelist
- def get_outputs_for(self, filename):
+ def get_outputs_for(self, filename: 'File') -> T.List[str]:
return self.outmap[filename]
- def get_generator(self):
+ def get_generator(self) -> 'Generator':
return self.generator
def get_extra_args(self):
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
index 60cfdfe..bd30b9b 100644
--- a/mesonbuild/compilers/__init__.py
+++ b/mesonbuild/compilers/__init__.py
@@ -107,6 +107,7 @@ __all__ = [
'VisualStudioCCompiler',
'VisualStudioCPPCompiler',
'CLikeCompiler',
+ 'CythonCompiler',
]
# 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.clang import ClangCompiler
from .mixins.clike import CLikeCompiler
+from .cython import CythonCompiler
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 361f5d6..cdb9f1c 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -64,6 +64,7 @@ lang_suffixes = {
'cs': ('cs',),
'swift': ('swift',),
'java': ('java',),
+ 'cython': ('pyx', ),
} # type: T.Dict[str, T.Tuple[str, ...]]
all_languages = lang_suffixes.keys()
cpp_suffixes = lang_suffixes['cpp'] + ('h',) # type: T.Tuple[str, ...]
@@ -97,6 +98,7 @@ CFLAGS_MAPPING: T.Mapping[str, str] = {
'd': 'DFLAGS',
'vala': 'VALAFLAGS',
'rust': 'RUSTFLAGS',
+ 'cython': 'CYTHONFLAGS',
}
CEXE_MAPPING: T.Mapping = {
diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py
new file mode 100644
index 0000000..513f079
--- /dev/null
+++ b/mesonbuild/compilers/cython.py
@@ -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
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index fc9b703..ce037dd 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -87,6 +87,7 @@ from .compilers import (
ClangObjCPPCompiler,
ClangClCCompiler,
ClangClCPPCompiler,
+ CythonCompiler,
FlangFortranCompiler,
G95FortranCompiler,
GnuCCompiler,
@@ -732,6 +733,7 @@ class Environment:
self.default_rust = ['rustc']
self.default_swift = ['swiftc']
self.default_vala = ['valac']
+ self.default_cython = [['cython']]
self.default_static_linker = ['ar', 'gar']
self.default_strip = ['strip']
self.vs_static_linker = ['lib']
@@ -1757,6 +1759,30 @@ class Environment:
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):
exelist = self.lookup_binary_entry(for_machine, 'vala')
is_cross = self.is_cross_build(for_machine)
@@ -2023,6 +2049,8 @@ class Environment:
comp = self.detect_fortran_compiler(for_machine)
elif lang == 'swift':
comp = self.detect_swift_compiler(for_machine)
+ elif lang == 'cython':
+ comp = self.detect_cython_compiler(for_machine)
else:
comp = None
return comp
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index ecaeb7a..eff76be 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -1193,8 +1193,9 @@ 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 and 'c' not in langs:
- FeatureNew('Adding Vala language without C', '0.59.0').use(self.subproject)
+ 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)
args.append('c')
success = True