Merge pull request #787 from mesonbuild/py3module
Created a Python 3 module for simpler building of Python extension mo…
This commit is contained in:
commit
34caf6471c
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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'):
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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.')
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue