Merge pull request #787 from mesonbuild/py3module

Created a Python 3 module for simpler building of Python extension mo…
This commit is contained in:
Jussi Pakkanen 2017-01-09 16:36:00 -05:00 committed by GitHub
commit 34caf6471c
18 changed files with 140 additions and 62 deletions

View File

@ -899,3 +899,11 @@ class CrossBuildInfo():
self.config['host_machine']['system'] == detect_system(): self.config['host_machine']['system'] == detect_system():
return False return False
return True 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

View File

@ -355,6 +355,10 @@ class BuildMachine(InterpreterObject):
def __init__(self, compilers): def __init__(self, compilers):
self.compilers = compilers self.compilers = compilers
InterpreterObject.__init__(self) InterpreterObject.__init__(self)
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, self.methods.update({'system': self.system_method,
'cpu_family': self.cpu_family_method, 'cpu_family': self.cpu_family_method,
'cpu': self.cpu_method, 'cpu': self.cpu_method,
@ -362,16 +366,16 @@ class BuildMachine(InterpreterObject):
}) })
def cpu_family_method(self, args, kwargs): 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): def cpu_method(self, args, kwargs):
return environment.detect_cpu(self.compilers) return self.held_object.cpu
def system_method(self, args, kwargs): def system_method(self, args, kwargs):
return environment.detect_system() return self.held_object.system
def endian_method(self, args, kwargs): def endian_method(self, args, kwargs):
return sys.byteorder return self.held_object.endian
# This class will provide both host_machine and # This class will provide both host_machine and
# target_machine # target_machine
@ -384,23 +388,27 @@ class CrossMachineInfo(InterpreterObject):
'Machine info is currently {}\n'.format(cross_info) + 'Machine info is currently {}\n'.format(cross_info) +
'but is missing {}.'.format(minimum_cross_info - set(cross_info))) 'but is missing {}.'.format(minimum_cross_info - set(cross_info)))
self.info = cross_info self.info = cross_info
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, self.methods.update({'system': self.system_method,
'cpu': self.cpu_method, 'cpu': self.cpu_method,
'cpu_family': self.cpu_family_method, 'cpu_family': self.cpu_family_method,
'endian': self.endian_method, 'endian': self.endian_method,
}) })
def system_method(self, args, kwargs): def cpu_family_method(self, args, kwargs):
return self.info['system'] return self.held_object.cpu_family
def cpu_method(self, args, kwargs): def cpu_method(self, args, kwargs):
return self.info['cpu'] return self.held_object.cpu
def cpu_family_method(self, args, kwargs): def system_method(self, args, kwargs):
return self.info['cpu_family'] return self.held_object.system
def endian_method(self, args, kwargs): def endian_method(self, args, kwargs):
return self.info['endian'] return self.held_object.endian
class IncludeDirsHolder(InterpreterObject): class IncludeDirsHolder(InterpreterObject):
def __init__(self, idobj): def __init__(self, idobj):
@ -1000,6 +1008,13 @@ class ModuleHolder(InterpreterObject):
state.man = self.interpreter.build.get_man() state.man = self.interpreter.build.get_man()
state.global_args = self.interpreter.build.global_args state.global_args = self.interpreter.build.global_args
state.project_args = self.interpreter.build.projects_args.get(self.interpreter.subproject, {}) 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
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) value = fn(state, args, kwargs)
if num_targets != len(self.interpreter.build.targets): if num_targets != len(self.interpreter.build.targets):
raise InterpreterException('Extension module altered internal state illegally.') raise InterpreterException('Extension module altered internal state illegally.')
@ -1261,8 +1276,11 @@ class Interpreter(InterpreterBase):
return DataHolder(item) return DataHolder(item)
elif isinstance(item, dependencies.InternalDependency): elif isinstance(item, dependencies.InternalDependency):
return InternalDependencyHolder(item) return InternalDependencyHolder(item)
elif isinstance(item, dependencies.ExternalProgram):
return ExternalProgramHolder(item)
elif hasattr(item, 'held_object'):
return item
else: else:
print(item)
raise InterpreterException('Module returned a value of unknown type.') raise InterpreterException('Module returned a value of unknown type.')
def process_new_values(self, invalues): def process_new_values(self, invalues):
@ -1279,16 +1297,19 @@ class Interpreter(InterpreterBase):
self.build.install_scripts.append(v) self.build.install_scripts.append(v)
elif isinstance(v, build.Data): elif isinstance(v, build.Data):
self.build.data.append(v) self.build.data.append(v)
elif isinstance(v, dependencies.ExternalProgram):
return ExternalProgramHolder(v)
elif isinstance(v, dependencies.InternalDependency): elif isinstance(v, dependencies.InternalDependency):
# FIXME: This is special cased and not ideal: # FIXME: This is special cased and not ideal:
# The first source is our new VapiTarget, the rest are deps # The first source is our new VapiTarget, the rest are deps
self.process_new_values(v.sources[0]) self.process_new_values(v.sources[0])
elif hasattr(v, 'held_object'):
pass
else: else:
raise InterpreterException('Module returned a value of unknown type.') raise InterpreterException('Module returned a value of unknown type.')
def module_method_callback(self, return_object): def module_method_callback(self, return_object):
if not isinstance(return_object, ModuleReturnValue): if not isinstance(return_object, ModuleReturnValue):
print(return_object)
assert(False) assert(False)
raise InterpreterException('Bug in module, it returned an invalid object') raise InterpreterException('Bug in module, it returned an invalid object')
invalues = return_object.new_objects invalues = return_object.new_objects

