##// END OF EJS Templates
packaging: don't crash building wix with python3.6 and earlier...
Matt Harbison -
r42259:9c07d345 default
parent child Browse files
Show More
@@ -1,157 +1,155 b''
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 distutils.version
11 import getpass
11 import getpass
12 import os
12 import os
13 import pathlib
13 import pathlib
14 import subprocess
14 import subprocess
15 import tarfile
15 import tarfile
16 import zipfile
16 import zipfile
17
17
18
18
19 def extract_tar_to_directory(source: pathlib.Path, dest: pathlib.Path):
19 def extract_tar_to_directory(source: pathlib.Path, dest: pathlib.Path):
20 with tarfile.open(source, 'r') as tf:
20 with tarfile.open(source, 'r') as tf:
21 tf.extractall(dest)
21 tf.extractall(dest)
22
22
23
23
24 def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path):
24 def extract_zip_to_directory(source: pathlib.Path, dest: pathlib.Path):
25 with zipfile.ZipFile(source, 'r') as zf:
25 with zipfile.ZipFile(source, 'r') as zf:
26 zf.extractall(dest)
26 zf.extractall(dest)
27
27
28
28
29 def find_vc_runtime_files(x64=False):
29 def find_vc_runtime_files(x64=False):
30 """Finds Visual C++ Runtime DLLs to include in distribution."""
30 """Finds Visual C++ Runtime DLLs to include in distribution."""
31 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
31 winsxs = pathlib.Path(os.environ['SYSTEMROOT']) / 'WinSxS'
32
32
33 prefix = 'amd64' if x64 else 'x86'
33 prefix = 'amd64' if x64 else 'x86'
34
34
35 candidates = sorted(p for p in os.listdir(winsxs)
35 candidates = sorted(p for p in os.listdir(winsxs)
36 if p.lower().startswith('%s_microsoft.vc90.crt_' % prefix))
36 if p.lower().startswith('%s_microsoft.vc90.crt_' % prefix))
37
37
38 for p in candidates:
38 for p in candidates:
39 print('found candidate VC runtime: %s' % p)
39 print('found candidate VC runtime: %s' % p)
40
40
41 # Take the newest version.
41 # Take the newest version.
42 version = candidates[-1]
42 version = candidates[-1]
43
43
44 d = winsxs / version
44 d = winsxs / version
45
45
46 return [
46 return [
47 d / 'msvcm90.dll',
47 d / 'msvcm90.dll',
48 d / 'msvcp90.dll',
48 d / 'msvcp90.dll',
49 d / 'msvcr90.dll',
49 d / 'msvcr90.dll',
50 winsxs / 'Manifests' / ('%s.manifest' % version),
50 winsxs / 'Manifests' / ('%s.manifest' % version),
51 ]
51 ]
52
52
53
53
54 def windows_10_sdk_info():
54 def windows_10_sdk_info():
55 """Resolves information about the Windows 10 SDK."""
55 """Resolves information about the Windows 10 SDK."""
56
56
57 base = pathlib.Path(os.environ['ProgramFiles(x86)']) / 'Windows Kits' / '10'
57 base = pathlib.Path(os.environ['ProgramFiles(x86)']) / 'Windows Kits' / '10'
58
58
59 if not base.is_dir():
59 if not base.is_dir():
60 raise Exception('unable to find Windows 10 SDK at %s' % base)
60 raise Exception('unable to find Windows 10 SDK at %s' % base)
61
61
62 # Find the latest version.
62 # Find the latest version.
63 bin_base = base / 'bin'
63 bin_base = base / 'bin'
64
64
65 versions = [v for v in os.listdir(bin_base) if v.startswith('10.')]
65 versions = [v for v in os.listdir(bin_base) if v.startswith('10.')]
66 version = sorted(versions, reverse=True)[0]
66 version = sorted(versions, reverse=True)[0]
67
67
68 bin_version = bin_base / version
68 bin_version = bin_base / version
69
69
70 return {
70 return {
71 'root': base,
71 'root': base,
72 'version': version,
72 'version': version,
73 'bin_root': bin_version,
73 'bin_root': bin_version,
74 'bin_x86': bin_version / 'x86',
74 'bin_x86': bin_version / 'x86',
75 'bin_x64': bin_version / 'x64'
75 'bin_x64': bin_version / 'x64'
76 }
76 }
77
77
78
78
79 def find_signtool():
79 def find_signtool():
80 """Find signtool.exe from the Windows SDK."""
80 """Find signtool.exe from the Windows SDK."""
81 sdk = windows_10_sdk_info()
81 sdk = windows_10_sdk_info()
82
82
83 for key in ('bin_x64', 'bin_x86'):
83 for key in ('bin_x64', 'bin_x86'):
84 p = sdk[key] / 'signtool.exe'
84 p = sdk[key] / 'signtool.exe'
85
85
86 if p.exists():
86 if p.exists():
87 return p
87 return p
88
88
89 raise Exception('could not find signtool.exe in Windows 10 SDK')
89 raise Exception('could not find signtool.exe in Windows 10 SDK')
90
90
91
91
92 def sign_with_signtool(file_path, description, subject_name=None,
92 def sign_with_signtool(file_path, description, subject_name=None,
93 cert_path=None, cert_password=None,
93 cert_path=None, cert_password=None,
94 timestamp_url=None):
94 timestamp_url=None):
95 """Digitally sign a file with signtool.exe.
95 """Digitally sign a file with signtool.exe.
96
96
97 ``file_path`` is file to sign.
97 ``file_path`` is file to sign.
98 ``description`` is text that goes in the signature.
98 ``description`` is text that goes in the signature.
99
99
100 The signing certificate can be specified by ``cert_path`` or
100 The signing certificate can be specified by ``cert_path`` or
101 ``subject_name``. These correspond to the ``/f`` and ``/n`` arguments
101 ``subject_name``. These correspond to the ``/f`` and ``/n`` arguments
102 to signtool.exe, respectively.
102 to signtool.exe, respectively.
103
103
104 The certificate password can be specified via ``cert_password``. If
104 The certificate password can be specified via ``cert_password``. If
105 not provided, you will be prompted for the password.
105 not provided, you will be prompted for the password.
106
106
107 ``timestamp_url`` is the URL of a RFC 3161 timestamp server (``/tr``
107 ``timestamp_url`` is the URL of a RFC 3161 timestamp server (``/tr``
108 argument to signtool.exe).
108 argument to signtool.exe).
109 """
109 """
110 if cert_path and subject_name:
110 if cert_path and subject_name:
111 raise ValueError('cannot specify both cert_path and subject_name')
111 raise ValueError('cannot specify both cert_path and subject_name')
112
112
113 while cert_path and not cert_password:
113 while cert_path and not cert_password:
114 cert_password = getpass.getpass('password for %s: ' % cert_path)
114 cert_password = getpass.getpass('password for %s: ' % cert_path)
115
115
116 args = [
116 args = [
117 str(find_signtool()), 'sign',
117 str(find_signtool()), 'sign',
118 '/v',
118 '/v',
119 '/fd', 'sha256',
119 '/fd', 'sha256',
120 '/d', description,
120 '/d', description,
121 ]
121 ]
122
122
123 if cert_path:
123 if cert_path:
124 args.extend(['/f', str(cert_path), '/p', cert_password])
124 args.extend(['/f', str(cert_path), '/p', cert_password])
125 elif subject_name:
125 elif subject_name:
126 args.extend(['/n', subject_name])
126 args.extend(['/n', subject_name])
127
127
128 if timestamp_url:
128 if timestamp_url:
129 args.extend(['/tr', timestamp_url, '/td', 'sha256'])
129 args.extend(['/tr', timestamp_url, '/td', 'sha256'])
130
130
131 args.append(str(file_path))
131 args.append(str(file_path))
132
132
133 print('signing %s' % file_path)
133 print('signing %s' % file_path)
134 subprocess.run(args, check=True)
134 subprocess.run(args, check=True)
135
135
136
136
137 PRINT_PYTHON_INFO = '''
137 PRINT_PYTHON_INFO = '''
138 import platform; print("%s:%s" % (platform.architecture()[0], platform.python_version()))
138 import platform; print("%s:%s" % (platform.architecture()[0], platform.python_version()))
139 '''.strip()
139 '''.strip()
140
140
141
141
142 def python_exe_info(python_exe: pathlib.Path):
142 def python_exe_info(python_exe: pathlib.Path):
143 """Obtain information about a Python executable."""
143 """Obtain information about a Python executable."""
144
144
145 res = subprocess.run(
145 res = subprocess.check_output([str(python_exe), '-c', PRINT_PYTHON_INFO])
146 [str(python_exe), '-c', PRINT_PYTHON_INFO],
147 capture_output=True, check=True)
148
146
149 arch, version = res.stdout.decode('utf-8').split(':')
147 arch, version = res.decode('utf-8').split(':')
150
148
151 version = distutils.version.LooseVersion(version)
149 version = distutils.version.LooseVersion(version)
152
150
153 return {
151 return {
154 'arch': arch,
152 'arch': arch,
155 'version': version,
153 'version': version,
156 'py3': version >= distutils.version.LooseVersion('3'),
154 'py3': version >= distutils.version.LooseVersion('3'),
157 }
155 }
General Comments 0
You need to be logged in to leave comments. Login now