1from __future__ import absolute_import, unicode_literals
2
3import abc
4import logging
5
6from six import add_metaclass
7
8from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest
9from virtualenv.util.path import Path
10
11from ..python2.python2 import Python2
12from .common import CPython, CPythonPosix, CPythonWindows, is_mac_os_framework
13
14
15@add_metaclass(abc.ABCMeta)
16class CPython2(CPython, Python2):
17    """Create a CPython version 2  virtual environment"""
18
19    @classmethod
20    def sources(cls, interpreter):
21        for src in super(CPython2, cls).sources(interpreter):
22            yield src
23        # include folder needed on Python 2 as we don't have pyenv.cfg
24        host_include_marker = cls.host_include_marker(interpreter)
25        if host_include_marker.exists():
26            yield PathRefToDest(host_include_marker.parent, dest=lambda self, _: self.include)
27
28    @classmethod
29    def needs_stdlib_py_module(cls):
30        return False
31
32    @classmethod
33    def host_include_marker(cls, interpreter):
34        return Path(interpreter.system_include) / "Python.h"
35
36    @property
37    def include(self):
38        # the pattern include the distribution name too at the end, remove that via the parent call
39        return (self.dest / self.interpreter.install_path("headers")).parent
40
41    @classmethod
42    def modules(cls):
43        return [
44            "os",  # landmark to set sys.prefix
45        ]
46
47    def ensure_directories(self):
48        dirs = super(CPython2, self).ensure_directories()
49        host_include_marker = self.host_include_marker(self.interpreter)
50        if host_include_marker.exists():
51            dirs.add(self.include.parent)
52        else:
53            logging.debug("no include folders as can't find include marker %s", host_include_marker)
54        return dirs
55
56
57@add_metaclass(abc.ABCMeta)
58class CPython2PosixBase(CPython2, CPythonPosix):
59    """common to macOs framework builds and other posix CPython2"""
60
61    @classmethod
62    def sources(cls, interpreter):
63        for src in super(CPython2PosixBase, cls).sources(interpreter):
64            yield src
65
66        # check if the makefile exists and if so make it available under the virtual environment
67        make_file = Path(interpreter.sysconfig["makefile_filename"])
68        if make_file.exists() and str(make_file).startswith(interpreter.prefix):
69            under_prefix = make_file.relative_to(Path(interpreter.prefix))
70            yield PathRefToDest(make_file, dest=lambda self, s: self.dest / under_prefix)
71
72
73class CPython2Posix(CPython2PosixBase):
74    """CPython 2 on POSIX (excluding macOs framework builds)"""
75
76    @classmethod
77    def can_describe(cls, interpreter):
78        return is_mac_os_framework(interpreter) is False and super(CPython2Posix, cls).can_describe(interpreter)
79
80    @classmethod
81    def sources(cls, interpreter):
82        for src in super(CPython2Posix, cls).sources(interpreter):
83            yield src
84        # landmark for exec_prefix
85        exec_marker_file, to_path, _ = cls.from_stdlib(cls.mappings(interpreter), "lib-dynload")
86        yield PathRefToDest(exec_marker_file, dest=to_path)
87
88
89class CPython2Windows(CPython2, CPythonWindows):
90    """CPython 2 on Windows"""
91
92    @classmethod
93    def sources(cls, interpreter):
94        for src in super(CPython2Windows, cls).sources(interpreter):
95            yield src
96        py27_dll = Path(interpreter.system_executable).parent / "python27.dll"
97        if py27_dll.exists():  # this might be global in the Windows folder in which case it's alright to be missing
98            yield PathRefToDest(py27_dll, dest=cls.to_bin)
99
100        libs = Path(interpreter.system_prefix) / "libs"
101        if libs.exists():
102            yield PathRefToDest(libs, dest=lambda self, s: self.dest / s.name)
103