View File

@ -6,6 +6,13 @@ from ..mesonlib import MesonException
_found_programs = {} _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): def find_program(program_name, target_name):
if program_name in _found_programs: if program_name in _found_programs:
return _found_programs[program_name] return _found_programs[program_name]

View File

@ -29,6 +29,7 @@ from .. import compilers
from .. import interpreter from .. import interpreter
from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget
from . import find_program, get_include_args from . import find_program, get_include_args
from . import ExtensionModule
# gresource compilation is broken due to the way # gresource compilation is broken due to the way
@ -57,7 +58,7 @@ def gir_has_extra_lib_arg():
pass pass
return _gir_has_extra_lib_arg return _gir_has_extra_lib_arg
class GnomeModule: class GnomeModule(ExtensionModule):
gir_dep = None gir_dep = None
@staticmethod @staticmethod

View File

@ -12,13 +12,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import sys
import shutil
from os import path from os import path
from .. import coredata, mesonlib, build from .. import coredata, mesonlib, build
from ..mesonlib import MesonException from ..mesonlib import MesonException
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule
import sys
import shutil
PRESET_ARGS = { PRESET_ARGS = {
'glib': [ 'glib': [
@ -46,7 +47,7 @@ PRESET_ARGS = {
] ]
} }
class I18nModule: class I18nModule(ExtensionModule):
def merge_file(self, state, args, kwargs): def merge_file(self, state, args, kwargs):
podir = kwargs.pop('po_dir', None) podir = kwargs.pop('po_dir', None)

View File

@ -13,8 +13,9 @@
# limitations under the License. # limitations under the License.
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule
class TestModule: class TestModule(ExtensionModule):
def print_hello(self, state, args, kwargs): def print_hello(self, state, args, kwargs):
print('Hello from a Meson module') print('Hello from a Meson module')

View File

@ -12,14 +12,16 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import os
from .. import build from .. import build
from .. import mesonlib from .. import mesonlib
from .. import mlog from .. import mlog
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule
import os
class PkgConfigModule: class PkgConfigModule(ExtensionModule):
def _get_lname(self, l, msg, pcfile): def _get_lname(self, l, msg, pcfile):
# Nothing special # Nothing special

View File

@ -0,0 +1,49 @@
# 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.
import sys
from .. import mesonlib, dependencies
from . import ExtensionModule
from mesonbuild.modules import ModuleReturnValue
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:
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 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()

View File

@ -17,10 +17,11 @@ from .. import mlog
from .. import build from .. import build
from ..mesonlib import MesonException, Popen_safe from ..mesonlib import MesonException, Popen_safe
from ..dependencies import Qt4Dependency from ..dependencies import Qt4Dependency
from . import ExtensionModule
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import ModuleReturnValue from . import ModuleReturnValue
class Qt4Module(): class Qt4Module(ExtensionModule):
tools_detected = False tools_detected = False
def _detect_tools(self, env): def _detect_tools(self, env):

View File

@ -17,10 +17,11 @@ from .. import mlog
from .. import build from .. import build
from ..mesonlib import MesonException, Popen_safe from ..mesonlib import MesonException, Popen_safe
from ..dependencies import Qt5Dependency from ..dependencies import Qt5Dependency
from . import ExtensionModule
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from . import ModuleReturnValue from . import ModuleReturnValue
class Qt5Module(): class Qt5Module(ExtensionModule):
tools_detected = False tools_detected = False
def _detect_tools(self, env): def _detect_tools(self, env):

View File

@ -21,10 +21,11 @@ import datetime
from .. import mlog from .. import mlog
from . import GirTarget, TypelibTarget from . import GirTarget, TypelibTarget
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule
import os import os
class RPMModule: class RPMModule(ExtensionModule):
def generate_spec_template(self, state, args, kwargs): def generate_spec_template(self, state, args, kwargs):
compiler_deps = set() compiler_deps = set()

View File

@ -12,14 +12,15 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import os
from .. import mesonlib, dependencies, build from .. import mesonlib, dependencies, build
from ..mesonlib import MesonException from ..mesonlib import MesonException
from . import get_include_args from . import get_include_args
from . import ModuleReturnValue from . import ModuleReturnValue
from . import ExtensionModule
import os class WindowsModule(ExtensionModule):
class WindowsModule:
def detect_compiler(self, compilers): def detect_compiler(self, compilers):
for l in ('c', 'cpp'): for l in ('c', 'cpp'):

View File

@ -1,6 +1,7 @@
project('python sample', 'c') project('python sample', 'c')
py3 = find_program('python3') py3_mod = import('python3')
py3 = py3_mod.find_python()
main = files('prog.py') main = files('prog.py')

View File

@ -1,5 +1,4 @@
submain = find_program('subprog.py')
test('subdir', test('subdir',
submain, py3,
args : files('subprog.py'),
env : 'PYTHONPATH=' + meson.source_root()) env : 'PYTHONPATH=' + meson.source_root())

View File

@ -1,17 +1,6 @@
if host_machine.system() == 'darwin' pylib = py3_mod.extension_module('tachyon',
# 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',
'tachyon_module.c', 'tachyon_module.c',
dependencies : py3_dep, dependencies : py3_dep,
name_prefix : '', )
name_suffix : suffix)
pypathdir = meson.current_build_dir() pypathdir = meson.current_build_dir()

