##// END OF EJS Templates
packaging: lowercase the `contrib` and `templates` directories with Inno...
Matt Harbison -
r44712:4aedef6d stable
parent child Browse files
Show More
@@ -1,245 +1,245
1 1 # py2exe.py - Functionality for performing py2exe builds.
2 2 #
3 3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # no-check-code because Python 3 native.
9 9
10 10 import os
11 11 import pathlib
12 12 import subprocess
13 13
14 14 from .downloads import download_entry
15 15 from .util import (
16 16 extract_tar_to_directory,
17 17 extract_zip_to_directory,
18 18 process_install_rules,
19 19 python_exe_info,
20 20 )
21 21
22 22
23 23 STAGING_RULES = [
24 ('contrib/bash_completion', 'Contrib/'),
25 ('contrib/hgk', 'Contrib/hgk.tcl'),
26 ('contrib/hgweb.fcgi', 'Contrib/'),
27 ('contrib/hgweb.wsgi', 'Contrib/'),
28 ('contrib/logo-droplets.svg', 'Contrib/'),
29 ('contrib/mercurial.el', 'Contrib/'),
30 ('contrib/mq.el', 'Contrib/'),
31 ('contrib/tcsh_completion', 'Contrib/'),
32 ('contrib/tcsh_completion_build.sh', 'Contrib/'),
33 ('contrib/vim/*', 'Contrib/Vim/'),
24 ('contrib/bash_completion', 'contrib/'),
25 ('contrib/hgk', 'contrib/hgk.tcl'),
26 ('contrib/hgweb.fcgi', 'contrib/'),
27 ('contrib/hgweb.wsgi', 'contrib/'),
28 ('contrib/logo-droplets.svg', 'contrib/'),
29 ('contrib/mercurial.el', 'contrib/'),
30 ('contrib/mq.el', 'contrib/'),
31 ('contrib/tcsh_completion', 'contrib/'),
32 ('contrib/tcsh_completion_build.sh', 'contrib/'),
33 ('contrib/vim/*', 'contrib/vim/'),
34 34 ('contrib/win32/postinstall.txt', 'ReleaseNotes.txt'),
35 35 ('contrib/win32/ReadMe.html', 'ReadMe.html'),
36 ('contrib/xml.rnc', 'Contrib/'),
37 ('contrib/zsh_completion', 'Contrib/'),
36 ('contrib/xml.rnc', 'contrib/'),
37 ('contrib/zsh_completion', 'contrib/'),
38 38 ('dist/hg.exe', './'),
39 39 ('dist/lib/*.dll', 'lib/'),
40 40 ('dist/lib/*.pyd', 'lib/'),
41 41 ('dist/lib/library.zip', 'lib/'),
42 42 ('dist/Microsoft.VC*.CRT.manifest', './'),
43 43 ('dist/msvc*.dll', './'),
44 44 ('dist/python*.dll', './'),
45 45 ('doc/*.html', 'doc/'),
46 46 ('doc/style.css', 'doc/'),
47 47 ('mercurial/helptext/**/*.txt', 'helptext/'),
48 48 ('mercurial/defaultrc/*.rc', 'defaultrc/'),
49 49 ('mercurial/locale/**/*', 'locale/'),
50 ('mercurial/templates/**/*', 'Templates/'),
50 ('mercurial/templates/**/*', 'templates/'),
51 51 ('COPYING', 'Copying.txt'),
52 52 ]
53 53
54 54 # List of paths to exclude from the staging area.
55 55 STAGING_EXCLUDES = [
56 56 'doc/hg-ssh.8.html',
57 57 ]
58 58
59 59
60 60 def build_py2exe(
61 61 source_dir: pathlib.Path,
62 62 build_dir: pathlib.Path,
63 63 python_exe: pathlib.Path,
64 64 build_name: str,
65 65 venv_requirements_txt: pathlib.Path,
66 66 extra_packages=None,
67 67 extra_excludes=None,
68 68 extra_dll_excludes=None,
69 69 extra_packages_script=None,
70 70 ):
71 71 """Build Mercurial with py2exe.
72 72
73 73 Build files will be placed in ``build_dir``.
74 74
75 75 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
76 76 for finding the Python 2.7 toolchain. So, we require the environment
77 77 to already be configured with an active toolchain.
78 78 """
79 79 if 'VCINSTALLDIR' not in os.environ:
80 80 raise Exception(
81 81 'not running from a Visual C++ build environment; '
82 82 'execute the "Visual C++ <version> Command Prompt" '
83 83 'application shortcut or a vcsvarsall.bat file'
84 84 )
85 85
86 86 # Identity x86/x64 and validate the environment matches the Python
87 87 # architecture.
88 88 vc_x64 = r'\x64' in os.environ['LIB']
89 89
90 90 py_info = python_exe_info(python_exe)
91 91
92 92 if vc_x64:
93 93 if py_info['arch'] != '64bit':
94 94 raise Exception(
95 95 'architecture mismatch: Visual C++ environment '
96 96 'is configured for 64-bit but Python is 32-bit'
97 97 )
98 98 else:
99 99 if py_info['arch'] != '32bit':
100 100 raise Exception(
101 101 'architecture mismatch: Visual C++ environment '
102 102 'is configured for 32-bit but Python is 64-bit'
103 103 )
104 104
105 105 if py_info['py3']:
106 106 raise Exception('Only Python 2 is currently supported')
107 107
108 108 build_dir.mkdir(exist_ok=True)
109 109
110 110 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
111 111 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
112 112 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
113 113 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
114 114
115 115 venv_path = build_dir / (
116 116 'venv-%s-%s' % (build_name, 'x64' if vc_x64 else 'x86')
117 117 )
118 118
119 119 gettext_root = build_dir / ('gettext-win-%s' % gettext_entry['version'])
120 120
121 121 if not gettext_root.exists():
122 122 extract_zip_to_directory(gettext_pkg, gettext_root)
123 123 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
124 124
125 125 # This assumes Python 2. We don't need virtualenv on Python 3.
126 126 virtualenv_src_path = build_dir / (
127 127 'virtualenv-%s' % virtualenv_entry['version']
128 128 )
129 129 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
130 130
131 131 if not virtualenv_src_path.exists():
132 132 extract_tar_to_directory(virtualenv_pkg, build_dir)
133 133
134 134 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
135 135
136 136 if not py2exe_source_path.exists():
137 137 extract_zip_to_directory(py2exe_pkg, build_dir)
138 138
139 139 if not venv_path.exists():
140 140 print('creating virtualenv with dependencies')
141 141 subprocess.run(
142 142 [str(python_exe), str(virtualenv_py), str(venv_path)], check=True
143 143 )
144 144
145 145 venv_python = venv_path / 'Scripts' / 'python.exe'
146 146 venv_pip = venv_path / 'Scripts' / 'pip.exe'
147 147
148 148 subprocess.run(
149 149 [str(venv_pip), 'install', '-r', str(venv_requirements_txt)], check=True
150 150 )
151 151
152 152 # Force distutils to use VC++ settings from environment, which was
153 153 # validated above.
154 154 env = dict(os.environ)
155 155 env['DISTUTILS_USE_SDK'] = '1'
156 156 env['MSSdk'] = '1'
157 157
158 158 if extra_packages_script:
159 159 more_packages = set(
160 160 subprocess.check_output(extra_packages_script, cwd=build_dir)
161 161 .split(b'\0')[-1]
162 162 .strip()
163 163 .decode('utf-8')
164 164 .splitlines()
165 165 )
166 166 if more_packages:
167 167 if not extra_packages:
168 168 extra_packages = more_packages
169 169 else:
170 170 extra_packages |= more_packages
171 171
172 172 if extra_packages:
173 173 env['HG_PY2EXE_EXTRA_PACKAGES'] = ' '.join(sorted(extra_packages))
174 174 hgext3rd_extras = sorted(
175 175 e for e in extra_packages if e.startswith('hgext3rd.')
176 176 )
177 177 if hgext3rd_extras:
178 178 env['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'] = ' '.join(hgext3rd_extras)
179 179 if extra_excludes:
180 180 env['HG_PY2EXE_EXTRA_EXCLUDES'] = ' '.join(sorted(extra_excludes))
181 181 if extra_dll_excludes:
182 182 env['HG_PY2EXE_EXTRA_DLL_EXCLUDES'] = ' '.join(
183 183 sorted(extra_dll_excludes)
184 184 )
185 185
186 186 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
187 187 if not py2exe_py_path.exists():
188 188 print('building py2exe')
189 189 subprocess.run(
190 190 [str(venv_python), 'setup.py', 'install'],
191 191 cwd=py2exe_source_path,
192 192 env=env,
193 193 check=True,
194 194 )
195 195
196 196 # Register location of msgfmt and other binaries.
197 197 env['PATH'] = '%s%s%s' % (
198 198 env['PATH'],
199 199 os.pathsep,
200 200 str(gettext_root / 'bin'),
201 201 )
202 202
203 203 print('building Mercurial')
204 204 subprocess.run(
205 205 [str(venv_python), 'setup.py', 'py2exe', 'build_doc', '--html'],
206 206 cwd=str(source_dir),
207 207 env=env,
208 208 check=True,
209 209 )
210 210
211 211
212 212 def stage_install(
213 213 source_dir: pathlib.Path, staging_dir: pathlib.Path, lower_case=False
214 214 ):
215 215 """Copy all files to be installed to a directory.
216 216
217 217 This allows packaging to simply walk a directory tree to find source
218 218 files.
219 219 """
220 220 if lower_case:
221 221 rules = []
222 222 for source, dest in STAGING_RULES:
223 223 # Only lower directory names.
224 224 if '/' in dest:
225 225 parent, leaf = dest.rsplit('/', 1)
226 226 dest = '%s/%s' % (parent.lower(), leaf)
227 227 rules.append((source, dest))
228 228 else:
229 229 rules = STAGING_RULES
230 230
231 231 process_install_rules(rules, source_dir, staging_dir)
232 232
233 233 # Write out a default editor.rc file to configure notepad as the
234 234 # default editor.
235 235 with (staging_dir / 'defaultrc' / 'editor.rc').open(
236 236 'w', encoding='utf-8'
237 237 ) as fh:
238 238 fh.write('[ui]\neditor = notepad\n')
239 239
240 240 # Purge any files we don't want to be there.
241 241 for f in STAGING_EXCLUDES:
242 242 p = staging_dir / f
243 243 if p.exists():
244 244 print('removing %s' % p)
245 245 p.unlink()
General Comments 0
You need to be logged in to leave comments. Login now