##// END OF EJS Templates
packaging: extract python exe info to own function...
Gregory Szorc -
r42080:7d121116 default
parent child Browse files
Show More
@@ -1,167 +1,158
1 # inno.py - Inno Setup functionality.
1 # inno.py - Inno Setup functionality.
2 #
2 #
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # no-check-code because Python 3 native.
8 # no-check-code because Python 3 native.
9
9
10 import os
10 import os
11 import pathlib
11 import pathlib
12 import shutil
12 import shutil
13 import subprocess
13 import subprocess
14
14
15 from .downloads import (
15 from .downloads import (
16 download_entry,
16 download_entry,
17 )
17 )
18 from .util import (
18 from .util import (
19 extract_tar_to_directory,
19 extract_tar_to_directory,
20 extract_zip_to_directory,
20 extract_zip_to_directory,
21 find_vc_runtime_files,
21 find_vc_runtime_files,
22 python_exe_info,
22 )
23 )
23
24
24
25
25 PRINT_PYTHON_INFO = '''
26 import platform, sys; print("%s:%d" % (platform.architecture()[0], sys.version_info[0]))
27 '''.strip()
28
29
30 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
26 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
31 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
27 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
32 version=None):
28 version=None):
33 """Build the Inno installer.
29 """Build the Inno installer.
34
30
35 Build files will be placed in ``build_dir``.
31 Build files will be placed in ``build_dir``.
36
32
37 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
33 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
38 for finding the Python 2.7 toolchain. So, we require the environment
34 for finding the Python 2.7 toolchain. So, we require the environment
39 to already be configured with an active toolchain.
35 to already be configured with an active toolchain.
40 """
36 """
41 if not iscc_exe.exists():
37 if not iscc_exe.exists():
42 raise Exception('%s does not exist' % iscc_exe)
38 raise Exception('%s does not exist' % iscc_exe)
43
39
44 if 'VCINSTALLDIR' not in os.environ:
40 if 'VCINSTALLDIR' not in os.environ:
45 raise Exception('not running from a Visual C++ build environment; '
41 raise Exception('not running from a Visual C++ build environment; '
46 'execute the "Visual C++ <version> Command Prompt" '
42 'execute the "Visual C++ <version> Command Prompt" '
47 'application shortcut or a vcsvarsall.bat file')
43 'application shortcut or a vcsvarsall.bat file')
48
44
49 # Identity x86/x64 and validate the environment matches the Python
45 # Identity x86/x64 and validate the environment matches the Python
50 # architecture.
46 # architecture.
51 vc_x64 = r'\x64' in os.environ['LIB']
47 vc_x64 = r'\x64' in os.environ['LIB']
52
48
53 res = subprocess.run(
49 py_info = python_exe_info(python_exe)
54 [str(python_exe), '-c', PRINT_PYTHON_INFO],
55 capture_output=True, check=True)
56
57 py_arch, py_version = res.stdout.decode('utf-8').split(':')
58 py_version = int(py_version)
59
50
60 if vc_x64:
51 if vc_x64:
61 if py_arch != '64bit':
52 if py_info['arch'] != '64bit':
62 raise Exception('architecture mismatch: Visual C++ environment '
53 raise Exception('architecture mismatch: Visual C++ environment '
63 'is configured for 64-bit but Python is 32-bit')
54 'is configured for 64-bit but Python is 32-bit')
64 else:
55 else:
65 if py_arch != '32bit':
56 if py_info['arch'] != '32bit':
66 raise Exception('architecture mismatch: Visual C++ environment '
57 raise Exception('architecture mismatch: Visual C++ environment '
67 'is configured for 32-bit but Python is 64-bit')
58 'is configured for 32-bit but Python is 64-bit')
68
59
69 if py_version != 2:
60 if py_info['py3']:
70 raise Exception('Only Python 2 is currently supported')
61 raise Exception('Only Python 2 is currently supported')
71
62
72 build_dir.mkdir(exist_ok=True)
63 build_dir.mkdir(exist_ok=True)
73
64
74 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
65 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
75 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
66 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
76 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
67 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
77 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
68 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
78
69
79 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
70 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
80
71
81 gettext_root = build_dir / (
72 gettext_root = build_dir / (
82 'gettext-win-%s' % gettext_entry['version'])
73 'gettext-win-%s' % gettext_entry['version'])
83
74
84 if not gettext_root.exists():
75 if not gettext_root.exists():
85 extract_zip_to_directory(gettext_pkg, gettext_root)
76 extract_zip_to_directory(gettext_pkg, gettext_root)
86 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
77 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
87
78
88 # This assumes Python 2. We don't need virtualenv on Python 3.
79 # This assumes Python 2. We don't need virtualenv on Python 3.
89 virtualenv_src_path = build_dir / (
80 virtualenv_src_path = build_dir / (
90 'virtualenv-%s' % virtualenv_entry['version'])
81 'virtualenv-%s' % virtualenv_entry['version'])
91 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
82 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
92
83
93 if not virtualenv_src_path.exists():
84 if not virtualenv_src_path.exists():
94 extract_tar_to_directory(virtualenv_pkg, build_dir)
85 extract_tar_to_directory(virtualenv_pkg, build_dir)
95
86
96 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
87 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
97
88
98 if not py2exe_source_path.exists():
89 if not py2exe_source_path.exists():
99 extract_zip_to_directory(py2exe_pkg, build_dir)
90 extract_zip_to_directory(py2exe_pkg, build_dir)
100
91
101 if not venv_path.exists():
92 if not venv_path.exists():
102 print('creating virtualenv with dependencies')
93 print('creating virtualenv with dependencies')
103 subprocess.run(
94 subprocess.run(
104 [str(python_exe), str(virtualenv_py), str(venv_path)],
95 [str(python_exe), str(virtualenv_py), str(venv_path)],
105 check=True)
96 check=True)
106
97
107 venv_python = venv_path / 'Scripts' / 'python.exe'
98 venv_python = venv_path / 'Scripts' / 'python.exe'
108 venv_pip = venv_path / 'Scripts' / 'pip.exe'
99 venv_pip = venv_path / 'Scripts' / 'pip.exe'
109
100
110 requirements_txt = (source_dir / 'contrib' / 'packaging' /
101 requirements_txt = (source_dir / 'contrib' / 'packaging' /
111 'inno' / 'requirements.txt')
102 'inno' / 'requirements.txt')
112 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
103 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
113 check=True)
104 check=True)
114
105
115 # Force distutils to use VC++ settings from environment, which was
106 # Force distutils to use VC++ settings from environment, which was
116 # validated above.
107 # validated above.
117 env = dict(os.environ)
108 env = dict(os.environ)
118 env['DISTUTILS_USE_SDK'] = '1'
109 env['DISTUTILS_USE_SDK'] = '1'
119 env['MSSdk'] = '1'
110 env['MSSdk'] = '1'
120
111
121 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
112 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
122 if not py2exe_py_path.exists():
113 if not py2exe_py_path.exists():
123 print('building py2exe')
114 print('building py2exe')
124 subprocess.run([str(venv_python), 'setup.py', 'install'],
115 subprocess.run([str(venv_python), 'setup.py', 'install'],
125 cwd=py2exe_source_path,
116 cwd=py2exe_source_path,
126 env=env,
117 env=env,
127 check=True)
118 check=True)
128
119
129 # Register location of msgfmt and other binaries.
120 # Register location of msgfmt and other binaries.
130 env['PATH'] = '%s%s%s' % (
121 env['PATH'] = '%s%s%s' % (
131 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
122 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
132
123
133 print('building Mercurial')
124 print('building Mercurial')
134 subprocess.run(
125 subprocess.run(
135 [str(venv_python), 'setup.py',
126 [str(venv_python), 'setup.py',
136 'py2exe', '-b', '3' if vc_x64 else '2',
127 'py2exe', '-b', '3' if vc_x64 else '2',
137 'build_doc', '--html'],
128 'build_doc', '--html'],
138 cwd=str(source_dir),
129 cwd=str(source_dir),
139 env=env,
130 env=env,
140 check=True)
131 check=True)
141
132
142 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
133 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
143 for f in find_vc_runtime_files(vc_x64):
134 for f in find_vc_runtime_files(vc_x64):
144 if f.name.endswith('.manifest'):
135 if f.name.endswith('.manifest'):
145 basename = 'Microsoft.VC90.CRT.manifest'
136 basename = 'Microsoft.VC90.CRT.manifest'
146 else:
137 else:
147 basename = f.name
138 basename = f.name
148
139
149 dest_path = source_dir / 'dist' / basename
140 dest_path = source_dir / 'dist' / basename
150
141
151 print('copying %s to %s' % (f, dest_path))
142 print('copying %s to %s' % (f, dest_path))
152 shutil.copyfile(f, dest_path)
143 shutil.copyfile(f, dest_path)
153
144
154 print('creating installer')
145 print('creating installer')
155
146
156 args = [str(iscc_exe)]
147 args = [str(iscc_exe)]
157
148
158 if vc_x64:
149 if vc_x64:
159 args.append('/dARCH=x64')
150 args.append('/dARCH=x64')
160
151
161 if version:
152 if version:
162 args.append('/dVERSION=%s' % version)
153 args.append('/dVERSION=%s' % version)
163
154
164 args.append('/Odist')
155 args.append('/Odist')
165 args.append('contrib/packaging/inno/mercurial.iss')
156 args.append('contrib/packaging/inno/mercurial.iss')
166
157
167 subprocess.run(args, cwd=str(source_dir), check=True)
158 subprocess.run(args, cwd=str(source_dir), check=True)
@@ -1,48 +1,73
1 # util.py - Common packaging utility code.
1 # util.py - Common packaging utility code.
2 #
2 #
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # no-check-code because Python 3 native.
8 # no-check-code because Python 3 native.
9
9
10 import distutils.version
10 import os
11 import os
11 import pathlib
12 import pathlib
13 import subprocess
12 import tarfile
14 import tarfile
13 import zipfile
15 import zipfile
14
16
15
17
16 def extract_tar_to_directory(source: pathlib.Path, dest: pathlib.Path):
18 def extract_tar_to_directory(source: pathlib.Path, dest: pathlib.Path):
17 with tarfile.open(source, 'r') as tf:
19 with tarfile.open(source, 'r') as tf:
18 tf.extractall(dest)
20 tf.extractall(dest)
19
21
20
22
21 def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path):
23 def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path):
22 with zipfile.ZipFile(source, 'r') as zf:
24 with zipfile.ZipFile(source, 'r') as zf:
23 zf.extractall(dest)
25 zf.extractall(dest)
24
26
25
27
26 def find_vc_runtime_files(x64=False):
28 def find_vc_runtime_files(x64=False):
27 """Finds Visual C++ Runtime DLLs to include in distribution."""
29 """Finds Visual C++ Runtime DLLs to include in distribution."""
28 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
30 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
29
31
30 prefix = 'amd64' if x64 else 'x86'
32 prefix = 'amd64' if x64 else 'x86'
31
33
32 candidates = sorted(p for p in os.listdir(winsxs)
34 candidates = sorted(p for p in os.listdir(winsxs)
33 if p.lower().startswith('%s_microsoft.vc90.crt_' % prefix))
35 if p.lower().startswith('%s_microsoft.vc90.crt_' % prefix))
34
36
35 for p in candidates:
37 for p in candidates:
36 print('found candidate VC runtime: %s' % p)
38 print('found candidate VC runtime: %s' % p)
37
39
38 # Take the newest version.
40 # Take the newest version.
39 version = candidates[-1]
41 version = candidates[-1]
40
42
41 d = winsxs / version
43 d = winsxs / version
42
44
43 return [
45 return [
44 d / 'msvcm90.dll',
46 d / 'msvcm90.dll',
45 d / 'msvcp90.dll',
47 d / 'msvcp90.dll',
46 d / 'msvcr90.dll',
48 d / 'msvcr90.dll',
47 winsxs / 'Manifests' / ('%s.manifest' % version),
49 winsxs / 'Manifests' / ('%s.manifest' % version),
48 ]
50 ]
51
52
53 PRINT_PYTHON_INFO = '''
54 import platform; print("%s:%s" % (platform.architecture()[0], platform.python_version()))
55 '''.strip()
56
57
58 def python_exe_info(python_exe: pathlib.Path):
59 """Obtain information about a Python executable."""
60
61 res = subprocess.run(
62 [str(python_exe), '-c', PRINT_PYTHON_INFO],
63 capture_output=True, check=True)
64
65 arch, version = res.stdout.decode('utf-8').split(':')
66
67 version = distutils.version.LooseVersion(version)
68
69 return {
70 'arch': arch,
71 'version': version,
72 'py3': version >= distutils.version.LooseVersion('3'),
73 }
General Comments 0
You need to be logged in to leave comments. Login now