View File

@ -3,13 +3,16 @@ project('Python extension module', 'c',
# Because Windows Python ships only with optimized libs, # Because Windows Python ships only with optimized libs,
# we must build this project the same way. # we must build this project the same way.
py3_mod = import('python3')
py3 = py3_mod.find_python()
py3_dep = dependency('python3', required : false) py3_dep = dependency('python3', required : false)
if py3_dep.found() if py3_dep.found()
subdir('ext') subdir('ext')
test('extmod', test('extmod',
find_program('blaster.py'), py3,
args : files('blaster.py'),
env : ['PYTHONPATH=' + pypathdir]) env : ['PYTHONPATH=' + pypathdir])
else else
error('MESON_SKIP_TEST: Python3 libraries not found, skipping test.') error('MESON_SKIP_TEST: Python3 libraries not found, skipping test.')

View File

@ -1,23 +1,11 @@
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', pyx_c = custom_target('storer_pyx',
output : 'storer_pyx.c', output : 'storer_pyx.c',
input : 'storer.pyx', input : 'storer.pyx',
command : [cython, '@INPUT@', '-o', '@OUTPUT@'], command : [cython, '@INPUT@', '-o', '@OUTPUT@'],
) )
slib = shared_library('storer', slib = py3_mod.extension_module('storer',
'storer.c', pyx_c, 'storer.c', pyx_c,
name_prefix : '',
name_suffix : suffix,
dependencies : py3_dep) dependencies : py3_dep)
pydir = meson.current_build_dir() pydir = meson.current_build_dir()

View File

@ -5,10 +5,14 @@ cython = find_program('cython3', required : false)
py3_dep = dependency('python3', required : false) py3_dep = dependency('python3', required : false)
if cython.found() and py3_dep.found() if cython.found() and py3_dep.found()
py3_dep = dependency('python3')
py3_mod = import('python3')
py3 = py3_mod.find_python()
subdir('libdir') subdir('libdir')
test('cython tester', test('cython tester',
find_program('cytest.py'), py3,
args : files('cytest.py'),
env : ['PYTHONPATH=' + pydir] env : ['PYTHONPATH=' + pydir]
) )
else else