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