##// END OF EJS Templates
packaging: extract py2exe functionality to own module...
Gregory Szorc -
r42081:a2e191a9 default
parent child Browse files
Show More
@@ -1,158 +1,70 b''
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 .py2exe import (
16 download_entry,
16 build_py2exe,
17 )
17 )
18 from .util import (
18 from .util import (
19 extract_tar_to_directory,
20 extract_zip_to_directory,
21 find_vc_runtime_files,
19 find_vc_runtime_files,
22 python_exe_info,
23 )
20 )
24
21
25
22
26 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
23 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
27 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
24 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
28 version=None):
25 version=None):
29 """Build the Inno installer.
26 """Build the Inno installer.
30
27
31 Build files will be placed in ``build_dir``.
28 Build files will be placed in ``build_dir``.
32
29
33 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
30 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
34 for finding the Python 2.7 toolchain. So, we require the environment
31 for finding the Python 2.7 toolchain. So, we require the environment
35 to already be configured with an active toolchain.
32 to already be configured with an active toolchain.
36 """
33 """
37 if not iscc_exe.exists():
34 if not iscc_exe.exists():
38 raise Exception('%s does not exist' % iscc_exe)
35 raise Exception('%s does not exist' % iscc_exe)
39
36
40 if 'VCINSTALLDIR' not in os.environ:
37 vc_x64 = r'\x64' in os.environ.get('LIB', '')
41 raise Exception('not running from a Visual C++ build environment; '
42 'execute the "Visual C++ <version> Command Prompt" '
43 'application shortcut or a vcsvarsall.bat file')
44
45 # Identity x86/x64 and validate the environment matches the Python
46 # architecture.
47 vc_x64 = r'\x64' in os.environ['LIB']
48
49 py_info = python_exe_info(python_exe)
50
51 if vc_x64:
52 if py_info['arch'] != '64bit':
53 raise Exception('architecture mismatch: Visual C++ environment '
54 'is configured for 64-bit but Python is 32-bit')
55 else:
56 if py_info['arch'] != '32bit':
57 raise Exception('architecture mismatch: Visual C++ environment '
58 'is configured for 32-bit but Python is 64-bit')
59
60 if py_info['py3']:
61 raise Exception('Only Python 2 is currently supported')
62
63 build_dir.mkdir(exist_ok=True)
64
65 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
66 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
67 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
68 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
69
70 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
71
72 gettext_root = build_dir / (
73 'gettext-win-%s' % gettext_entry['version'])
74
75 if not gettext_root.exists():
76 extract_zip_to_directory(gettext_pkg, gettext_root)
77 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
78
79 # This assumes Python 2. We don't need virtualenv on Python 3.
80 virtualenv_src_path = build_dir / (
81 'virtualenv-%s' % virtualenv_entry['version'])
82 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
83
84 if not virtualenv_src_path.exists():
85 extract_tar_to_directory(virtualenv_pkg, build_dir)
86
87 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
88
89 if not py2exe_source_path.exists():
90 extract_zip_to_directory(py2exe_pkg, build_dir)
91
92 if not venv_path.exists():
93 print('creating virtualenv with dependencies')
94 subprocess.run(
95 [str(python_exe), str(virtualenv_py), str(venv_path)],
96 check=True)
97
98 venv_python = venv_path / 'Scripts' / 'python.exe'
99 venv_pip = venv_path / 'Scripts' / 'pip.exe'
100
38
101 requirements_txt = (source_dir / 'contrib' / 'packaging' /
39 requirements_txt = (source_dir / 'contrib' / 'packaging' /
102 'inno' / 'requirements.txt')
40 'inno' / 'requirements.txt')
103 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
104 check=True)
105
41
106 # Force distutils to use VC++ settings from environment, which was
42 build_py2exe(source_dir, build_dir, python_exe, 'inno',
107 # validated above.
43 requirements_txt)
108 env = dict(os.environ)
109 env['DISTUTILS_USE_SDK'] = '1'
110 env['MSSdk'] = '1'
111
112 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
113 if not py2exe_py_path.exists():
114 print('building py2exe')
115 subprocess.run([str(venv_python), 'setup.py', 'install'],
116 cwd=py2exe_source_path,
117 env=env,
118 check=True)
119
120 # Register location of msgfmt and other binaries.
121 env['PATH'] = '%s%s%s' % (
122 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
123
124 print('building Mercurial')
125 subprocess.run(
126 [str(venv_python), 'setup.py',
127 'py2exe', '-b', '3' if vc_x64 else '2',
128 'build_doc', '--html'],
129 cwd=str(source_dir),
130 env=env,
131 check=True)
132
44
133 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
45 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
134 for f in find_vc_runtime_files(vc_x64):
46 for f in find_vc_runtime_files(vc_x64):
135 if f.name.endswith('.manifest'):
47 if f.name.endswith('.manifest'):
136 basename = 'Microsoft.VC90.CRT.manifest'
48 basename = 'Microsoft.VC90.CRT.manifest'
137 else:
49 else:
138 basename = f.name
50 basename = f.name
139
51
140 dest_path = source_dir / 'dist' / basename
52 dest_path = source_dir / 'dist' / basename
141
53
142 print('copying %s to %s' % (f, dest_path))
54 print('copying %s to %s' % (f, dest_path))
143 shutil.copyfile(f, dest_path)
55 shutil.copyfile(f, dest_path)
144
56
145 print('creating installer')
57 print('creating installer')
146
58
147 args = [str(iscc_exe)]
59 args = [str(iscc_exe)]
148
60
149 if vc_x64:
61 if vc_x64:
150 args.append('/dARCH=x64')
62 args.append('/dARCH=x64')
151
63
152 if version:
64 if version:
153 args.append('/dVERSION=%s' % version)
65 args.append('/dVERSION=%s' % version)
154
66
155 args.append('/Odist')
67 args.append('/Odist')
156 args.append('contrib/packaging/inno/mercurial.iss')
68 args.append('contrib/packaging/inno/mercurial.iss')
157
69
158 subprocess.run(args, cwd=str(source_dir), check=True)
70 subprocess.run(args, cwd=str(source_dir), check=True)
@@ -1,158 +1,125 b''
1 # inno.py - Inno Setup functionality.
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 shutil
13 import subprocess
12 import subprocess
14
13
15 from .downloads import (
14 from .downloads import (
16 download_entry,
15 download_entry,
17 )
16 )
18 from .util import (
17 from .util import (
19 extract_tar_to_directory,
18 extract_tar_to_directory,
20 extract_zip_to_directory,
19 extract_zip_to_directory,
21 find_vc_runtime_files,
22 python_exe_info,
20 python_exe_info,
23 )
21 )
24
22
25
23
26 def build(source_dir: pathlib.Path, build_dir: pathlib.Path,
24 def build_py2exe(source_dir: pathlib.Path, build_dir: pathlib.Path,
27 python_exe: pathlib.Path, iscc_exe: pathlib.Path,
25 python_exe: pathlib.Path, build_name: str,
28 version=None):
26 venv_requirements_txt: pathlib.Path):
29 """Build the Inno installer.
27 """Build Mercurial with py2exe.
30
28
31 Build files will be placed in ``build_dir``.
29 Build files will be placed in ``build_dir``.
32
30
33 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
31 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
34 for finding the Python 2.7 toolchain. So, we require the environment
32 for finding the Python 2.7 toolchain. So, we require the environment
35 to already be configured with an active toolchain.
33 to already be configured with an active toolchain.
36 """
34 """
37 if not iscc_exe.exists():
38 raise Exception('%s does not exist' % iscc_exe)
39
40 if 'VCINSTALLDIR' not in os.environ:
35 if 'VCINSTALLDIR' not in os.environ:
41 raise Exception('not running from a Visual C++ build environment; '
36 raise Exception('not running from a Visual C++ build environment; '
42 'execute the "Visual C++ <version> Command Prompt" '
37 'execute the "Visual C++ <version> Command Prompt" '
43 'application shortcut or a vcsvarsall.bat file')
38 'application shortcut or a vcsvarsall.bat file')
44
39
45 # Identity x86/x64 and validate the environment matches the Python
40 # Identity x86/x64 and validate the environment matches the Python
46 # architecture.
41 # architecture.
47 vc_x64 = r'\x64' in os.environ['LIB']
42 vc_x64 = r'\x64' in os.environ['LIB']
48
43
49 py_info = python_exe_info(python_exe)
44 py_info = python_exe_info(python_exe)
50
45
51 if vc_x64:
46 if vc_x64:
52 if py_info['arch'] != '64bit':
47 if py_info['arch'] != '64bit':
53 raise Exception('architecture mismatch: Visual C++ environment '
48 raise Exception('architecture mismatch: Visual C++ environment '
54 'is configured for 64-bit but Python is 32-bit')
49 'is configured for 64-bit but Python is 32-bit')
55 else:
50 else:
56 if py_info['arch'] != '32bit':
51 if py_info['arch'] != '32bit':
57 raise Exception('architecture mismatch: Visual C++ environment '
52 raise Exception('architecture mismatch: Visual C++ environment '
58 'is configured for 32-bit but Python is 64-bit')
53 'is configured for 32-bit but Python is 64-bit')
59
54
60 if py_info['py3']:
55 if py_info['py3']:
61 raise Exception('Only Python 2 is currently supported')
56 raise Exception('Only Python 2 is currently supported')
62
57
63 build_dir.mkdir(exist_ok=True)
58 build_dir.mkdir(exist_ok=True)
64
59
65 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
60 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
66 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
61 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
67 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
62 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
68 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
63 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
69
64
70 venv_path = build_dir / ('venv-inno-%s' % ('x64' if vc_x64 else 'x86'))
65 venv_path = build_dir / ('venv-%s-%s' % (build_name,
66 'x64' if vc_x64 else 'x86'))
71
67
72 gettext_root = build_dir / (
68 gettext_root = build_dir / (
73 'gettext-win-%s' % gettext_entry['version'])
69 'gettext-win-%s' % gettext_entry['version'])
74
70
75 if not gettext_root.exists():
71 if not gettext_root.exists():
76 extract_zip_to_directory(gettext_pkg, gettext_root)
72 extract_zip_to_directory(gettext_pkg, gettext_root)
77 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
73 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
78
74
79 # This assumes Python 2. We don't need virtualenv on Python 3.
75 # This assumes Python 2. We don't need virtualenv on Python 3.
80 virtualenv_src_path = build_dir / (
76 virtualenv_src_path = build_dir / (
81 'virtualenv-%s' % virtualenv_entry['version'])
77 'virtualenv-%s' % virtualenv_entry['version'])
82 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
78 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
83
79
84 if not virtualenv_src_path.exists():
80 if not virtualenv_src_path.exists():
85 extract_tar_to_directory(virtualenv_pkg, build_dir)
81 extract_tar_to_directory(virtualenv_pkg, build_dir)
86
82
87 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
83 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
88
84
89 if not py2exe_source_path.exists():
85 if not py2exe_source_path.exists():
90 extract_zip_to_directory(py2exe_pkg, build_dir)
86 extract_zip_to_directory(py2exe_pkg, build_dir)
91
87
92 if not venv_path.exists():
88 if not venv_path.exists():
93 print('creating virtualenv with dependencies')
89 print('creating virtualenv with dependencies')
94 subprocess.run(
90 subprocess.run(
95 [str(python_exe), str(virtualenv_py), str(venv_path)],
91 [str(python_exe), str(virtualenv_py), str(venv_path)],
96 check=True)
92 check=True)
97
93
98 venv_python = venv_path / 'Scripts' / 'python.exe'
94 venv_python = venv_path / 'Scripts' / 'python.exe'
99 venv_pip = venv_path / 'Scripts' / 'pip.exe'
95 venv_pip = venv_path / 'Scripts' / 'pip.exe'
100
96
101 requirements_txt = (source_dir / 'contrib' / 'packaging' /
97 subprocess.run([str(venv_pip), 'install', '-r', str(venv_requirements_txt)],
102 'inno' / 'requirements.txt')
103 subprocess.run([str(venv_pip), 'install', '-r', str(requirements_txt)],
104 check=True)
98 check=True)
105
99
106 # Force distutils to use VC++ settings from environment, which was
100 # Force distutils to use VC++ settings from environment, which was
107 # validated above.
101 # validated above.
108 env = dict(os.environ)
102 env = dict(os.environ)
109 env['DISTUTILS_USE_SDK'] = '1'
103 env['DISTUTILS_USE_SDK'] = '1'
110 env['MSSdk'] = '1'
104 env['MSSdk'] = '1'
111
105
112 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
106 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
113 if not py2exe_py_path.exists():
107 if not py2exe_py_path.exists():
114 print('building py2exe')
108 print('building py2exe')
115 subprocess.run([str(venv_python), 'setup.py', 'install'],
109 subprocess.run([str(venv_python), 'setup.py', 'install'],
116 cwd=py2exe_source_path,
110 cwd=py2exe_source_path,
117 env=env,
111 env=env,
118 check=True)
112 check=True)
119
113
120 # Register location of msgfmt and other binaries.
114 # Register location of msgfmt and other binaries.
121 env['PATH'] = '%s%s%s' % (
115 env['PATH'] = '%s%s%s' % (
122 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
116 env['PATH'], os.pathsep, str(gettext_root / 'bin'))
123
117
124 print('building Mercurial')
118 print('building Mercurial')
125 subprocess.run(
119 subprocess.run(
126 [str(venv_python), 'setup.py',
120 [str(venv_python), 'setup.py',
127 'py2exe', '-b', '3' if vc_x64 else '2',
121 'py2exe', '-b', '3' if vc_x64 else '2',
128 'build_doc', '--html'],
122 'build_doc', '--html'],
129 cwd=str(source_dir),
123 cwd=str(source_dir),
130 env=env,
124 env=env,
131 check=True)
125 check=True)
132
133 # hg.exe depends on VC9 runtime DLLs. Copy those into place.
134 for f in find_vc_runtime_files(vc_x64):
135 if f.name.endswith('.manifest'):
136 basename = 'Microsoft.VC90.CRT.manifest'
137 else:
138 basename = f.name
139
140 dest_path = source_dir / 'dist' / basename
141
142 print('copying %s to %s' % (f, dest_path))
143 shutil.copyfile(f, dest_path)
144
145 print('creating installer')
146
147 args = [str(iscc_exe)]
148
149 if vc_x64:
150 args.append('/dARCH=x64')
151
152 if version:
153 args.append('/dVERSION=%s' % version)
154
155 args.append('/Odist')
156 args.append('contrib/packaging/inno/mercurial.iss')
157
158 subprocess.run(args, cwd=str(source_dir), check=True)
@@ -1,73 +1,74 b''
1 #require test-repo
1 #require test-repo
2
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
3 $ . "$TESTDIR/helpers-testrepo.sh"
4 $ check_code="$TESTDIR"/../contrib/check-code.py
4 $ check_code="$TESTDIR"/../contrib/check-code.py
5 $ cd "$TESTDIR"/..
5 $ cd "$TESTDIR"/..
6
6
7 New errors are not allowed. Warnings are strongly discouraged.
7 New errors are not allowed. Warnings are strongly discouraged.
8 (The writing "no-che?k-code" is for not skipping this file when checking.)
8 (The writing "no-che?k-code" is for not skipping this file when checking.)
9
9
10 $ testrepohg locate \
10 $ testrepohg locate \
11 > -X contrib/python-zstandard \
11 > -X contrib/python-zstandard \
12 > -X hgext/fsmonitor/pywatchman \
12 > -X hgext/fsmonitor/pywatchman \
13 > -X mercurial/thirdparty \
13 > -X mercurial/thirdparty \
14 > | sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
14 > | sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
15 Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)
15 Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)
16 Skipping contrib/packaging/hgpackaging/inno.py it has no-che?k-code (glob)
16 Skipping contrib/packaging/hgpackaging/inno.py it has no-che?k-code (glob)
17 Skipping contrib/packaging/hgpackaging/py2exe.py it has no-che?k-code (glob)
17 Skipping contrib/packaging/hgpackaging/util.py it has no-che?k-code (glob)
18 Skipping contrib/packaging/hgpackaging/util.py it has no-che?k-code (glob)
18 Skipping contrib/packaging/inno/build.py it has no-che?k-code (glob)
19 Skipping contrib/packaging/inno/build.py it has no-che?k-code (glob)
19 Skipping i18n/polib.py it has no-che?k-code (glob)
20 Skipping i18n/polib.py it has no-che?k-code (glob)
20 Skipping mercurial/statprof.py it has no-che?k-code (glob)
21 Skipping mercurial/statprof.py it has no-che?k-code (glob)
21 Skipping tests/badserverext.py it has no-che?k-code (glob)
22 Skipping tests/badserverext.py it has no-che?k-code (glob)
22
23
23 @commands in debugcommands.py should be in alphabetical order.
24 @commands in debugcommands.py should be in alphabetical order.
24
25
25 >>> import re
26 >>> import re
26 >>> commands = []
27 >>> commands = []
27 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
28 >>> with open('mercurial/debugcommands.py', 'rb') as fh:
28 ... for line in fh:
29 ... for line in fh:
29 ... m = re.match(br"^@command\('([a-z]+)", line)
30 ... m = re.match(br"^@command\('([a-z]+)", line)
30 ... if m:
31 ... if m:
31 ... commands.append(m.group(1))
32 ... commands.append(m.group(1))
32 >>> scommands = list(sorted(commands))
33 >>> scommands = list(sorted(commands))
33 >>> for i, command in enumerate(scommands):
34 >>> for i, command in enumerate(scommands):
34 ... if command != commands[i]:
35 ... if command != commands[i]:
35 ... print('commands in debugcommands.py not sorted; first differing '
36 ... print('commands in debugcommands.py not sorted; first differing '
36 ... 'command is %s; expected %s' % (commands[i], command))
37 ... 'command is %s; expected %s' % (commands[i], command))
37 ... break
38 ... break
38
39
39 Prevent adding new files in the root directory accidentally.
40 Prevent adding new files in the root directory accidentally.
40
41
41 $ testrepohg files 'glob:*'
42 $ testrepohg files 'glob:*'
42 .arcconfig
43 .arcconfig
43 .clang-format
44 .clang-format
44 .editorconfig
45 .editorconfig
45 .hgignore
46 .hgignore
46 .hgsigs
47 .hgsigs
47 .hgtags
48 .hgtags
48 .jshintrc
49 .jshintrc
49 CONTRIBUTING
50 CONTRIBUTING
50 CONTRIBUTORS
51 CONTRIBUTORS
51 COPYING
52 COPYING
52 Makefile
53 Makefile
53 README.rst
54 README.rst
54 hg
55 hg
55 hgeditor
56 hgeditor
56 hgweb.cgi
57 hgweb.cgi
57 setup.py
58 setup.py
58
59
59 Prevent adding modules which could be shadowed by ancient .so/.dylib.
60 Prevent adding modules which could be shadowed by ancient .so/.dylib.
60
61
61 $ testrepohg files \
62 $ testrepohg files \
62 > mercurial/base85.py \
63 > mercurial/base85.py \
63 > mercurial/bdiff.py \
64 > mercurial/bdiff.py \
64 > mercurial/diffhelpers.py \
65 > mercurial/diffhelpers.py \
65 > mercurial/mpatch.py \
66 > mercurial/mpatch.py \
66 > mercurial/osutil.py \
67 > mercurial/osutil.py \
67 > mercurial/parsers.py \
68 > mercurial/parsers.py \
68 > mercurial/zstd.py
69 > mercurial/zstd.py
69 [1]
70 [1]
70
71
71 Keep python3 tests sorted:
72 Keep python3 tests sorted:
72 $ sort < contrib/python3-whitelist > $TESTTMP/py3sorted
73 $ sort < contrib/python3-whitelist > $TESTTMP/py3sorted
73 $ cmp contrib/python3-whitelist $TESTTMP/py3sorted || echo 'Please sort passing tests!'
74 $ cmp contrib/python3-whitelist $TESTTMP/py3sorted || echo 'Please sort passing tests!'
General Comments 0
You need to be logged in to leave comments. Login now