1# Copyright 2016-2017 The Meson development team
2
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6
7#     http://www.apache.org/licenses/LICENSE-2.0
8
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import sysconfig
16from .. import mesonlib
17
18from . import ExtensionModule
19from ..interpreterbase import noKwargs, permittedKwargs, FeatureDeprecated
20from ..build import known_shmod_kwargs
21from ..programs import ExternalProgram
22
23
24class Python3Module(ExtensionModule):
25    @FeatureDeprecated('python3 module', '0.48.0')
26    def __init__(self, *args, **kwargs):
27        super().__init__(*args, **kwargs)
28        self.methods.update({
29            'extension_module': self.extension_module,
30            'find_python': self.find_python,
31            'language_version': self.language_version,
32            'sysconfig_path': self.sysconfig_path,
33        })
34
35    @permittedKwargs(known_shmod_kwargs)
36    def extension_module(self, state, args, kwargs):
37        if 'name_prefix' in kwargs:
38            raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.')
39        if 'name_suffix' in kwargs:
40            raise mesonlib.MesonException('Name_suffix is set automatically, specifying it is forbidden.')
41        host_system = state.host_machine.system
42        if host_system == 'darwin':
43            # Default suffix is 'dylib' but Python does not use it for extensions.
44            suffix = 'so'
45        elif host_system == 'windows':
46            # On Windows the extension is pyd for some unexplainable reason.
47            suffix = 'pyd'
48        else:
49            suffix = []
50        kwargs['name_prefix'] = ''
51        kwargs['name_suffix'] = suffix
52        return self.interpreter.func_shared_module(None, args, kwargs)
53
54    @noKwargs
55    def find_python(self, state, args, kwargs):
56        command = state.environment.lookup_binary_entry(mesonlib.MachineChoice.HOST, 'python3')
57        if command is not None:
58            py3 = ExternalProgram.from_entry('python3', command)
59        else:
60            py3 = ExternalProgram('python3', mesonlib.python_command, silent=True)
61        return py3
62
63    @noKwargs
64    def language_version(self, state, args, kwargs):
65        return sysconfig.get_python_version()
66
67    @noKwargs
68    def sysconfig_path(self, state, args, kwargs):
69        if len(args) != 1:
70            raise mesonlib.MesonException('sysconfig_path() requires passing the name of path to get.')
71        path_name = args[0]
72        valid_names = sysconfig.get_path_names()
73        if path_name not in valid_names:
74            raise mesonlib.MesonException(f'{path_name} is not a valid path name {valid_names}.')
75
76        # Get a relative path without a prefix, e.g. lib/python3.6/site-packages
77        return sysconfig.get_path(path_name, vars={'base': '', 'platbase': '', 'installed_base': ''})[1:]
78
79
80def initialize(*args, **kwargs):
81    return Python3Module(*args, **kwargs)
82