1
|
|
|
""" |
2
|
|
|
AppVeyor will at least have few Pythons around so there's no point of implementing a bootstrapper in PowerShell. |
3
|
|
|
|
4
|
|
|
This is a port of https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 |
5
|
|
|
with various fixes and improvements that just weren't feasible to implement in PowerShell. |
6
|
|
|
""" |
7
|
|
|
from __future__ import print_function |
8
|
|
|
from os import environ |
9
|
|
|
from os.path import exists |
10
|
|
|
from subprocess import check_call |
11
|
|
|
|
12
|
|
|
try: |
13
|
|
|
from urllib.request import urlretrieve |
14
|
|
|
except ImportError: |
15
|
|
|
from urllib import urlretrieve |
16
|
|
|
|
17
|
|
|
BASE_URL = "https://www.python.org/ftp/python/" |
18
|
|
|
GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" |
19
|
|
|
GET_PIP_PATH = "C:\get-pip.py" |
20
|
|
|
URLS = { |
21
|
|
|
("2.7", "64"): BASE_URL + "2.7.10/python-2.7.13.amd64.msi", |
22
|
|
|
("2.7", "32"): BASE_URL + "2.7.10/python-2.7.13.msi", |
23
|
|
|
# NOTE: no .msi installer for 3.3.6 |
24
|
|
|
("3.3", "64"): BASE_URL + "3.3.3/python-3.3.5.amd64.msi", |
25
|
|
|
("3.3", "32"): BASE_URL + "3.3.3/python-3.3.5.msi", |
26
|
|
|
("3.4", "64"): BASE_URL + "3.4.3/python-3.4.6.amd64.msi", |
27
|
|
|
("3.4", "32"): BASE_URL + "3.4.3/python-3.4.6.msi", |
28
|
|
|
("3.5", "64"): BASE_URL + "3.5.0/python-3.5.3-amd64.exe", |
29
|
|
|
("3.5", "32"): BASE_URL + "3.5.0/python-3.5.3.exe", |
30
|
|
|
("3.6", "64"): BASE_URL + "3.6.0/python-3.6.0-amd64.exe", |
31
|
|
|
("3.6", "32"): BASE_URL + "3.6.0/python-3.6.0.exe", |
32
|
|
|
} |
33
|
|
|
INSTALL_CMD = { |
34
|
|
|
# Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. |
35
|
|
|
"2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], |
36
|
|
|
["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], |
37
|
|
|
"3.3": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], |
38
|
|
|
["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], |
39
|
|
|
"3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], |
40
|
|
|
["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], |
41
|
|
|
"3.5": [["{path}", "/quiet", "TargetDir={home}"]], |
42
|
|
|
"3.6": [["{path}", "/quiet", "TargetDir={home}"]], |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
|
46
|
|
|
def download_file(url, path): |
47
|
|
|
print("Downloading: {} (into {})".format(url, path)) |
48
|
|
|
progress = [0, 0] |
49
|
|
|
|
50
|
|
|
def report(count, size, total): |
51
|
|
|
progress[0] = count * size |
52
|
|
|
if progress[0] - progress[1] > 1000000: |
53
|
|
|
progress[1] = progress[0] |
54
|
|
|
print("Downloaded {:,}/{:,} ...".format(progress[1], total)) |
55
|
|
|
|
56
|
|
|
dest, _ = urlretrieve(url, path, reporthook=report) |
57
|
|
|
return dest |
58
|
|
|
|
59
|
|
|
|
60
|
|
|
def install_python(version, arch, home): |
61
|
|
|
print("Installing Python", version, "for", arch, "bit architecture to", home) |
62
|
|
|
if exists(home): |
63
|
|
|
return |
64
|
|
|
|
65
|
|
|
path = download_python(version, arch) |
66
|
|
|
print("Installing", path, "to", home) |
67
|
|
|
success = False |
68
|
|
|
for cmd in INSTALL_CMD[version]: |
69
|
|
|
cmd = [part.format(home=home, path=path) for part in cmd] |
70
|
|
|
print("Running:", " ".join(cmd)) |
71
|
|
|
try: |
72
|
|
|
check_call(cmd) |
73
|
|
|
except Exception as exc: |
74
|
|
|
print("Failed command", cmd, "with:", exc) |
75
|
|
|
if exists("install.log"): |
76
|
|
|
with open("install.log") as fh: |
77
|
|
|
print(fh.read()) |
78
|
|
|
else: |
79
|
|
|
success = True |
80
|
|
|
if success: |
81
|
|
|
print("Installation complete!") |
82
|
|
|
else: |
83
|
|
|
print("Installation failed") |
84
|
|
|
|
85
|
|
|
|
86
|
|
|
def download_python(version, arch): |
87
|
|
|
for _ in range(3): |
88
|
|
|
try: |
89
|
|
|
return download_file(URLS[version, arch], "installer.exe") |
90
|
|
|
except Exception as exc: |
91
|
|
|
print("Failed to download:", exc) |
92
|
|
|
print("Retrying ...") |
93
|
|
|
|
94
|
|
|
|
95
|
|
|
def install_pip(home): |
96
|
|
|
pip_path = home + "/Scripts/pip.exe" |
97
|
|
|
python_path = home + "/python.exe" |
98
|
|
|
if exists(pip_path): |
99
|
|
|
print("pip already installed.") |
100
|
|
|
else: |
101
|
|
|
print("Installing pip...") |
102
|
|
|
download_file(GET_PIP_URL, GET_PIP_PATH) |
103
|
|
|
print("Executing:", python_path, GET_PIP_PATH) |
104
|
|
|
check_call([python_path, GET_PIP_PATH]) |
105
|
|
|
|
106
|
|
|
|
107
|
|
|
def install_packages(home, *packages): |
108
|
|
|
cmd = [home + "/Scripts/pip.exe", "install"] |
109
|
|
|
cmd.extend(packages) |
110
|
|
|
check_call(cmd) |
111
|
|
|
|
112
|
|
|
|
113
|
|
|
if __name__ == "__main__": |
114
|
|
|
install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) |
115
|
|
|
install_pip(environ['PYTHON_HOME']) |
116
|
|
|
install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") |
117
|
|
|
|