From 24221d71ccd341759b008cf1918826910c40247c Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Wed, 14 Sep 2016 20:26:14 +0300 Subject: Created a Python 3 module for simpler building of Python extension modules. --- mesonbuild/environment.py | 8 ++++ mesonbuild/interpreter.py | 54 ++++++++++++++++---------- mesonbuild/modules/python3.py | 40 +++++++++++++++++++ test cases/python3/2 extmodule/ext/meson.build | 15 +------ test cases/python3/2 extmodule/meson.build | 1 + test cases/python3/3 cython/libdir/meson.build | 4 +- test cases/python3/3 cython/meson.build | 2 + 7 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 mesonbuild/modules/python3.py diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index e4a55b3..64d9f8b 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -899,3 +899,11 @@ class CrossBuildInfo(): self.config['host_machine']['system'] == detect_system(): return False return True + + +class MachineInfo: + def __init__(self, system, cpu_family, cpu, endian): + self.system = system + self.cpu_family = cpu_family + self.cpu = cpu + self.endian = endian diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 382c04c..bfc1453 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -355,23 +355,27 @@ class BuildMachine(InterpreterObject): def __init__(self, compilers): self.compilers = compilers InterpreterObject.__init__(self) - self.methods.update({'system': self.system_method, - 'cpu_family': self.cpu_family_method, - 'cpu': self.cpu_method, - 'endian': self.endian_method, - }) + self.held_object = environment.MachineInfo(environment.detect_system(), + environment.detect_cpu_family(self.compilers), + environment.detect_cpu(self.compilers), + sys.byteorder) + self.methods.update({'system' : self.system_method, + 'cpu_family' : self.cpu_family_method, + 'cpu' : self.cpu_method, + 'endian' : self.endian_method, + }) def cpu_family_method(self, args, kwargs): - return environment.detect_cpu_family(self.compilers) + return self.held_object.cpu_family def cpu_method(self, args, kwargs): - return environment.detect_cpu(self.compilers) + return self.held_object.cpu def system_method(self, args, kwargs): - return environment.detect_system() + return self.held_object.system def endian_method(self, args, kwargs): - return sys.byteorder + return self.held_object.endian # This class will provide both host_machine and # target_machine @@ -384,23 +388,27 @@ class CrossMachineInfo(InterpreterObject): 'Machine info is currently {}\n'.format(cross_info) + 'but is missing {}.'.format(minimum_cross_info - set(cross_info))) self.info = cross_info - self.methods.update({'system': self.system_method, - 'cpu': self.cpu_method, - 'cpu_family': self.cpu_family_method, - 'endian': self.endian_method, - }) + self.held_object = environment.MachineInfo(cross_info['system'], + cross_info['cpu_family'], + cross_info['cpu'], + cross_info['endian']) + self.methods.update({'system' : self.system_method, + 'cpu' : self.cpu_method, + 'cpu_family' : self.cpu_family_method, + 'endian' : self.endian_method, + }) - def system_method(self, args, kwargs): - return self.info['system'] + def cpu_family_method(self, args, kwargs): + return self.held_object.cpu_family def cpu_method(self, args, kwargs): - return self.info['cpu'] + return self.held_object.cpu - def cpu_family_method(self, args, kwargs): - return self.info['cpu_family'] + def system_method(self, args, kwargs): + return self.held_object.system def endian_method(self, args, kwargs): - return self.info['endian'] + return self.held_object.endian class IncludeDirsHolder(InterpreterObject): def __init__(self, idobj): @@ -1000,6 +1008,10 @@ class ModuleHolder(InterpreterObject): state.man = self.interpreter.build.get_man() state.global_args = self.interpreter.build.global_args state.project_args = self.interpreter.build.projects_args.get(self.interpreter.subproject, {}) + state.build_machine = self.interpreter.builtin['build_machine'].held_object + state.host_machine = self.interpreter.builtin['host_machine'].held_object + state.target_machine = self.interpreter.builtin['target_machine'].held_object + state.interpreter = self.interpreter value = fn(state, args, kwargs) if num_targets != len(self.interpreter.build.targets): raise InterpreterException('Extension module altered internal state illegally.') @@ -1283,6 +1295,8 @@ class Interpreter(InterpreterBase): # FIXME: This is special cased and not ideal: # The first source is our new VapiTarget, the rest are deps self.process_new_values(v.sources[0]) + elif hasattr(v, 'held_object'): + pass else: raise InterpreterException('Module returned a value of unknown type.') diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py new file mode 100644 index 0000000..370e925 --- /dev/null +++ b/mesonbuild/modules/python3.py @@ -0,0 +1,40 @@ +# Copyright 2016-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .. import coredata, build +from .. import mesonlib +import os + +class Python3Module: + + def extension_module(self, state, args, kwargs): + if 'name_prefix' in kwargs: + raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.') + if 'name_suffix' in kwargs: + raise mesonlib.MesonException('Name_suffix is set automatically, specifying it is forbidden.') + host_system = state.host_machine.system + if host_system == 'darwin': + # Default suffix is 'dylib' but Python does not use it for extensions. + suffix = 'so' + elif host_system == 'windows': + # On Windows the extension is pyd for some unexplainable reason. + suffix = 'pyd' + else: + suffix = [] + kwargs['name_prefix'] = '' + kwargs['name_suffix'] = suffix + return state.interpreter.func_shared_module(None, args, kwargs) + +def initialize(): + return Python3Module() diff --git a/test cases/python3/2 extmodule/ext/meson.build b/test cases/python3/2 extmodule/ext/meson.build index 7d67953..d5d8849 100644 --- a/test cases/python3/2 extmodule/ext/meson.build +++ b/test cases/python3/2 extmodule/ext/meson.build @@ -1,17 +1,6 @@ -if host_machine.system() == 'darwin' - # Default suffix is 'dylib' but Python does not use for extensions. - suffix = 'so' -elif host_machine.system() == 'windows' - # On Windows the extension is pyd for some unexplainable reason. - suffix = 'pyd' -else - suffix = [] -endif - -pylib = shared_library('tachyon', +pylib = py3_mod.extension_module('tachyon', 'tachyon_module.c', dependencies : py3_dep, - name_prefix : '', - name_suffix : suffix) +) pypathdir = meson.current_build_dir() diff --git a/test cases/python3/2 extmodule/meson.build b/test cases/python3/2 extmodule/meson.build index 92a12b2..582a14e 100644 --- a/test cases/python3/2 extmodule/meson.build +++ b/test cases/python3/2 extmodule/meson.build @@ -3,6 +3,7 @@ project('Python extension module', 'c', # Because Windows Python ships only with optimized libs, # we must build this project the same way. +py3_mod = import('python3') py3_dep = dependency('python3', required : false) if py3_dep.found() diff --git a/test cases/python3/3 cython/libdir/meson.build b/test cases/python3/3 cython/libdir/meson.build index 5c0352e..0d015f0 100644 --- a/test cases/python3/3 cython/libdir/meson.build +++ b/test cases/python3/3 cython/libdir/meson.build @@ -14,10 +14,8 @@ pyx_c = custom_target('storer_pyx', command : [cython, '@INPUT@', '-o', '@OUTPUT@'], ) -slib = shared_library('storer', +slib = py3_mod.extension_module('storer', 'storer.c', pyx_c, - name_prefix : '', - name_suffix : suffix, dependencies : py3_dep) pydir = meson.current_build_dir() diff --git a/test cases/python3/3 cython/meson.build b/test cases/python3/3 cython/meson.build index 22bbf7a..c6027ac 100644 --- a/test cases/python3/3 cython/meson.build +++ b/test cases/python3/3 cython/meson.build @@ -5,6 +5,8 @@ cython = find_program('cython3', required : false) py3_dep = dependency('python3', required : false) if cython.found() and py3_dep.found() + py3_dep = dependency('python3') + py3_mod = import('python3') subdir('libdir') test('cython tester', -- cgit v1.1 From fbabe8ad85725762e46b7c4c2f2c680c3351ec80 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 8 Jan 2017 22:47:57 +0200 Subject: There are two different kinds of extensions: modules that create new objects directly and snippets that just call into interpreter methods. --- mesonbuild/interpreter.py | 30 ++++++++++++++++-------------- mesonbuild/modules/__init__.py | 7 +++++++ mesonbuild/modules/gnome.py | 3 ++- mesonbuild/modules/i18n.py | 9 +++++---- mesonbuild/modules/modtest.py | 3 ++- mesonbuild/modules/pkgconfig.py | 6 ++++-- mesonbuild/modules/python3.py | 13 ++++++++----- mesonbuild/modules/qt4.py | 3 ++- mesonbuild/modules/qt5.py | 3 ++- mesonbuild/modules/rpm.py | 3 ++- mesonbuild/modules/windows.py | 7 ++++--- 11 files changed, 54 insertions(+), 33 deletions(-) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index bfc1453..29a5772 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -359,11 +359,11 @@ class BuildMachine(InterpreterObject): environment.detect_cpu_family(self.compilers), environment.detect_cpu(self.compilers), sys.byteorder) - self.methods.update({'system' : self.system_method, - 'cpu_family' : self.cpu_family_method, - 'cpu' : self.cpu_method, - 'endian' : self.endian_method, - }) + self.methods.update({'system': self.system_method, + 'cpu_family': self.cpu_family_method, + 'cpu': self.cpu_method, + 'endian': self.endian_method, + }) def cpu_family_method(self, args, kwargs): return self.held_object.cpu_family @@ -392,11 +392,11 @@ class CrossMachineInfo(InterpreterObject): cross_info['cpu_family'], cross_info['cpu'], cross_info['endian']) - self.methods.update({'system' : self.system_method, - 'cpu' : self.cpu_method, - 'cpu_family' : self.cpu_family_method, - 'endian' : self.endian_method, - }) + self.methods.update({'system': self.system_method, + 'cpu': self.cpu_method, + 'cpu_family': self.cpu_family_method, + 'endian': self.endian_method, + }) def cpu_family_method(self, args, kwargs): return self.held_object.cpu_family @@ -1011,10 +1011,12 @@ class ModuleHolder(InterpreterObject): state.build_machine = self.interpreter.builtin['build_machine'].held_object state.host_machine = self.interpreter.builtin['host_machine'].held_object state.target_machine = self.interpreter.builtin['target_machine'].held_object - state.interpreter = self.interpreter - value = fn(state, args, kwargs) - if num_targets != len(self.interpreter.build.targets): - raise InterpreterException('Extension module altered internal state illegally.') + if self.held_object.is_snippet(method_name): + value = fn(self.interpreter, state, args, kwargs) + else: + value = fn(state, args, kwargs) + if num_targets != len(self.interpreter.build.targets): + raise InterpreterException('Extension module altered internal state illegally.') return self.interpreter.module_method_callback(value) class MesonMain(InterpreterObject): diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 16cada0..c7f24d4 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -6,6 +6,13 @@ from ..mesonlib import MesonException _found_programs = {} +class ExtensionModule: + def __init__(self): + self.snippets = set() # List of methods that operate only on the interpreter. + + def is_snippet(self, funcname): + return funcname in self.snippets + def find_program(program_name, target_name): if program_name in _found_programs: return _found_programs[program_name] diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index ad34640..47fa68e 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -29,6 +29,7 @@ from .. import compilers from .. import interpreter from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget from . import find_program, get_include_args +from . import ExtensionModule # gresource compilation is broken due to the way @@ -57,7 +58,7 @@ def gir_has_extra_lib_arg(): pass return _gir_has_extra_lib_arg -class GnomeModule: +class GnomeModule(ExtensionModule): gir_dep = None @staticmethod diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index fa52463..5738cb3 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -12,13 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +import sys +import shutil + from os import path from .. import coredata, mesonlib, build from ..mesonlib import MesonException from . import ModuleReturnValue - -import sys -import shutil +from . import ExtensionModule PRESET_ARGS = { 'glib': [ @@ -46,7 +47,7 @@ PRESET_ARGS = { ] } -class I18nModule: +class I18nModule(ExtensionModule): def merge_file(self, state, args, kwargs): podir = kwargs.pop('po_dir', None) diff --git a/mesonbuild/modules/modtest.py b/mesonbuild/modules/modtest.py index dc347e2..3e11b70 100644 --- a/mesonbuild/modules/modtest.py +++ b/mesonbuild/modules/modtest.py @@ -13,8 +13,9 @@ # limitations under the License. from . import ModuleReturnValue +from . import ExtensionModule -class TestModule: +class TestModule(ExtensionModule): def print_hello(self, state, args, kwargs): print('Hello from a Meson module') diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 38358f3..c558d48 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -12,14 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os + from .. import build from .. import mesonlib from .. import mlog from . import ModuleReturnValue +from . import ExtensionModule -import os -class PkgConfigModule: +class PkgConfigModule(ExtensionModule): def _get_lname(self, l, msg, pcfile): # Nothing special diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 370e925..9b6e71e 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -12,13 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .. import coredata, build from .. import mesonlib -import os -class Python3Module: +from . import ExtensionModule - def extension_module(self, state, args, kwargs): +class Python3Module(ExtensionModule): + def __init__(self): + super().__init__() + self.snippets.add('extension_module') + + def extension_module(self, interpreter, state, args, kwargs): if 'name_prefix' in kwargs: raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.') if 'name_suffix' in kwargs: @@ -34,7 +37,7 @@ class Python3Module: suffix = [] kwargs['name_prefix'] = '' kwargs['name_suffix'] = suffix - return state.interpreter.func_shared_module(None, args, kwargs) + return interpreter.func_shared_module(None, args, kwargs) def initialize(): return Python3Module() diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py index 6759270..9a9ec04 100644 --- a/mesonbuild/modules/qt4.py +++ b/mesonbuild/modules/qt4.py @@ -17,10 +17,11 @@ from .. import mlog from .. import build from ..mesonlib import MesonException, Popen_safe from ..dependencies import Qt4Dependency +from . import ExtensionModule import xml.etree.ElementTree as ET from . import ModuleReturnValue -class Qt4Module(): +class Qt4Module(ExtensionModule): tools_detected = False def _detect_tools(self, env): diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py index 53f1cb5..cb5c261 100644 --- a/mesonbuild/modules/qt5.py +++ b/mesonbuild/modules/qt5.py @@ -17,10 +17,11 @@ from .. import mlog from .. import build from ..mesonlib import MesonException, Popen_safe from ..dependencies import Qt5Dependency +from . import ExtensionModule import xml.etree.ElementTree as ET from . import ModuleReturnValue -class Qt5Module(): +class Qt5Module(ExtensionModule): tools_detected = False def _detect_tools(self, env): diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index a696db9..bd8a3c4 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -21,10 +21,11 @@ import datetime from .. import mlog from . import GirTarget, TypelibTarget from . import ModuleReturnValue +from . import ExtensionModule import os -class RPMModule: +class RPMModule(ExtensionModule): def generate_spec_template(self, state, args, kwargs): compiler_deps = set() diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 8574dbe..8203789 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -12,14 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os + from .. import mesonlib, dependencies, build from ..mesonlib import MesonException from . import get_include_args from . import ModuleReturnValue +from . import ExtensionModule -import os - -class WindowsModule: +class WindowsModule(ExtensionModule): def detect_compiler(self, compilers): for l in ('c', 'cpp'): -- cgit v1.1 From 6ac9a8e738dae97c64308da83949931fb726bbe7 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 8 Jan 2017 23:35:59 +0200 Subject: Add .find_python() method. Supersedes #777. --- mesonbuild/interpreter.py | 11 ++++++++--- mesonbuild/modules/python3.py | 8 +++++++- test cases/python3/1 basic/meson.build | 3 ++- test cases/python3/1 basic/subdir/meson.build | 5 ++--- test cases/python3/2 extmodule/meson.build | 4 +++- test cases/python3/3 cython/libdir/meson.build | 10 ---------- test cases/python3/3 cython/meson.build | 4 +++- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 29a5772..c736cd0 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1013,11 +1013,12 @@ class ModuleHolder(InterpreterObject): state.target_machine = self.interpreter.builtin['target_machine'].held_object if self.held_object.is_snippet(method_name): value = fn(self.interpreter, state, args, kwargs) + return self.interpreter.holderify(value) else: value = fn(state, args, kwargs) if num_targets != len(self.interpreter.build.targets): raise InterpreterException('Extension module altered internal state illegally.') - return self.interpreter.module_method_callback(value) + return self.interpreter.module_method_callback(value) class MesonMain(InterpreterObject): def __init__(self, build, interpreter): @@ -1275,8 +1276,11 @@ class Interpreter(InterpreterBase): return DataHolder(item) elif isinstance(item, dependencies.InternalDependency): return InternalDependencyHolder(item) + elif isinstance(item, dependencies.ExternalProgram): + return ExternalProgramHolder(item) + elif hasattr(item, 'held_object'): + return item else: - print(item) raise InterpreterException('Module returned a value of unknown type.') def process_new_values(self, invalues): @@ -1293,6 +1297,8 @@ class Interpreter(InterpreterBase): self.build.install_scripts.append(v) elif isinstance(v, build.Data): self.build.data.append(v) + elif isinstance(v, dependencies.ExternalProgram): + return ExternalProgramHolder(v) elif isinstance(v, dependencies.InternalDependency): # FIXME: This is special cased and not ideal: # The first source is our new VapiTarget, the rest are deps @@ -1304,7 +1310,6 @@ class Interpreter(InterpreterBase): def module_method_callback(self, return_object): if not isinstance(return_object, ModuleReturnValue): - print(return_object) assert(False) raise InterpreterException('Bug in module, it returned an invalid object') invalues = return_object.new_objects diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 9b6e71e..53e28c4 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .. import mesonlib +import sys +from .. import mesonlib, dependencies from . import ExtensionModule +from mesonbuild.modules import ModuleReturnValue class Python3Module(ExtensionModule): def __init__(self): @@ -39,5 +41,9 @@ class Python3Module(ExtensionModule): kwargs['name_suffix'] = suffix return interpreter.func_shared_module(None, args, kwargs) + def find_python(self, state, args, kwargs): + py3 = dependencies.ExternalProgram('python3', sys.executable, silent=True) + return ModuleReturnValue(py3, [py3]) + def initialize(): return Python3Module() diff --git a/test cases/python3/1 basic/meson.build b/test cases/python3/1 basic/meson.build index badd3e5..9d5f874 100644 --- a/test cases/python3/1 basic/meson.build +++ b/test cases/python3/1 basic/meson.build @@ -1,6 +1,7 @@ project('python sample', 'c') -py3 = find_program('python3') +py3_mod = import('python3') +py3 = py3_mod.find_python() main = files('prog.py') diff --git a/test cases/python3/1 basic/subdir/meson.build b/test cases/python3/1 basic/subdir/meson.build index 3f275ad..8fe91b9 100644 --- a/test cases/python3/1 basic/subdir/meson.build +++ b/test cases/python3/1 basic/subdir/meson.build @@ -1,5 +1,4 @@ -submain = find_program('subprog.py') - test('subdir', - submain, + py3, + args : files('subprog.py'), env : 'PYTHONPATH=' + meson.source_root()) diff --git a/test cases/python3/2 extmodule/meson.build b/test cases/python3/2 extmodule/meson.build index 582a14e..25e2c63 100644 --- a/test cases/python3/2 extmodule/meson.build +++ b/test cases/python3/2 extmodule/meson.build @@ -4,13 +4,15 @@ project('Python extension module', 'c', # we must build this project the same way. py3_mod = import('python3') +py3 = py3_mod.find_python() py3_dep = dependency('python3', required : false) if py3_dep.found() subdir('ext') test('extmod', - find_program('blaster.py'), + py3, + args : files('blaster.py'), env : ['PYTHONPATH=' + pypathdir]) else error('MESON_SKIP_TEST: Python3 libraries not found, skipping test.') diff --git a/test cases/python3/3 cython/libdir/meson.build b/test cases/python3/3 cython/libdir/meson.build index 0d015f0..7823a6b 100644 --- a/test cases/python3/3 cython/libdir/meson.build +++ b/test cases/python3/3 cython/libdir/meson.build @@ -1,13 +1,3 @@ -if host_machine.system() == 'darwin' - # Default suffix is 'dylib' but Python does not use for extensions. - suffix = 'so' -elif host_machine.system() == 'windows' - # On Windows the extension is pyd for some unexplainable reason. - suffix = 'pyd' -else - suffix = [] -endif - pyx_c = custom_target('storer_pyx', output : 'storer_pyx.c', input : 'storer.pyx', diff --git a/test cases/python3/3 cython/meson.build b/test cases/python3/3 cython/meson.build index c6027ac..753b906 100644 --- a/test cases/python3/3 cython/meson.build +++ b/test cases/python3/3 cython/meson.build @@ -7,10 +7,12 @@ py3_dep = dependency('python3', required : false) if cython.found() and py3_dep.found() py3_dep = dependency('python3') py3_mod = import('python3') + py3 = py3_mod.find_python() subdir('libdir') test('cython tester', - find_program('cytest.py'), + py3, + args : files('cytest.py'), env : ['PYTHONPATH=' + pydir] ) else -- cgit v1.1