##// END OF EJS Templates
setup: include vendored 3rd party type stubs...
Matt Harbison -
r50550:54421ef8 default
parent child Browse files
Show More
@@ -1,1788 +1,1792 b''
1 #
1 #
2 # This is the mercurial setup script.
2 # This is the mercurial setup script.
3 #
3 #
4 # 'python setup.py install', or
4 # 'python setup.py install', or
5 # 'python setup.py --help' for more options
5 # 'python setup.py --help' for more options
6 import os
6 import os
7
7
8 # Mercurial can't work on 3.6.0 or 3.6.1 due to a bug in % formatting
8 # Mercurial can't work on 3.6.0 or 3.6.1 due to a bug in % formatting
9 # in bytestrings.
9 # in bytestrings.
10 supportedpy = ','.join(
10 supportedpy = ','.join(
11 [
11 [
12 '>=3.6.2',
12 '>=3.6.2',
13 ]
13 ]
14 )
14 )
15
15
16 import sys, platform
16 import sys, platform
17 import sysconfig
17 import sysconfig
18
18
19
19
20 def sysstr(s):
20 def sysstr(s):
21 return s.decode('latin-1')
21 return s.decode('latin-1')
22
22
23
23
24 import ssl
24 import ssl
25
25
26 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
26 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
27 # 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
27 # 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
28 # (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
28 # (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
29 # were defined only if compiled against a OpenSSL version with TLS 1.1 / 1.2
29 # were defined only if compiled against a OpenSSL version with TLS 1.1 / 1.2
30 # support. At the mentioned commit, they were unconditionally defined.
30 # support. At the mentioned commit, they were unconditionally defined.
31 _notset = object()
31 _notset = object()
32 has_tlsv1_1 = getattr(ssl, 'HAS_TLSv1_1', _notset)
32 has_tlsv1_1 = getattr(ssl, 'HAS_TLSv1_1', _notset)
33 if has_tlsv1_1 is _notset:
33 if has_tlsv1_1 is _notset:
34 has_tlsv1_1 = getattr(ssl, 'PROTOCOL_TLSv1_1', _notset) is not _notset
34 has_tlsv1_1 = getattr(ssl, 'PROTOCOL_TLSv1_1', _notset) is not _notset
35 has_tlsv1_2 = getattr(ssl, 'HAS_TLSv1_2', _notset)
35 has_tlsv1_2 = getattr(ssl, 'HAS_TLSv1_2', _notset)
36 if has_tlsv1_2 is _notset:
36 if has_tlsv1_2 is _notset:
37 has_tlsv1_2 = getattr(ssl, 'PROTOCOL_TLSv1_2', _notset) is not _notset
37 has_tlsv1_2 = getattr(ssl, 'PROTOCOL_TLSv1_2', _notset) is not _notset
38 if not (has_tlsv1_1 or has_tlsv1_2):
38 if not (has_tlsv1_1 or has_tlsv1_2):
39 error = """
39 error = """
40 The `ssl` module does not advertise support for TLS 1.1 or TLS 1.2.
40 The `ssl` module does not advertise support for TLS 1.1 or TLS 1.2.
41 Please make sure that your Python installation was compiled against an OpenSSL
41 Please make sure that your Python installation was compiled against an OpenSSL
42 version enabling these features (likely this requires the OpenSSL version to
42 version enabling these features (likely this requires the OpenSSL version to
43 be at least 1.0.1).
43 be at least 1.0.1).
44 """
44 """
45 print(error, file=sys.stderr)
45 print(error, file=sys.stderr)
46 sys.exit(1)
46 sys.exit(1)
47
47
48 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
48 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
49
49
50 # Solaris Python packaging brain damage
50 # Solaris Python packaging brain damage
51 try:
51 try:
52 import hashlib
52 import hashlib
53
53
54 sha = hashlib.sha1()
54 sha = hashlib.sha1()
55 except ImportError:
55 except ImportError:
56 try:
56 try:
57 import sha
57 import sha
58
58
59 sha.sha # silence unused import warning
59 sha.sha # silence unused import warning
60 except ImportError:
60 except ImportError:
61 raise SystemExit(
61 raise SystemExit(
62 "Couldn't import standard hashlib (incomplete Python install)."
62 "Couldn't import standard hashlib (incomplete Python install)."
63 )
63 )
64
64
65 try:
65 try:
66 import zlib
66 import zlib
67
67
68 zlib.compressobj # silence unused import warning
68 zlib.compressobj # silence unused import warning
69 except ImportError:
69 except ImportError:
70 raise SystemExit(
70 raise SystemExit(
71 "Couldn't import standard zlib (incomplete Python install)."
71 "Couldn't import standard zlib (incomplete Python install)."
72 )
72 )
73
73
74 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
74 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
75 isironpython = False
75 isironpython = False
76 try:
76 try:
77 isironpython = (
77 isironpython = (
78 platform.python_implementation().lower().find("ironpython") != -1
78 platform.python_implementation().lower().find("ironpython") != -1
79 )
79 )
80 except AttributeError:
80 except AttributeError:
81 pass
81 pass
82
82
83 if isironpython:
83 if isironpython:
84 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
84 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
85 else:
85 else:
86 try:
86 try:
87 import bz2
87 import bz2
88
88
89 bz2.BZ2Compressor # silence unused import warning
89 bz2.BZ2Compressor # silence unused import warning
90 except ImportError:
90 except ImportError:
91 raise SystemExit(
91 raise SystemExit(
92 "Couldn't import standard bz2 (incomplete Python install)."
92 "Couldn't import standard bz2 (incomplete Python install)."
93 )
93 )
94
94
95 ispypy = "PyPy" in sys.version
95 ispypy = "PyPy" in sys.version
96
96
97 import ctypes
97 import ctypes
98 import stat, subprocess, time
98 import stat, subprocess, time
99 import re
99 import re
100 import shutil
100 import shutil
101 import tempfile
101 import tempfile
102
102
103 # We have issues with setuptools on some platforms and builders. Until
103 # We have issues with setuptools on some platforms and builders. Until
104 # those are resolved, setuptools is opt-in except for platforms where
104 # those are resolved, setuptools is opt-in except for platforms where
105 # we don't have issues.
105 # we don't have issues.
106 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
106 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
107 if issetuptools:
107 if issetuptools:
108 from setuptools import setup
108 from setuptools import setup
109 else:
109 else:
110 from distutils.core import setup
110 from distutils.core import setup
111 from distutils.ccompiler import new_compiler
111 from distutils.ccompiler import new_compiler
112 from distutils.core import Command, Extension
112 from distutils.core import Command, Extension
113 from distutils.dist import Distribution
113 from distutils.dist import Distribution
114 from distutils.command.build import build
114 from distutils.command.build import build
115 from distutils.command.build_ext import build_ext
115 from distutils.command.build_ext import build_ext
116 from distutils.command.build_py import build_py
116 from distutils.command.build_py import build_py
117 from distutils.command.build_scripts import build_scripts
117 from distutils.command.build_scripts import build_scripts
118 from distutils.command.install import install
118 from distutils.command.install import install
119 from distutils.command.install_lib import install_lib
119 from distutils.command.install_lib import install_lib
120 from distutils.command.install_scripts import install_scripts
120 from distutils.command.install_scripts import install_scripts
121 from distutils import log
121 from distutils import log
122 from distutils.spawn import spawn, find_executable
122 from distutils.spawn import spawn, find_executable
123 from distutils import file_util
123 from distutils import file_util
124 from distutils.errors import (
124 from distutils.errors import (
125 CCompilerError,
125 CCompilerError,
126 DistutilsError,
126 DistutilsError,
127 DistutilsExecError,
127 DistutilsExecError,
128 )
128 )
129 from distutils.sysconfig import get_python_inc, get_config_var
129 from distutils.sysconfig import get_python_inc, get_config_var
130 from distutils.version import StrictVersion
130 from distutils.version import StrictVersion
131
131
132 # Explain to distutils.StrictVersion how our release candidates are versioned
132 # Explain to distutils.StrictVersion how our release candidates are versioned
133 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
133 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
134
134
135
135
136 def write_if_changed(path, content):
136 def write_if_changed(path, content):
137 """Write content to a file iff the content hasn't changed."""
137 """Write content to a file iff the content hasn't changed."""
138 if os.path.exists(path):
138 if os.path.exists(path):
139 with open(path, 'rb') as fh:
139 with open(path, 'rb') as fh:
140 current = fh.read()
140 current = fh.read()
141 else:
141 else:
142 current = b''
142 current = b''
143
143
144 if current != content:
144 if current != content:
145 with open(path, 'wb') as fh:
145 with open(path, 'wb') as fh:
146 fh.write(content)
146 fh.write(content)
147
147
148
148
149 scripts = ['hg']
149 scripts = ['hg']
150 if os.name == 'nt':
150 if os.name == 'nt':
151 # We remove hg.bat if we are able to build hg.exe.
151 # We remove hg.bat if we are able to build hg.exe.
152 scripts.append('contrib/win32/hg.bat')
152 scripts.append('contrib/win32/hg.bat')
153
153
154
154
155 def cancompile(cc, code):
155 def cancompile(cc, code):
156 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
156 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
157 devnull = oldstderr = None
157 devnull = oldstderr = None
158 try:
158 try:
159 fname = os.path.join(tmpdir, 'testcomp.c')
159 fname = os.path.join(tmpdir, 'testcomp.c')
160 f = open(fname, 'w')
160 f = open(fname, 'w')
161 f.write(code)
161 f.write(code)
162 f.close()
162 f.close()
163 # Redirect stderr to /dev/null to hide any error messages
163 # Redirect stderr to /dev/null to hide any error messages
164 # from the compiler.
164 # from the compiler.
165 # This will have to be changed if we ever have to check
165 # This will have to be changed if we ever have to check
166 # for a function on Windows.
166 # for a function on Windows.
167 devnull = open('/dev/null', 'w')
167 devnull = open('/dev/null', 'w')
168 oldstderr = os.dup(sys.stderr.fileno())
168 oldstderr = os.dup(sys.stderr.fileno())
169 os.dup2(devnull.fileno(), sys.stderr.fileno())
169 os.dup2(devnull.fileno(), sys.stderr.fileno())
170 objects = cc.compile([fname], output_dir=tmpdir)
170 objects = cc.compile([fname], output_dir=tmpdir)
171 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
171 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
172 return True
172 return True
173 except Exception:
173 except Exception:
174 return False
174 return False
175 finally:
175 finally:
176 if oldstderr is not None:
176 if oldstderr is not None:
177 os.dup2(oldstderr, sys.stderr.fileno())
177 os.dup2(oldstderr, sys.stderr.fileno())
178 if devnull is not None:
178 if devnull is not None:
179 devnull.close()
179 devnull.close()
180 shutil.rmtree(tmpdir)
180 shutil.rmtree(tmpdir)
181
181
182
182
183 # simplified version of distutils.ccompiler.CCompiler.has_function
183 # simplified version of distutils.ccompiler.CCompiler.has_function
184 # that actually removes its temporary files.
184 # that actually removes its temporary files.
185 def hasfunction(cc, funcname):
185 def hasfunction(cc, funcname):
186 code = 'int main(void) { %s(); }\n' % funcname
186 code = 'int main(void) { %s(); }\n' % funcname
187 return cancompile(cc, code)
187 return cancompile(cc, code)
188
188
189
189
190 def hasheader(cc, headername):
190 def hasheader(cc, headername):
191 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
191 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
192 return cancompile(cc, code)
192 return cancompile(cc, code)
193
193
194
194
195 # py2exe needs to be installed to work
195 # py2exe needs to be installed to work
196 try:
196 try:
197 import py2exe
197 import py2exe
198
198
199 py2exe.patch_distutils()
199 py2exe.patch_distutils()
200 py2exeloaded = True
200 py2exeloaded = True
201 # import py2exe's patched Distribution class
201 # import py2exe's patched Distribution class
202 from distutils.core import Distribution
202 from distutils.core import Distribution
203 except ImportError:
203 except ImportError:
204 py2exeloaded = False
204 py2exeloaded = False
205
205
206
206
207 def runcmd(cmd, env, cwd=None):
207 def runcmd(cmd, env, cwd=None):
208 p = subprocess.Popen(
208 p = subprocess.Popen(
209 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
209 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
210 )
210 )
211 out, err = p.communicate()
211 out, err = p.communicate()
212 return p.returncode, out, err
212 return p.returncode, out, err
213
213
214
214
215 class hgcommand:
215 class hgcommand:
216 def __init__(self, cmd, env):
216 def __init__(self, cmd, env):
217 self.cmd = cmd
217 self.cmd = cmd
218 self.env = env
218 self.env = env
219
219
220 def run(self, args):
220 def run(self, args):
221 cmd = self.cmd + args
221 cmd = self.cmd + args
222 returncode, out, err = runcmd(cmd, self.env)
222 returncode, out, err = runcmd(cmd, self.env)
223 err = filterhgerr(err)
223 err = filterhgerr(err)
224 if err or returncode != 0:
224 if err or returncode != 0:
225 print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
225 print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
226 print(err, file=sys.stderr)
226 print(err, file=sys.stderr)
227 return b''
227 return b''
228 return out
228 return out
229
229
230
230
231 def filterhgerr(err):
231 def filterhgerr(err):
232 # If root is executing setup.py, but the repository is owned by
232 # If root is executing setup.py, but the repository is owned by
233 # another user (as in "sudo python setup.py install") we will get
233 # another user (as in "sudo python setup.py install") we will get
234 # trust warnings since the .hg/hgrc file is untrusted. That is
234 # trust warnings since the .hg/hgrc file is untrusted. That is
235 # fine, we don't want to load it anyway. Python may warn about
235 # fine, we don't want to load it anyway. Python may warn about
236 # a missing __init__.py in mercurial/locale, we also ignore that.
236 # a missing __init__.py in mercurial/locale, we also ignore that.
237 err = [
237 err = [
238 e
238 e
239 for e in err.splitlines()
239 for e in err.splitlines()
240 if (
240 if (
241 not e.startswith(b'not trusting file')
241 not e.startswith(b'not trusting file')
242 and not e.startswith(b'warning: Not importing')
242 and not e.startswith(b'warning: Not importing')
243 and not e.startswith(b'obsolete feature not enabled')
243 and not e.startswith(b'obsolete feature not enabled')
244 and not e.startswith(b'*** failed to import extension')
244 and not e.startswith(b'*** failed to import extension')
245 and not e.startswith(b'devel-warn:')
245 and not e.startswith(b'devel-warn:')
246 and not (
246 and not (
247 e.startswith(b'(third party extension')
247 e.startswith(b'(third party extension')
248 and e.endswith(b'or newer of Mercurial; disabling)')
248 and e.endswith(b'or newer of Mercurial; disabling)')
249 )
249 )
250 )
250 )
251 ]
251 ]
252 return b'\n'.join(b' ' + e for e in err)
252 return b'\n'.join(b' ' + e for e in err)
253
253
254
254
255 def findhg():
255 def findhg():
256 """Try to figure out how we should invoke hg for examining the local
256 """Try to figure out how we should invoke hg for examining the local
257 repository contents.
257 repository contents.
258
258
259 Returns an hgcommand object."""
259 Returns an hgcommand object."""
260 # By default, prefer the "hg" command in the user's path. This was
260 # By default, prefer the "hg" command in the user's path. This was
261 # presumably the hg command that the user used to create this repository.
261 # presumably the hg command that the user used to create this repository.
262 #
262 #
263 # This repository may require extensions or other settings that would not
263 # This repository may require extensions or other settings that would not
264 # be enabled by running the hg script directly from this local repository.
264 # be enabled by running the hg script directly from this local repository.
265 hgenv = os.environ.copy()
265 hgenv = os.environ.copy()
266 # Use HGPLAIN to disable hgrc settings that would change output formatting,
266 # Use HGPLAIN to disable hgrc settings that would change output formatting,
267 # and disable localization for the same reasons.
267 # and disable localization for the same reasons.
268 hgenv['HGPLAIN'] = '1'
268 hgenv['HGPLAIN'] = '1'
269 hgenv['LANGUAGE'] = 'C'
269 hgenv['LANGUAGE'] = 'C'
270 hgcmd = ['hg']
270 hgcmd = ['hg']
271 # Run a simple "hg log" command just to see if using hg from the user's
271 # Run a simple "hg log" command just to see if using hg from the user's
272 # path works and can successfully interact with this repository. Windows
272 # path works and can successfully interact with this repository. Windows
273 # gives precedence to hg.exe in the current directory, so fall back to the
273 # gives precedence to hg.exe in the current directory, so fall back to the
274 # python invocation of local hg, where pythonXY.dll can always be found.
274 # python invocation of local hg, where pythonXY.dll can always be found.
275 check_cmd = ['log', '-r.', '-Ttest']
275 check_cmd = ['log', '-r.', '-Ttest']
276 if os.name != 'nt' or not os.path.exists("hg.exe"):
276 if os.name != 'nt' or not os.path.exists("hg.exe"):
277 try:
277 try:
278 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
278 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
279 except EnvironmentError:
279 except EnvironmentError:
280 retcode = -1
280 retcode = -1
281 if retcode == 0 and not filterhgerr(err):
281 if retcode == 0 and not filterhgerr(err):
282 return hgcommand(hgcmd, hgenv)
282 return hgcommand(hgcmd, hgenv)
283
283
284 # Fall back to trying the local hg installation.
284 # Fall back to trying the local hg installation.
285 hgenv = localhgenv()
285 hgenv = localhgenv()
286 hgcmd = [sys.executable, 'hg']
286 hgcmd = [sys.executable, 'hg']
287 try:
287 try:
288 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
288 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
289 except EnvironmentError:
289 except EnvironmentError:
290 retcode = -1
290 retcode = -1
291 if retcode == 0 and not filterhgerr(err):
291 if retcode == 0 and not filterhgerr(err):
292 return hgcommand(hgcmd, hgenv)
292 return hgcommand(hgcmd, hgenv)
293
293
294 raise SystemExit(
294 raise SystemExit(
295 'Unable to find a working hg binary to extract the '
295 'Unable to find a working hg binary to extract the '
296 'version from the repository tags'
296 'version from the repository tags'
297 )
297 )
298
298
299
299
300 def localhgenv():
300 def localhgenv():
301 """Get an environment dictionary to use for invoking or importing
301 """Get an environment dictionary to use for invoking or importing
302 mercurial from the local repository."""
302 mercurial from the local repository."""
303 # Execute hg out of this directory with a custom environment which takes
303 # Execute hg out of this directory with a custom environment which takes
304 # care to not use any hgrc files and do no localization.
304 # care to not use any hgrc files and do no localization.
305 env = {
305 env = {
306 'HGMODULEPOLICY': 'py',
306 'HGMODULEPOLICY': 'py',
307 'HGRCPATH': '',
307 'HGRCPATH': '',
308 'LANGUAGE': 'C',
308 'LANGUAGE': 'C',
309 'PATH': '',
309 'PATH': '',
310 } # make pypi modules that use os.environ['PATH'] happy
310 } # make pypi modules that use os.environ['PATH'] happy
311 if 'LD_LIBRARY_PATH' in os.environ:
311 if 'LD_LIBRARY_PATH' in os.environ:
312 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
312 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
313 if 'SystemRoot' in os.environ:
313 if 'SystemRoot' in os.environ:
314 # SystemRoot is required by Windows to load various DLLs. See:
314 # SystemRoot is required by Windows to load various DLLs. See:
315 # https://bugs.python.org/issue13524#msg148850
315 # https://bugs.python.org/issue13524#msg148850
316 env['SystemRoot'] = os.environ['SystemRoot']
316 env['SystemRoot'] = os.environ['SystemRoot']
317 return env
317 return env
318
318
319
319
320 version = ''
320 version = ''
321
321
322 if os.path.isdir('.hg'):
322 if os.path.isdir('.hg'):
323 hg = findhg()
323 hg = findhg()
324 cmd = ['log', '-r', '.', '--template', '{tags}\n']
324 cmd = ['log', '-r', '.', '--template', '{tags}\n']
325 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
325 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
326 hgid = sysstr(hg.run(['id', '-i'])).strip()
326 hgid = sysstr(hg.run(['id', '-i'])).strip()
327 if not hgid:
327 if not hgid:
328 # Bail out if hg is having problems interacting with this repository,
328 # Bail out if hg is having problems interacting with this repository,
329 # rather than falling through and producing a bogus version number.
329 # rather than falling through and producing a bogus version number.
330 # Continuing with an invalid version number will break extensions
330 # Continuing with an invalid version number will break extensions
331 # that define minimumhgversion.
331 # that define minimumhgversion.
332 raise SystemExit('Unable to determine hg version from local repository')
332 raise SystemExit('Unable to determine hg version from local repository')
333 if numerictags: # tag(s) found
333 if numerictags: # tag(s) found
334 version = numerictags[-1]
334 version = numerictags[-1]
335 if hgid.endswith('+'): # propagate the dirty status to the tag
335 if hgid.endswith('+'): # propagate the dirty status to the tag
336 version += '+'
336 version += '+'
337 else: # no tag found
337 else: # no tag found
338 ltagcmd = ['parents', '--template', '{latesttag}']
338 ltagcmd = ['parents', '--template', '{latesttag}']
339 ltag = sysstr(hg.run(ltagcmd))
339 ltag = sysstr(hg.run(ltagcmd))
340 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
340 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
341 changessince = len(hg.run(changessincecmd).splitlines())
341 changessince = len(hg.run(changessincecmd).splitlines())
342 version = '%s+hg%s.%s' % (ltag, changessince, hgid)
342 version = '%s+hg%s.%s' % (ltag, changessince, hgid)
343 if version.endswith('+'):
343 if version.endswith('+'):
344 version = version[:-1] + 'local' + time.strftime('%Y%m%d')
344 version = version[:-1] + 'local' + time.strftime('%Y%m%d')
345 elif os.path.exists('.hg_archival.txt'):
345 elif os.path.exists('.hg_archival.txt'):
346 kw = dict(
346 kw = dict(
347 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
347 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
348 )
348 )
349 if 'tag' in kw:
349 if 'tag' in kw:
350 version = kw['tag']
350 version = kw['tag']
351 elif 'latesttag' in kw:
351 elif 'latesttag' in kw:
352 if 'changessincelatesttag' in kw:
352 if 'changessincelatesttag' in kw:
353 version = (
353 version = (
354 '%(latesttag)s+hg%(changessincelatesttag)s.%(node).12s' % kw
354 '%(latesttag)s+hg%(changessincelatesttag)s.%(node).12s' % kw
355 )
355 )
356 else:
356 else:
357 version = '%(latesttag)s+hg%(latesttagdistance)s.%(node).12s' % kw
357 version = '%(latesttag)s+hg%(latesttagdistance)s.%(node).12s' % kw
358 else:
358 else:
359 version = '0+hg' + kw.get('node', '')[:12]
359 version = '0+hg' + kw.get('node', '')[:12]
360 elif os.path.exists('mercurial/__version__.py'):
360 elif os.path.exists('mercurial/__version__.py'):
361 with open('mercurial/__version__.py') as f:
361 with open('mercurial/__version__.py') as f:
362 data = f.read()
362 data = f.read()
363 version = re.search('version = b"(.*)"', data).group(1)
363 version = re.search('version = b"(.*)"', data).group(1)
364
364
365 if version:
365 if version:
366 versionb = version
366 versionb = version
367 if not isinstance(versionb, bytes):
367 if not isinstance(versionb, bytes):
368 versionb = versionb.encode('ascii')
368 versionb = versionb.encode('ascii')
369
369
370 write_if_changed(
370 write_if_changed(
371 'mercurial/__version__.py',
371 'mercurial/__version__.py',
372 b''.join(
372 b''.join(
373 [
373 [
374 b'# this file is autogenerated by setup.py\n'
374 b'# this file is autogenerated by setup.py\n'
375 b'version = b"%s"\n' % versionb,
375 b'version = b"%s"\n' % versionb,
376 ]
376 ]
377 ),
377 ),
378 )
378 )
379
379
380
380
381 class hgbuild(build):
381 class hgbuild(build):
382 # Insert hgbuildmo first so that files in mercurial/locale/ are found
382 # Insert hgbuildmo first so that files in mercurial/locale/ are found
383 # when build_py is run next.
383 # when build_py is run next.
384 sub_commands = [('build_mo', None)] + build.sub_commands
384 sub_commands = [('build_mo', None)] + build.sub_commands
385
385
386
386
387 class hgbuildmo(build):
387 class hgbuildmo(build):
388
388
389 description = "build translations (.mo files)"
389 description = "build translations (.mo files)"
390
390
391 def run(self):
391 def run(self):
392 if not find_executable('msgfmt'):
392 if not find_executable('msgfmt'):
393 self.warn(
393 self.warn(
394 "could not find msgfmt executable, no translations "
394 "could not find msgfmt executable, no translations "
395 "will be built"
395 "will be built"
396 )
396 )
397 return
397 return
398
398
399 podir = 'i18n'
399 podir = 'i18n'
400 if not os.path.isdir(podir):
400 if not os.path.isdir(podir):
401 self.warn("could not find %s/ directory" % podir)
401 self.warn("could not find %s/ directory" % podir)
402 return
402 return
403
403
404 join = os.path.join
404 join = os.path.join
405 for po in os.listdir(podir):
405 for po in os.listdir(podir):
406 if not po.endswith('.po'):
406 if not po.endswith('.po'):
407 continue
407 continue
408 pofile = join(podir, po)
408 pofile = join(podir, po)
409 modir = join('locale', po[:-3], 'LC_MESSAGES')
409 modir = join('locale', po[:-3], 'LC_MESSAGES')
410 mofile = join(modir, 'hg.mo')
410 mofile = join(modir, 'hg.mo')
411 mobuildfile = join('mercurial', mofile)
411 mobuildfile = join('mercurial', mofile)
412 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
412 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
413 if sys.platform != 'sunos5':
413 if sys.platform != 'sunos5':
414 # msgfmt on Solaris does not know about -c
414 # msgfmt on Solaris does not know about -c
415 cmd.append('-c')
415 cmd.append('-c')
416 self.mkpath(join('mercurial', modir))
416 self.mkpath(join('mercurial', modir))
417 self.make_file([pofile], mobuildfile, spawn, (cmd,))
417 self.make_file([pofile], mobuildfile, spawn, (cmd,))
418
418
419
419
420 class hgdist(Distribution):
420 class hgdist(Distribution):
421 pure = False
421 pure = False
422 rust = False
422 rust = False
423 no_rust = False
423 no_rust = False
424 cffi = ispypy
424 cffi = ispypy
425
425
426 global_options = Distribution.global_options + [
426 global_options = Distribution.global_options + [
427 ('pure', None, "use pure (slow) Python code instead of C extensions"),
427 ('pure', None, "use pure (slow) Python code instead of C extensions"),
428 ('rust', None, "use Rust extensions additionally to C extensions"),
428 ('rust', None, "use Rust extensions additionally to C extensions"),
429 (
429 (
430 'no-rust',
430 'no-rust',
431 None,
431 None,
432 "do not use Rust extensions additionally to C extensions",
432 "do not use Rust extensions additionally to C extensions",
433 ),
433 ),
434 ]
434 ]
435
435
436 negative_opt = Distribution.negative_opt.copy()
436 negative_opt = Distribution.negative_opt.copy()
437 boolean_options = ['pure', 'rust', 'no-rust']
437 boolean_options = ['pure', 'rust', 'no-rust']
438 negative_opt['no-rust'] = 'rust'
438 negative_opt['no-rust'] = 'rust'
439
439
440 def _set_command_options(self, command_obj, option_dict=None):
440 def _set_command_options(self, command_obj, option_dict=None):
441 # Not all distutils versions in the wild have boolean_options.
441 # Not all distutils versions in the wild have boolean_options.
442 # This should be cleaned up when we're Python 3 only.
442 # This should be cleaned up when we're Python 3 only.
443 command_obj.boolean_options = (
443 command_obj.boolean_options = (
444 getattr(command_obj, 'boolean_options', []) + self.boolean_options
444 getattr(command_obj, 'boolean_options', []) + self.boolean_options
445 )
445 )
446 return Distribution._set_command_options(
446 return Distribution._set_command_options(
447 self, command_obj, option_dict=option_dict
447 self, command_obj, option_dict=option_dict
448 )
448 )
449
449
450 def parse_command_line(self):
450 def parse_command_line(self):
451 ret = Distribution.parse_command_line(self)
451 ret = Distribution.parse_command_line(self)
452 if not (self.rust or self.no_rust):
452 if not (self.rust or self.no_rust):
453 hgrustext = os.environ.get('HGWITHRUSTEXT')
453 hgrustext = os.environ.get('HGWITHRUSTEXT')
454 # TODO record it for proper rebuild upon changes
454 # TODO record it for proper rebuild upon changes
455 # (see mercurial/__modulepolicy__.py)
455 # (see mercurial/__modulepolicy__.py)
456 if hgrustext != 'cpython' and hgrustext is not None:
456 if hgrustext != 'cpython' and hgrustext is not None:
457 if hgrustext:
457 if hgrustext:
458 msg = 'unknown HGWITHRUSTEXT value: %s' % hgrustext
458 msg = 'unknown HGWITHRUSTEXT value: %s' % hgrustext
459 print(msg, file=sys.stderr)
459 print(msg, file=sys.stderr)
460 hgrustext = None
460 hgrustext = None
461 self.rust = hgrustext is not None
461 self.rust = hgrustext is not None
462 self.no_rust = not self.rust
462 self.no_rust = not self.rust
463 return ret
463 return ret
464
464
465 def has_ext_modules(self):
465 def has_ext_modules(self):
466 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
466 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
467 # too late for some cases
467 # too late for some cases
468 return not self.pure and Distribution.has_ext_modules(self)
468 return not self.pure and Distribution.has_ext_modules(self)
469
469
470
470
471 # This is ugly as a one-liner. So use a variable.
471 # This is ugly as a one-liner. So use a variable.
472 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
472 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
473 buildextnegops['no-zstd'] = 'zstd'
473 buildextnegops['no-zstd'] = 'zstd'
474 buildextnegops['no-rust'] = 'rust'
474 buildextnegops['no-rust'] = 'rust'
475
475
476
476
477 class hgbuildext(build_ext):
477 class hgbuildext(build_ext):
478 user_options = build_ext.user_options + [
478 user_options = build_ext.user_options + [
479 ('zstd', None, 'compile zstd bindings [default]'),
479 ('zstd', None, 'compile zstd bindings [default]'),
480 ('no-zstd', None, 'do not compile zstd bindings'),
480 ('no-zstd', None, 'do not compile zstd bindings'),
481 (
481 (
482 'rust',
482 'rust',
483 None,
483 None,
484 'compile Rust extensions if they are in use '
484 'compile Rust extensions if they are in use '
485 '(requires Cargo) [default]',
485 '(requires Cargo) [default]',
486 ),
486 ),
487 ('no-rust', None, 'do not compile Rust extensions'),
487 ('no-rust', None, 'do not compile Rust extensions'),
488 ]
488 ]
489
489
490 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
490 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
491 negative_opt = buildextnegops
491 negative_opt = buildextnegops
492
492
493 def initialize_options(self):
493 def initialize_options(self):
494 self.zstd = True
494 self.zstd = True
495 self.rust = True
495 self.rust = True
496
496
497 return build_ext.initialize_options(self)
497 return build_ext.initialize_options(self)
498
498
499 def finalize_options(self):
499 def finalize_options(self):
500 # Unless overridden by the end user, build extensions in parallel.
500 # Unless overridden by the end user, build extensions in parallel.
501 # Only influences behavior on Python 3.5+.
501 # Only influences behavior on Python 3.5+.
502 if getattr(self, 'parallel', None) is None:
502 if getattr(self, 'parallel', None) is None:
503 self.parallel = True
503 self.parallel = True
504
504
505 return build_ext.finalize_options(self)
505 return build_ext.finalize_options(self)
506
506
507 def build_extensions(self):
507 def build_extensions(self):
508 ruststandalones = [
508 ruststandalones = [
509 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
509 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
510 ]
510 ]
511 self.extensions = [
511 self.extensions = [
512 e for e in self.extensions if e not in ruststandalones
512 e for e in self.extensions if e not in ruststandalones
513 ]
513 ]
514 # Filter out zstd if disabled via argument.
514 # Filter out zstd if disabled via argument.
515 if not self.zstd:
515 if not self.zstd:
516 self.extensions = [
516 self.extensions = [
517 e for e in self.extensions if e.name != 'mercurial.zstd'
517 e for e in self.extensions if e.name != 'mercurial.zstd'
518 ]
518 ]
519
519
520 # Build Rust standalone extensions if it'll be used
520 # Build Rust standalone extensions if it'll be used
521 # and its build is not explicitly disabled (for external build
521 # and its build is not explicitly disabled (for external build
522 # as Linux distributions would do)
522 # as Linux distributions would do)
523 if self.distribution.rust and self.rust:
523 if self.distribution.rust and self.rust:
524 if not sys.platform.startswith('linux'):
524 if not sys.platform.startswith('linux'):
525 self.warn(
525 self.warn(
526 "rust extensions have only been tested on Linux "
526 "rust extensions have only been tested on Linux "
527 "and may not behave correctly on other platforms"
527 "and may not behave correctly on other platforms"
528 )
528 )
529
529
530 for rustext in ruststandalones:
530 for rustext in ruststandalones:
531 rustext.build('' if self.inplace else self.build_lib)
531 rustext.build('' if self.inplace else self.build_lib)
532
532
533 return build_ext.build_extensions(self)
533 return build_ext.build_extensions(self)
534
534
535 def build_extension(self, ext):
535 def build_extension(self, ext):
536 if (
536 if (
537 self.distribution.rust
537 self.distribution.rust
538 and self.rust
538 and self.rust
539 and isinstance(ext, RustExtension)
539 and isinstance(ext, RustExtension)
540 ):
540 ):
541 ext.rustbuild()
541 ext.rustbuild()
542 try:
542 try:
543 build_ext.build_extension(self, ext)
543 build_ext.build_extension(self, ext)
544 except CCompilerError:
544 except CCompilerError:
545 if not getattr(ext, 'optional', False):
545 if not getattr(ext, 'optional', False):
546 raise
546 raise
547 log.warn(
547 log.warn(
548 "Failed to build optional extension '%s' (skipping)", ext.name
548 "Failed to build optional extension '%s' (skipping)", ext.name
549 )
549 )
550
550
551
551
552 class hgbuildscripts(build_scripts):
552 class hgbuildscripts(build_scripts):
553 def run(self):
553 def run(self):
554 if os.name != 'nt' or self.distribution.pure:
554 if os.name != 'nt' or self.distribution.pure:
555 return build_scripts.run(self)
555 return build_scripts.run(self)
556
556
557 exebuilt = False
557 exebuilt = False
558 try:
558 try:
559 self.run_command('build_hgexe')
559 self.run_command('build_hgexe')
560 exebuilt = True
560 exebuilt = True
561 except (DistutilsError, CCompilerError):
561 except (DistutilsError, CCompilerError):
562 log.warn('failed to build optional hg.exe')
562 log.warn('failed to build optional hg.exe')
563
563
564 if exebuilt:
564 if exebuilt:
565 # Copying hg.exe to the scripts build directory ensures it is
565 # Copying hg.exe to the scripts build directory ensures it is
566 # installed by the install_scripts command.
566 # installed by the install_scripts command.
567 hgexecommand = self.get_finalized_command('build_hgexe')
567 hgexecommand = self.get_finalized_command('build_hgexe')
568 dest = os.path.join(self.build_dir, 'hg.exe')
568 dest = os.path.join(self.build_dir, 'hg.exe')
569 self.mkpath(self.build_dir)
569 self.mkpath(self.build_dir)
570 self.copy_file(hgexecommand.hgexepath, dest)
570 self.copy_file(hgexecommand.hgexepath, dest)
571
571
572 # Remove hg.bat because it is redundant with hg.exe.
572 # Remove hg.bat because it is redundant with hg.exe.
573 self.scripts.remove('contrib/win32/hg.bat')
573 self.scripts.remove('contrib/win32/hg.bat')
574
574
575 return build_scripts.run(self)
575 return build_scripts.run(self)
576
576
577
577
578 class hgbuildpy(build_py):
578 class hgbuildpy(build_py):
579 def finalize_options(self):
579 def finalize_options(self):
580 build_py.finalize_options(self)
580 build_py.finalize_options(self)
581
581
582 if self.distribution.pure:
582 if self.distribution.pure:
583 self.distribution.ext_modules = []
583 self.distribution.ext_modules = []
584 elif self.distribution.cffi:
584 elif self.distribution.cffi:
585 from mercurial.cffi import (
585 from mercurial.cffi import (
586 bdiffbuild,
586 bdiffbuild,
587 mpatchbuild,
587 mpatchbuild,
588 )
588 )
589
589
590 exts = [
590 exts = [
591 mpatchbuild.ffi.distutils_extension(),
591 mpatchbuild.ffi.distutils_extension(),
592 bdiffbuild.ffi.distutils_extension(),
592 bdiffbuild.ffi.distutils_extension(),
593 ]
593 ]
594 # cffi modules go here
594 # cffi modules go here
595 if sys.platform == 'darwin':
595 if sys.platform == 'darwin':
596 from mercurial.cffi import osutilbuild
596 from mercurial.cffi import osutilbuild
597
597
598 exts.append(osutilbuild.ffi.distutils_extension())
598 exts.append(osutilbuild.ffi.distutils_extension())
599 self.distribution.ext_modules = exts
599 self.distribution.ext_modules = exts
600 else:
600 else:
601 h = os.path.join(get_python_inc(), 'Python.h')
601 h = os.path.join(get_python_inc(), 'Python.h')
602 if not os.path.exists(h):
602 if not os.path.exists(h):
603 raise SystemExit(
603 raise SystemExit(
604 'Python headers are required to build '
604 'Python headers are required to build '
605 'Mercurial but weren\'t found in %s' % h
605 'Mercurial but weren\'t found in %s' % h
606 )
606 )
607
607
608 def run(self):
608 def run(self):
609 basepath = os.path.join(self.build_lib, 'mercurial')
609 basepath = os.path.join(self.build_lib, 'mercurial')
610 self.mkpath(basepath)
610 self.mkpath(basepath)
611
611
612 rust = self.distribution.rust
612 rust = self.distribution.rust
613 if self.distribution.pure:
613 if self.distribution.pure:
614 modulepolicy = 'py'
614 modulepolicy = 'py'
615 elif self.build_lib == '.':
615 elif self.build_lib == '.':
616 # in-place build should run without rebuilding and Rust extensions
616 # in-place build should run without rebuilding and Rust extensions
617 modulepolicy = 'rust+c-allow' if rust else 'allow'
617 modulepolicy = 'rust+c-allow' if rust else 'allow'
618 else:
618 else:
619 modulepolicy = 'rust+c' if rust else 'c'
619 modulepolicy = 'rust+c' if rust else 'c'
620
620
621 content = b''.join(
621 content = b''.join(
622 [
622 [
623 b'# this file is autogenerated by setup.py\n',
623 b'# this file is autogenerated by setup.py\n',
624 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
624 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
625 ]
625 ]
626 )
626 )
627 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
627 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
628
628
629 build_py.run(self)
629 build_py.run(self)
630
630
631
631
632 class buildhgextindex(Command):
632 class buildhgextindex(Command):
633 description = 'generate prebuilt index of hgext (for frozen package)'
633 description = 'generate prebuilt index of hgext (for frozen package)'
634 user_options = []
634 user_options = []
635 _indexfilename = 'hgext/__index__.py'
635 _indexfilename = 'hgext/__index__.py'
636
636
637 def initialize_options(self):
637 def initialize_options(self):
638 pass
638 pass
639
639
640 def finalize_options(self):
640 def finalize_options(self):
641 pass
641 pass
642
642
643 def run(self):
643 def run(self):
644 if os.path.exists(self._indexfilename):
644 if os.path.exists(self._indexfilename):
645 with open(self._indexfilename, 'w') as f:
645 with open(self._indexfilename, 'w') as f:
646 f.write('# empty\n')
646 f.write('# empty\n')
647
647
648 # here no extension enabled, disabled() lists up everything
648 # here no extension enabled, disabled() lists up everything
649 code = (
649 code = (
650 'import pprint; from mercurial import extensions; '
650 'import pprint; from mercurial import extensions; '
651 'ext = extensions.disabled();'
651 'ext = extensions.disabled();'
652 'ext.pop("__index__", None);'
652 'ext.pop("__index__", None);'
653 'pprint.pprint(ext)'
653 'pprint.pprint(ext)'
654 )
654 )
655 returncode, out, err = runcmd(
655 returncode, out, err = runcmd(
656 [sys.executable, '-c', code], localhgenv()
656 [sys.executable, '-c', code], localhgenv()
657 )
657 )
658 if err or returncode != 0:
658 if err or returncode != 0:
659 raise DistutilsExecError(err)
659 raise DistutilsExecError(err)
660
660
661 with open(self._indexfilename, 'wb') as f:
661 with open(self._indexfilename, 'wb') as f:
662 f.write(b'# this file is autogenerated by setup.py\n')
662 f.write(b'# this file is autogenerated by setup.py\n')
663 f.write(b'docs = ')
663 f.write(b'docs = ')
664 f.write(out)
664 f.write(out)
665
665
666
666
667 class buildhgexe(build_ext):
667 class buildhgexe(build_ext):
668 description = 'compile hg.exe from mercurial/exewrapper.c'
668 description = 'compile hg.exe from mercurial/exewrapper.c'
669
669
670 LONG_PATHS_MANIFEST = """\
670 LONG_PATHS_MANIFEST = """\
671 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
671 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
672 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
672 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
673 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
673 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
674 <security>
674 <security>
675 <requestedPrivileges>
675 <requestedPrivileges>
676 <requestedExecutionLevel
676 <requestedExecutionLevel
677 level="asInvoker"
677 level="asInvoker"
678 uiAccess="false"
678 uiAccess="false"
679 />
679 />
680 </requestedPrivileges>
680 </requestedPrivileges>
681 </security>
681 </security>
682 </trustInfo>
682 </trustInfo>
683 <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
683 <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
684 <application>
684 <application>
685 <!-- Windows Vista -->
685 <!-- Windows Vista -->
686 <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
686 <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
687 <!-- Windows 7 -->
687 <!-- Windows 7 -->
688 <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
688 <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
689 <!-- Windows 8 -->
689 <!-- Windows 8 -->
690 <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
690 <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
691 <!-- Windows 8.1 -->
691 <!-- Windows 8.1 -->
692 <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
692 <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
693 <!-- Windows 10 and Windows 11 -->
693 <!-- Windows 10 and Windows 11 -->
694 <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
694 <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
695 </application>
695 </application>
696 </compatibility>
696 </compatibility>
697 <application xmlns="urn:schemas-microsoft-com:asm.v3">
697 <application xmlns="urn:schemas-microsoft-com:asm.v3">
698 <windowsSettings
698 <windowsSettings
699 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
699 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
700 <ws2:longPathAware>true</ws2:longPathAware>
700 <ws2:longPathAware>true</ws2:longPathAware>
701 </windowsSettings>
701 </windowsSettings>
702 </application>
702 </application>
703 <dependency>
703 <dependency>
704 <dependentAssembly>
704 <dependentAssembly>
705 <assemblyIdentity type="win32"
705 <assemblyIdentity type="win32"
706 name="Microsoft.Windows.Common-Controls"
706 name="Microsoft.Windows.Common-Controls"
707 version="6.0.0.0"
707 version="6.0.0.0"
708 processorArchitecture="*"
708 processorArchitecture="*"
709 publicKeyToken="6595b64144ccf1df"
709 publicKeyToken="6595b64144ccf1df"
710 language="*" />
710 language="*" />
711 </dependentAssembly>
711 </dependentAssembly>
712 </dependency>
712 </dependency>
713 </assembly>
713 </assembly>
714 """
714 """
715
715
716 def initialize_options(self):
716 def initialize_options(self):
717 build_ext.initialize_options(self)
717 build_ext.initialize_options(self)
718
718
719 def build_extensions(self):
719 def build_extensions(self):
720 if os.name != 'nt':
720 if os.name != 'nt':
721 return
721 return
722 if isinstance(self.compiler, HackedMingw32CCompiler):
722 if isinstance(self.compiler, HackedMingw32CCompiler):
723 self.compiler.compiler_so = self.compiler.compiler # no -mdll
723 self.compiler.compiler_so = self.compiler.compiler # no -mdll
724 self.compiler.dll_libraries = [] # no -lmsrvc90
724 self.compiler.dll_libraries = [] # no -lmsrvc90
725
725
726 pythonlib = None
726 pythonlib = None
727
727
728 dirname = os.path.dirname(self.get_ext_fullpath('dummy'))
728 dirname = os.path.dirname(self.get_ext_fullpath('dummy'))
729 self.hgtarget = os.path.join(dirname, 'hg')
729 self.hgtarget = os.path.join(dirname, 'hg')
730
730
731 if getattr(sys, 'dllhandle', None):
731 if getattr(sys, 'dllhandle', None):
732 # Different Python installs can have different Python library
732 # Different Python installs can have different Python library
733 # names. e.g. the official CPython distribution uses pythonXY.dll
733 # names. e.g. the official CPython distribution uses pythonXY.dll
734 # and MinGW uses libpythonX.Y.dll.
734 # and MinGW uses libpythonX.Y.dll.
735 _kernel32 = ctypes.windll.kernel32
735 _kernel32 = ctypes.windll.kernel32
736 _kernel32.GetModuleFileNameA.argtypes = [
736 _kernel32.GetModuleFileNameA.argtypes = [
737 ctypes.c_void_p,
737 ctypes.c_void_p,
738 ctypes.c_void_p,
738 ctypes.c_void_p,
739 ctypes.c_ulong,
739 ctypes.c_ulong,
740 ]
740 ]
741 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
741 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
742 size = 1000
742 size = 1000
743 buf = ctypes.create_string_buffer(size + 1)
743 buf = ctypes.create_string_buffer(size + 1)
744 filelen = _kernel32.GetModuleFileNameA(
744 filelen = _kernel32.GetModuleFileNameA(
745 sys.dllhandle, ctypes.byref(buf), size
745 sys.dllhandle, ctypes.byref(buf), size
746 )
746 )
747
747
748 if filelen > 0 and filelen != size:
748 if filelen > 0 and filelen != size:
749 dllbasename = os.path.basename(buf.value)
749 dllbasename = os.path.basename(buf.value)
750 if not dllbasename.lower().endswith(b'.dll'):
750 if not dllbasename.lower().endswith(b'.dll'):
751 raise SystemExit(
751 raise SystemExit(
752 'Python DLL does not end with .dll: %s' % dllbasename
752 'Python DLL does not end with .dll: %s' % dllbasename
753 )
753 )
754 pythonlib = dllbasename[:-4]
754 pythonlib = dllbasename[:-4]
755
755
756 # Copy the pythonXY.dll next to the binary so that it runs
756 # Copy the pythonXY.dll next to the binary so that it runs
757 # without tampering with PATH.
757 # without tampering with PATH.
758 dest = os.path.join(
758 dest = os.path.join(
759 os.path.dirname(self.hgtarget),
759 os.path.dirname(self.hgtarget),
760 os.fsdecode(dllbasename),
760 os.fsdecode(dllbasename),
761 )
761 )
762
762
763 if not os.path.exists(dest):
763 if not os.path.exists(dest):
764 shutil.copy(buf.value, dest)
764 shutil.copy(buf.value, dest)
765
765
766 # Also overwrite python3.dll so that hgext.git is usable.
766 # Also overwrite python3.dll so that hgext.git is usable.
767 # TODO: also handle the MSYS flavor
767 # TODO: also handle the MSYS flavor
768 python_x = os.path.join(
768 python_x = os.path.join(
769 os.path.dirname(os.fsdecode(buf.value)),
769 os.path.dirname(os.fsdecode(buf.value)),
770 "python3.dll",
770 "python3.dll",
771 )
771 )
772
772
773 if os.path.exists(python_x):
773 if os.path.exists(python_x):
774 dest = os.path.join(
774 dest = os.path.join(
775 os.path.dirname(self.hgtarget),
775 os.path.dirname(self.hgtarget),
776 os.path.basename(python_x),
776 os.path.basename(python_x),
777 )
777 )
778
778
779 shutil.copy(python_x, dest)
779 shutil.copy(python_x, dest)
780
780
781 if not pythonlib:
781 if not pythonlib:
782 log.warn(
782 log.warn(
783 'could not determine Python DLL filename; assuming pythonXY'
783 'could not determine Python DLL filename; assuming pythonXY'
784 )
784 )
785
785
786 hv = sys.hexversion
786 hv = sys.hexversion
787 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
787 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
788
788
789 log.info('using %s as Python library name' % pythonlib)
789 log.info('using %s as Python library name' % pythonlib)
790 with open('mercurial/hgpythonlib.h', 'wb') as f:
790 with open('mercurial/hgpythonlib.h', 'wb') as f:
791 f.write(b'/* this file is autogenerated by setup.py */\n')
791 f.write(b'/* this file is autogenerated by setup.py */\n')
792 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
792 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
793
793
794 objects = self.compiler.compile(
794 objects = self.compiler.compile(
795 ['mercurial/exewrapper.c'],
795 ['mercurial/exewrapper.c'],
796 output_dir=self.build_temp,
796 output_dir=self.build_temp,
797 macros=[('_UNICODE', None), ('UNICODE', None)],
797 macros=[('_UNICODE', None), ('UNICODE', None)],
798 )
798 )
799 self.compiler.link_executable(
799 self.compiler.link_executable(
800 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
800 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
801 )
801 )
802
802
803 self.addlongpathsmanifest()
803 self.addlongpathsmanifest()
804
804
805 def addlongpathsmanifest(self):
805 def addlongpathsmanifest(self):
806 """Add manifest pieces so that hg.exe understands long paths
806 """Add manifest pieces so that hg.exe understands long paths
807
807
808 Why resource #1 should be used for .exe manifests? I don't know and
808 Why resource #1 should be used for .exe manifests? I don't know and
809 wasn't able to find an explanation for mortals. But it seems to work.
809 wasn't able to find an explanation for mortals. But it seems to work.
810 """
810 """
811 exefname = self.compiler.executable_filename(self.hgtarget)
811 exefname = self.compiler.executable_filename(self.hgtarget)
812 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
812 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
813 os.close(fdauto)
813 os.close(fdauto)
814 with open(manfname, 'w', encoding="UTF-8") as f:
814 with open(manfname, 'w', encoding="UTF-8") as f:
815 f.write(self.LONG_PATHS_MANIFEST)
815 f.write(self.LONG_PATHS_MANIFEST)
816 log.info("long paths manifest is written to '%s'" % manfname)
816 log.info("long paths manifest is written to '%s'" % manfname)
817 outputresource = '-outputresource:%s;#1' % exefname
817 outputresource = '-outputresource:%s;#1' % exefname
818 log.info("running mt.exe to update hg.exe's manifest in-place")
818 log.info("running mt.exe to update hg.exe's manifest in-place")
819
819
820 self.spawn(
820 self.spawn(
821 [
821 [
822 self.compiler.mt,
822 self.compiler.mt,
823 '-nologo',
823 '-nologo',
824 '-manifest',
824 '-manifest',
825 manfname,
825 manfname,
826 outputresource,
826 outputresource,
827 ]
827 ]
828 )
828 )
829 log.info("done updating hg.exe's manifest")
829 log.info("done updating hg.exe's manifest")
830 os.remove(manfname)
830 os.remove(manfname)
831
831
832 @property
832 @property
833 def hgexepath(self):
833 def hgexepath(self):
834 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
834 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
835 return os.path.join(self.build_temp, dir, 'hg.exe')
835 return os.path.join(self.build_temp, dir, 'hg.exe')
836
836
837
837
838 class hgbuilddoc(Command):
838 class hgbuilddoc(Command):
839 description = 'build documentation'
839 description = 'build documentation'
840 user_options = [
840 user_options = [
841 ('man', None, 'generate man pages'),
841 ('man', None, 'generate man pages'),
842 ('html', None, 'generate html pages'),
842 ('html', None, 'generate html pages'),
843 ]
843 ]
844
844
845 def initialize_options(self):
845 def initialize_options(self):
846 self.man = None
846 self.man = None
847 self.html = None
847 self.html = None
848
848
849 def finalize_options(self):
849 def finalize_options(self):
850 # If --man or --html are set, only generate what we're told to.
850 # If --man or --html are set, only generate what we're told to.
851 # Otherwise generate everything.
851 # Otherwise generate everything.
852 have_subset = self.man is not None or self.html is not None
852 have_subset = self.man is not None or self.html is not None
853
853
854 if have_subset:
854 if have_subset:
855 self.man = True if self.man else False
855 self.man = True if self.man else False
856 self.html = True if self.html else False
856 self.html = True if self.html else False
857 else:
857 else:
858 self.man = True
858 self.man = True
859 self.html = True
859 self.html = True
860
860
861 def run(self):
861 def run(self):
862 def normalizecrlf(p):
862 def normalizecrlf(p):
863 with open(p, 'rb') as fh:
863 with open(p, 'rb') as fh:
864 orig = fh.read()
864 orig = fh.read()
865
865
866 if b'\r\n' not in orig:
866 if b'\r\n' not in orig:
867 return
867 return
868
868
869 log.info('normalizing %s to LF line endings' % p)
869 log.info('normalizing %s to LF line endings' % p)
870 with open(p, 'wb') as fh:
870 with open(p, 'wb') as fh:
871 fh.write(orig.replace(b'\r\n', b'\n'))
871 fh.write(orig.replace(b'\r\n', b'\n'))
872
872
873 def gentxt(root):
873 def gentxt(root):
874 txt = 'doc/%s.txt' % root
874 txt = 'doc/%s.txt' % root
875 log.info('generating %s' % txt)
875 log.info('generating %s' % txt)
876 res, out, err = runcmd(
876 res, out, err = runcmd(
877 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
877 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
878 )
878 )
879 if res:
879 if res:
880 raise SystemExit(
880 raise SystemExit(
881 'error running gendoc.py: %s'
881 'error running gendoc.py: %s'
882 % '\n'.join([sysstr(out), sysstr(err)])
882 % '\n'.join([sysstr(out), sysstr(err)])
883 )
883 )
884
884
885 with open(txt, 'wb') as fh:
885 with open(txt, 'wb') as fh:
886 fh.write(out)
886 fh.write(out)
887
887
888 def gengendoc(root):
888 def gengendoc(root):
889 gendoc = 'doc/%s.gendoc.txt' % root
889 gendoc = 'doc/%s.gendoc.txt' % root
890
890
891 log.info('generating %s' % gendoc)
891 log.info('generating %s' % gendoc)
892 res, out, err = runcmd(
892 res, out, err = runcmd(
893 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
893 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
894 os.environ,
894 os.environ,
895 cwd='doc',
895 cwd='doc',
896 )
896 )
897 if res:
897 if res:
898 raise SystemExit(
898 raise SystemExit(
899 'error running gendoc: %s'
899 'error running gendoc: %s'
900 % '\n'.join([sysstr(out), sysstr(err)])
900 % '\n'.join([sysstr(out), sysstr(err)])
901 )
901 )
902
902
903 with open(gendoc, 'wb') as fh:
903 with open(gendoc, 'wb') as fh:
904 fh.write(out)
904 fh.write(out)
905
905
906 def genman(root):
906 def genman(root):
907 log.info('generating doc/%s' % root)
907 log.info('generating doc/%s' % root)
908 res, out, err = runcmd(
908 res, out, err = runcmd(
909 [
909 [
910 sys.executable,
910 sys.executable,
911 'runrst',
911 'runrst',
912 'hgmanpage',
912 'hgmanpage',
913 '--halt',
913 '--halt',
914 'warning',
914 'warning',
915 '--strip-elements-with-class',
915 '--strip-elements-with-class',
916 'htmlonly',
916 'htmlonly',
917 '%s.txt' % root,
917 '%s.txt' % root,
918 root,
918 root,
919 ],
919 ],
920 os.environ,
920 os.environ,
921 cwd='doc',
921 cwd='doc',
922 )
922 )
923 if res:
923 if res:
924 raise SystemExit(
924 raise SystemExit(
925 'error running runrst: %s'
925 'error running runrst: %s'
926 % '\n'.join([sysstr(out), sysstr(err)])
926 % '\n'.join([sysstr(out), sysstr(err)])
927 )
927 )
928
928
929 normalizecrlf('doc/%s' % root)
929 normalizecrlf('doc/%s' % root)
930
930
931 def genhtml(root):
931 def genhtml(root):
932 log.info('generating doc/%s.html' % root)
932 log.info('generating doc/%s.html' % root)
933 res, out, err = runcmd(
933 res, out, err = runcmd(
934 [
934 [
935 sys.executable,
935 sys.executable,
936 'runrst',
936 'runrst',
937 'html',
937 'html',
938 '--halt',
938 '--halt',
939 'warning',
939 'warning',
940 '--link-stylesheet',
940 '--link-stylesheet',
941 '--stylesheet-path',
941 '--stylesheet-path',
942 'style.css',
942 'style.css',
943 '%s.txt' % root,
943 '%s.txt' % root,
944 '%s.html' % root,
944 '%s.html' % root,
945 ],
945 ],
946 os.environ,
946 os.environ,
947 cwd='doc',
947 cwd='doc',
948 )
948 )
949 if res:
949 if res:
950 raise SystemExit(
950 raise SystemExit(
951 'error running runrst: %s'
951 'error running runrst: %s'
952 % '\n'.join([sysstr(out), sysstr(err)])
952 % '\n'.join([sysstr(out), sysstr(err)])
953 )
953 )
954
954
955 normalizecrlf('doc/%s.html' % root)
955 normalizecrlf('doc/%s.html' % root)
956
956
957 # This logic is duplicated in doc/Makefile.
957 # This logic is duplicated in doc/Makefile.
958 sources = {
958 sources = {
959 f
959 f
960 for f in os.listdir('mercurial/helptext')
960 for f in os.listdir('mercurial/helptext')
961 if re.search(r'[0-9]\.txt$', f)
961 if re.search(r'[0-9]\.txt$', f)
962 }
962 }
963
963
964 # common.txt is a one-off.
964 # common.txt is a one-off.
965 gentxt('common')
965 gentxt('common')
966
966
967 for source in sorted(sources):
967 for source in sorted(sources):
968 assert source[-4:] == '.txt'
968 assert source[-4:] == '.txt'
969 root = source[:-4]
969 root = source[:-4]
970
970
971 gentxt(root)
971 gentxt(root)
972 gengendoc(root)
972 gengendoc(root)
973
973
974 if self.man:
974 if self.man:
975 genman(root)
975 genman(root)
976 if self.html:
976 if self.html:
977 genhtml(root)
977 genhtml(root)
978
978
979
979
980 class hginstall(install):
980 class hginstall(install):
981
981
982 user_options = install.user_options + [
982 user_options = install.user_options + [
983 (
983 (
984 'old-and-unmanageable',
984 'old-and-unmanageable',
985 None,
985 None,
986 'noop, present for eggless setuptools compat',
986 'noop, present for eggless setuptools compat',
987 ),
987 ),
988 (
988 (
989 'single-version-externally-managed',
989 'single-version-externally-managed',
990 None,
990 None,
991 'noop, present for eggless setuptools compat',
991 'noop, present for eggless setuptools compat',
992 ),
992 ),
993 ]
993 ]
994
994
995 sub_commands = install.sub_commands + [
995 sub_commands = install.sub_commands + [
996 ('install_completion', lambda self: True)
996 ('install_completion', lambda self: True)
997 ]
997 ]
998
998
999 # Also helps setuptools not be sad while we refuse to create eggs.
999 # Also helps setuptools not be sad while we refuse to create eggs.
1000 single_version_externally_managed = True
1000 single_version_externally_managed = True
1001
1001
1002 def get_sub_commands(self):
1002 def get_sub_commands(self):
1003 # Screen out egg related commands to prevent egg generation. But allow
1003 # Screen out egg related commands to prevent egg generation. But allow
1004 # mercurial.egg-info generation, since that is part of modern
1004 # mercurial.egg-info generation, since that is part of modern
1005 # packaging.
1005 # packaging.
1006 excl = {'bdist_egg'}
1006 excl = {'bdist_egg'}
1007 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1007 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1008
1008
1009
1009
1010 class hginstalllib(install_lib):
1010 class hginstalllib(install_lib):
1011 """
1011 """
1012 This is a specialization of install_lib that replaces the copy_file used
1012 This is a specialization of install_lib that replaces the copy_file used
1013 there so that it supports setting the mode of files after copying them,
1013 there so that it supports setting the mode of files after copying them,
1014 instead of just preserving the mode that the files originally had. If your
1014 instead of just preserving the mode that the files originally had. If your
1015 system has a umask of something like 027, preserving the permissions when
1015 system has a umask of something like 027, preserving the permissions when
1016 copying will lead to a broken install.
1016 copying will lead to a broken install.
1017
1017
1018 Note that just passing keep_permissions=False to copy_file would be
1018 Note that just passing keep_permissions=False to copy_file would be
1019 insufficient, as it might still be applying a umask.
1019 insufficient, as it might still be applying a umask.
1020 """
1020 """
1021
1021
1022 def run(self):
1022 def run(self):
1023 realcopyfile = file_util.copy_file
1023 realcopyfile = file_util.copy_file
1024
1024
1025 def copyfileandsetmode(*args, **kwargs):
1025 def copyfileandsetmode(*args, **kwargs):
1026 src, dst = args[0], args[1]
1026 src, dst = args[0], args[1]
1027 dst, copied = realcopyfile(*args, **kwargs)
1027 dst, copied = realcopyfile(*args, **kwargs)
1028 if copied:
1028 if copied:
1029 st = os.stat(src)
1029 st = os.stat(src)
1030 # Persist executable bit (apply it to group and other if user
1030 # Persist executable bit (apply it to group and other if user
1031 # has it)
1031 # has it)
1032 if st[stat.ST_MODE] & stat.S_IXUSR:
1032 if st[stat.ST_MODE] & stat.S_IXUSR:
1033 setmode = int('0755', 8)
1033 setmode = int('0755', 8)
1034 else:
1034 else:
1035 setmode = int('0644', 8)
1035 setmode = int('0644', 8)
1036 m = stat.S_IMODE(st[stat.ST_MODE])
1036 m = stat.S_IMODE(st[stat.ST_MODE])
1037 m = (m & ~int('0777', 8)) | setmode
1037 m = (m & ~int('0777', 8)) | setmode
1038 os.chmod(dst, m)
1038 os.chmod(dst, m)
1039
1039
1040 file_util.copy_file = copyfileandsetmode
1040 file_util.copy_file = copyfileandsetmode
1041 try:
1041 try:
1042 install_lib.run(self)
1042 install_lib.run(self)
1043 finally:
1043 finally:
1044 file_util.copy_file = realcopyfile
1044 file_util.copy_file = realcopyfile
1045
1045
1046
1046
1047 class hginstallscripts(install_scripts):
1047 class hginstallscripts(install_scripts):
1048 """
1048 """
1049 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1049 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1050 the configured directory for modules. If possible, the path is made relative
1050 the configured directory for modules. If possible, the path is made relative
1051 to the directory for scripts.
1051 to the directory for scripts.
1052 """
1052 """
1053
1053
1054 def initialize_options(self):
1054 def initialize_options(self):
1055 install_scripts.initialize_options(self)
1055 install_scripts.initialize_options(self)
1056
1056
1057 self.install_lib = None
1057 self.install_lib = None
1058
1058
1059 def finalize_options(self):
1059 def finalize_options(self):
1060 install_scripts.finalize_options(self)
1060 install_scripts.finalize_options(self)
1061 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1061 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1062
1062
1063 def run(self):
1063 def run(self):
1064 install_scripts.run(self)
1064 install_scripts.run(self)
1065
1065
1066 # It only makes sense to replace @LIBDIR@ with the install path if
1066 # It only makes sense to replace @LIBDIR@ with the install path if
1067 # the install path is known. For wheels, the logic below calculates
1067 # the install path is known. For wheels, the logic below calculates
1068 # the libdir to be "../..". This is because the internal layout of a
1068 # the libdir to be "../..". This is because the internal layout of a
1069 # wheel archive looks like:
1069 # wheel archive looks like:
1070 #
1070 #
1071 # mercurial-3.6.1.data/scripts/hg
1071 # mercurial-3.6.1.data/scripts/hg
1072 # mercurial/__init__.py
1072 # mercurial/__init__.py
1073 #
1073 #
1074 # When installing wheels, the subdirectories of the "<pkg>.data"
1074 # When installing wheels, the subdirectories of the "<pkg>.data"
1075 # directory are translated to system local paths and files therein
1075 # directory are translated to system local paths and files therein
1076 # are copied in place. The mercurial/* files are installed into the
1076 # are copied in place. The mercurial/* files are installed into the
1077 # site-packages directory. However, the site-packages directory
1077 # site-packages directory. However, the site-packages directory
1078 # isn't known until wheel install time. This means we have no clue
1078 # isn't known until wheel install time. This means we have no clue
1079 # at wheel generation time what the installed site-packages directory
1079 # at wheel generation time what the installed site-packages directory
1080 # will be. And, wheels don't appear to provide the ability to register
1080 # will be. And, wheels don't appear to provide the ability to register
1081 # custom code to run during wheel installation. This all means that
1081 # custom code to run during wheel installation. This all means that
1082 # we can't reliably set the libdir in wheels: the default behavior
1082 # we can't reliably set the libdir in wheels: the default behavior
1083 # of looking in sys.path must do.
1083 # of looking in sys.path must do.
1084
1084
1085 if (
1085 if (
1086 os.path.splitdrive(self.install_dir)[0]
1086 os.path.splitdrive(self.install_dir)[0]
1087 != os.path.splitdrive(self.install_lib)[0]
1087 != os.path.splitdrive(self.install_lib)[0]
1088 ):
1088 ):
1089 # can't make relative paths from one drive to another, so use an
1089 # can't make relative paths from one drive to another, so use an
1090 # absolute path instead
1090 # absolute path instead
1091 libdir = self.install_lib
1091 libdir = self.install_lib
1092 else:
1092 else:
1093 libdir = os.path.relpath(self.install_lib, self.install_dir)
1093 libdir = os.path.relpath(self.install_lib, self.install_dir)
1094
1094
1095 for outfile in self.outfiles:
1095 for outfile in self.outfiles:
1096 with open(outfile, 'rb') as fp:
1096 with open(outfile, 'rb') as fp:
1097 data = fp.read()
1097 data = fp.read()
1098
1098
1099 # skip binary files
1099 # skip binary files
1100 if b'\0' in data:
1100 if b'\0' in data:
1101 continue
1101 continue
1102
1102
1103 # During local installs, the shebang will be rewritten to the final
1103 # During local installs, the shebang will be rewritten to the final
1104 # install path. During wheel packaging, the shebang has a special
1104 # install path. During wheel packaging, the shebang has a special
1105 # value.
1105 # value.
1106 if data.startswith(b'#!python'):
1106 if data.startswith(b'#!python'):
1107 log.info(
1107 log.info(
1108 'not rewriting @LIBDIR@ in %s because install path '
1108 'not rewriting @LIBDIR@ in %s because install path '
1109 'not known' % outfile
1109 'not known' % outfile
1110 )
1110 )
1111 continue
1111 continue
1112
1112
1113 data = data.replace(b'@LIBDIR@', libdir.encode('unicode_escape'))
1113 data = data.replace(b'@LIBDIR@', libdir.encode('unicode_escape'))
1114 with open(outfile, 'wb') as fp:
1114 with open(outfile, 'wb') as fp:
1115 fp.write(data)
1115 fp.write(data)
1116
1116
1117
1117
1118 class hginstallcompletion(Command):
1118 class hginstallcompletion(Command):
1119 description = 'Install shell completion'
1119 description = 'Install shell completion'
1120
1120
1121 def initialize_options(self):
1121 def initialize_options(self):
1122 self.install_dir = None
1122 self.install_dir = None
1123 self.outputs = []
1123 self.outputs = []
1124
1124
1125 def finalize_options(self):
1125 def finalize_options(self):
1126 self.set_undefined_options(
1126 self.set_undefined_options(
1127 'install_data', ('install_dir', 'install_dir')
1127 'install_data', ('install_dir', 'install_dir')
1128 )
1128 )
1129
1129
1130 def get_outputs(self):
1130 def get_outputs(self):
1131 return self.outputs
1131 return self.outputs
1132
1132
1133 def run(self):
1133 def run(self):
1134 for src, dir_path, dest in (
1134 for src, dir_path, dest in (
1135 (
1135 (
1136 'bash_completion',
1136 'bash_completion',
1137 ('share', 'bash-completion', 'completions'),
1137 ('share', 'bash-completion', 'completions'),
1138 'hg',
1138 'hg',
1139 ),
1139 ),
1140 ('zsh_completion', ('share', 'zsh', 'site-functions'), '_hg'),
1140 ('zsh_completion', ('share', 'zsh', 'site-functions'), '_hg'),
1141 ):
1141 ):
1142 dir = os.path.join(self.install_dir, *dir_path)
1142 dir = os.path.join(self.install_dir, *dir_path)
1143 self.mkpath(dir)
1143 self.mkpath(dir)
1144
1144
1145 dest = os.path.join(dir, dest)
1145 dest = os.path.join(dir, dest)
1146 self.outputs.append(dest)
1146 self.outputs.append(dest)
1147 self.copy_file(os.path.join('contrib', src), dest)
1147 self.copy_file(os.path.join('contrib', src), dest)
1148
1148
1149
1149
1150 # virtualenv installs custom distutils/__init__.py and
1150 # virtualenv installs custom distutils/__init__.py and
1151 # distutils/distutils.cfg files which essentially proxy back to the
1151 # distutils/distutils.cfg files which essentially proxy back to the
1152 # "real" distutils in the main Python install. The presence of this
1152 # "real" distutils in the main Python install. The presence of this
1153 # directory causes py2exe to pick up the "hacked" distutils package
1153 # directory causes py2exe to pick up the "hacked" distutils package
1154 # from the virtualenv and "import distutils" will fail from the py2exe
1154 # from the virtualenv and "import distutils" will fail from the py2exe
1155 # build because the "real" distutils files can't be located.
1155 # build because the "real" distutils files can't be located.
1156 #
1156 #
1157 # We work around this by monkeypatching the py2exe code finding Python
1157 # We work around this by monkeypatching the py2exe code finding Python
1158 # modules to replace the found virtualenv distutils modules with the
1158 # modules to replace the found virtualenv distutils modules with the
1159 # original versions via filesystem scanning. This is a bit hacky. But
1159 # original versions via filesystem scanning. This is a bit hacky. But
1160 # it allows us to use virtualenvs for py2exe packaging, which is more
1160 # it allows us to use virtualenvs for py2exe packaging, which is more
1161 # deterministic and reproducible.
1161 # deterministic and reproducible.
1162 #
1162 #
1163 # It's worth noting that the common StackOverflow suggestions for this
1163 # It's worth noting that the common StackOverflow suggestions for this
1164 # problem involve copying the original distutils files into the
1164 # problem involve copying the original distutils files into the
1165 # virtualenv or into the staging directory after setup() is invoked.
1165 # virtualenv or into the staging directory after setup() is invoked.
1166 # The former is very brittle and can easily break setup(). Our hacking
1166 # The former is very brittle and can easily break setup(). Our hacking
1167 # of the found modules routine has a similar result as copying the files
1167 # of the found modules routine has a similar result as copying the files
1168 # manually. But it makes fewer assumptions about how py2exe works and
1168 # manually. But it makes fewer assumptions about how py2exe works and
1169 # is less brittle.
1169 # is less brittle.
1170
1170
1171 # This only catches virtualenvs made with virtualenv (as opposed to
1171 # This only catches virtualenvs made with virtualenv (as opposed to
1172 # venv, which is likely what Python 3 uses).
1172 # venv, which is likely what Python 3 uses).
1173 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1173 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1174
1174
1175 if py2exehacked:
1175 if py2exehacked:
1176 from distutils.command.py2exe import py2exe as buildpy2exe
1176 from distutils.command.py2exe import py2exe as buildpy2exe
1177 from py2exe.mf import Module as py2exemodule
1177 from py2exe.mf import Module as py2exemodule
1178
1178
1179 class hgbuildpy2exe(buildpy2exe):
1179 class hgbuildpy2exe(buildpy2exe):
1180 def find_needed_modules(self, mf, files, modules):
1180 def find_needed_modules(self, mf, files, modules):
1181 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1181 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1182
1182
1183 # Replace virtualenv's distutils modules with the real ones.
1183 # Replace virtualenv's distutils modules with the real ones.
1184 modules = {}
1184 modules = {}
1185 for k, v in res.modules.items():
1185 for k, v in res.modules.items():
1186 if k != 'distutils' and not k.startswith('distutils.'):
1186 if k != 'distutils' and not k.startswith('distutils.'):
1187 modules[k] = v
1187 modules[k] = v
1188
1188
1189 res.modules = modules
1189 res.modules = modules
1190
1190
1191 import opcode
1191 import opcode
1192
1192
1193 distutilsreal = os.path.join(
1193 distutilsreal = os.path.join(
1194 os.path.dirname(opcode.__file__), 'distutils'
1194 os.path.dirname(opcode.__file__), 'distutils'
1195 )
1195 )
1196
1196
1197 for root, dirs, files in os.walk(distutilsreal):
1197 for root, dirs, files in os.walk(distutilsreal):
1198 for f in sorted(files):
1198 for f in sorted(files):
1199 if not f.endswith('.py'):
1199 if not f.endswith('.py'):
1200 continue
1200 continue
1201
1201
1202 full = os.path.join(root, f)
1202 full = os.path.join(root, f)
1203
1203
1204 parents = ['distutils']
1204 parents = ['distutils']
1205
1205
1206 if root != distutilsreal:
1206 if root != distutilsreal:
1207 rel = os.path.relpath(root, distutilsreal)
1207 rel = os.path.relpath(root, distutilsreal)
1208 parents.extend(p for p in rel.split(os.sep))
1208 parents.extend(p for p in rel.split(os.sep))
1209
1209
1210 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1210 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1211
1211
1212 if modname.startswith('distutils.tests.'):
1212 if modname.startswith('distutils.tests.'):
1213 continue
1213 continue
1214
1214
1215 if modname.endswith('.__init__'):
1215 if modname.endswith('.__init__'):
1216 modname = modname[: -len('.__init__')]
1216 modname = modname[: -len('.__init__')]
1217 path = os.path.dirname(full)
1217 path = os.path.dirname(full)
1218 else:
1218 else:
1219 path = None
1219 path = None
1220
1220
1221 res.modules[modname] = py2exemodule(
1221 res.modules[modname] = py2exemodule(
1222 modname, full, path=path
1222 modname, full, path=path
1223 )
1223 )
1224
1224
1225 if 'distutils' not in res.modules:
1225 if 'distutils' not in res.modules:
1226 raise SystemExit('could not find distutils modules')
1226 raise SystemExit('could not find distutils modules')
1227
1227
1228 return res
1228 return res
1229
1229
1230
1230
1231 cmdclass = {
1231 cmdclass = {
1232 'build': hgbuild,
1232 'build': hgbuild,
1233 'build_doc': hgbuilddoc,
1233 'build_doc': hgbuilddoc,
1234 'build_mo': hgbuildmo,
1234 'build_mo': hgbuildmo,
1235 'build_ext': hgbuildext,
1235 'build_ext': hgbuildext,
1236 'build_py': hgbuildpy,
1236 'build_py': hgbuildpy,
1237 'build_scripts': hgbuildscripts,
1237 'build_scripts': hgbuildscripts,
1238 'build_hgextindex': buildhgextindex,
1238 'build_hgextindex': buildhgextindex,
1239 'install': hginstall,
1239 'install': hginstall,
1240 'install_completion': hginstallcompletion,
1240 'install_completion': hginstallcompletion,
1241 'install_lib': hginstalllib,
1241 'install_lib': hginstalllib,
1242 'install_scripts': hginstallscripts,
1242 'install_scripts': hginstallscripts,
1243 'build_hgexe': buildhgexe,
1243 'build_hgexe': buildhgexe,
1244 }
1244 }
1245
1245
1246 if py2exehacked:
1246 if py2exehacked:
1247 cmdclass['py2exe'] = hgbuildpy2exe
1247 cmdclass['py2exe'] = hgbuildpy2exe
1248
1248
1249 packages = [
1249 packages = [
1250 'mercurial',
1250 'mercurial',
1251 'mercurial.cext',
1251 'mercurial.cext',
1252 'mercurial.cffi',
1252 'mercurial.cffi',
1253 'mercurial.defaultrc',
1253 'mercurial.defaultrc',
1254 'mercurial.dirstateutils',
1254 'mercurial.dirstateutils',
1255 'mercurial.helptext',
1255 'mercurial.helptext',
1256 'mercurial.helptext.internals',
1256 'mercurial.helptext.internals',
1257 'mercurial.hgweb',
1257 'mercurial.hgweb',
1258 'mercurial.interfaces',
1258 'mercurial.interfaces',
1259 'mercurial.pure',
1259 'mercurial.pure',
1260 'mercurial.templates',
1260 'mercurial.templates',
1261 'mercurial.thirdparty',
1261 'mercurial.thirdparty',
1262 'mercurial.thirdparty.attr',
1262 'mercurial.thirdparty.attr',
1263 'mercurial.thirdparty.zope',
1263 'mercurial.thirdparty.zope',
1264 'mercurial.thirdparty.zope.interface',
1264 'mercurial.thirdparty.zope.interface',
1265 'mercurial.upgrade_utils',
1265 'mercurial.upgrade_utils',
1266 'mercurial.utils',
1266 'mercurial.utils',
1267 'mercurial.revlogutils',
1267 'mercurial.revlogutils',
1268 'mercurial.testing',
1268 'mercurial.testing',
1269 'hgext',
1269 'hgext',
1270 'hgext.convert',
1270 'hgext.convert',
1271 'hgext.fsmonitor',
1271 'hgext.fsmonitor',
1272 'hgext.fastannotate',
1272 'hgext.fastannotate',
1273 'hgext.fsmonitor.pywatchman',
1273 'hgext.fsmonitor.pywatchman',
1274 'hgext.git',
1274 'hgext.git',
1275 'hgext.highlight',
1275 'hgext.highlight',
1276 'hgext.hooklib',
1276 'hgext.hooklib',
1277 'hgext.infinitepush',
1277 'hgext.infinitepush',
1278 'hgext.largefiles',
1278 'hgext.largefiles',
1279 'hgext.lfs',
1279 'hgext.lfs',
1280 'hgext.narrow',
1280 'hgext.narrow',
1281 'hgext.remotefilelog',
1281 'hgext.remotefilelog',
1282 'hgext.zeroconf',
1282 'hgext.zeroconf',
1283 'hgext3rd',
1283 'hgext3rd',
1284 'hgdemandimport',
1284 'hgdemandimport',
1285 ]
1285 ]
1286
1286
1287 for name in os.listdir(os.path.join('mercurial', 'templates')):
1287 for name in os.listdir(os.path.join('mercurial', 'templates')):
1288 if name != '__pycache__' and os.path.isdir(
1288 if name != '__pycache__' and os.path.isdir(
1289 os.path.join('mercurial', 'templates', name)
1289 os.path.join('mercurial', 'templates', name)
1290 ):
1290 ):
1291 packages.append('mercurial.templates.%s' % name)
1291 packages.append('mercurial.templates.%s' % name)
1292
1292
1293 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1293 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1294 # py2exe can't cope with namespace packages very well, so we have to
1294 # py2exe can't cope with namespace packages very well, so we have to
1295 # install any hgext3rd.* extensions that we want in the final py2exe
1295 # install any hgext3rd.* extensions that we want in the final py2exe
1296 # image here. This is gross, but you gotta do what you gotta do.
1296 # image here. This is gross, but you gotta do what you gotta do.
1297 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1297 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1298
1298
1299 common_depends = [
1299 common_depends = [
1300 'mercurial/bitmanipulation.h',
1300 'mercurial/bitmanipulation.h',
1301 'mercurial/compat.h',
1301 'mercurial/compat.h',
1302 'mercurial/cext/util.h',
1302 'mercurial/cext/util.h',
1303 ]
1303 ]
1304 common_include_dirs = ['mercurial']
1304 common_include_dirs = ['mercurial']
1305
1305
1306 common_cflags = []
1306 common_cflags = []
1307
1307
1308 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1308 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1309 # makes declarations not at the top of a scope in the headers.
1309 # makes declarations not at the top of a scope in the headers.
1310 if os.name != 'nt' and sys.version_info[1] < 9:
1310 if os.name != 'nt' and sys.version_info[1] < 9:
1311 common_cflags = ['-Werror=declaration-after-statement']
1311 common_cflags = ['-Werror=declaration-after-statement']
1312
1312
1313 osutil_cflags = []
1313 osutil_cflags = []
1314 osutil_ldflags = []
1314 osutil_ldflags = []
1315
1315
1316 # platform specific macros
1316 # platform specific macros
1317 for plat, func in [('bsd', 'setproctitle')]:
1317 for plat, func in [('bsd', 'setproctitle')]:
1318 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1318 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1319 osutil_cflags.append('-DHAVE_%s' % func.upper())
1319 osutil_cflags.append('-DHAVE_%s' % func.upper())
1320
1320
1321 for plat, macro, code in [
1321 for plat, macro, code in [
1322 (
1322 (
1323 'bsd|darwin',
1323 'bsd|darwin',
1324 'BSD_STATFS',
1324 'BSD_STATFS',
1325 '''
1325 '''
1326 #include <sys/param.h>
1326 #include <sys/param.h>
1327 #include <sys/mount.h>
1327 #include <sys/mount.h>
1328 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1328 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1329 ''',
1329 ''',
1330 ),
1330 ),
1331 (
1331 (
1332 'linux',
1332 'linux',
1333 'LINUX_STATFS',
1333 'LINUX_STATFS',
1334 '''
1334 '''
1335 #include <linux/magic.h>
1335 #include <linux/magic.h>
1336 #include <sys/vfs.h>
1336 #include <sys/vfs.h>
1337 int main() { struct statfs s; return sizeof(s.f_type); }
1337 int main() { struct statfs s; return sizeof(s.f_type); }
1338 ''',
1338 ''',
1339 ),
1339 ),
1340 ]:
1340 ]:
1341 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1341 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1342 osutil_cflags.append('-DHAVE_%s' % macro)
1342 osutil_cflags.append('-DHAVE_%s' % macro)
1343
1343
1344 if sys.platform == 'darwin':
1344 if sys.platform == 'darwin':
1345 osutil_ldflags += ['-framework', 'ApplicationServices']
1345 osutil_ldflags += ['-framework', 'ApplicationServices']
1346
1346
1347 if sys.platform == 'sunos5':
1347 if sys.platform == 'sunos5':
1348 osutil_ldflags += ['-lsocket']
1348 osutil_ldflags += ['-lsocket']
1349
1349
1350 xdiff_srcs = [
1350 xdiff_srcs = [
1351 'mercurial/thirdparty/xdiff/xdiffi.c',
1351 'mercurial/thirdparty/xdiff/xdiffi.c',
1352 'mercurial/thirdparty/xdiff/xprepare.c',
1352 'mercurial/thirdparty/xdiff/xprepare.c',
1353 'mercurial/thirdparty/xdiff/xutils.c',
1353 'mercurial/thirdparty/xdiff/xutils.c',
1354 ]
1354 ]
1355
1355
1356 xdiff_headers = [
1356 xdiff_headers = [
1357 'mercurial/thirdparty/xdiff/xdiff.h',
1357 'mercurial/thirdparty/xdiff/xdiff.h',
1358 'mercurial/thirdparty/xdiff/xdiffi.h',
1358 'mercurial/thirdparty/xdiff/xdiffi.h',
1359 'mercurial/thirdparty/xdiff/xinclude.h',
1359 'mercurial/thirdparty/xdiff/xinclude.h',
1360 'mercurial/thirdparty/xdiff/xmacros.h',
1360 'mercurial/thirdparty/xdiff/xmacros.h',
1361 'mercurial/thirdparty/xdiff/xprepare.h',
1361 'mercurial/thirdparty/xdiff/xprepare.h',
1362 'mercurial/thirdparty/xdiff/xtypes.h',
1362 'mercurial/thirdparty/xdiff/xtypes.h',
1363 'mercurial/thirdparty/xdiff/xutils.h',
1363 'mercurial/thirdparty/xdiff/xutils.h',
1364 ]
1364 ]
1365
1365
1366
1366
1367 class RustCompilationError(CCompilerError):
1367 class RustCompilationError(CCompilerError):
1368 """Exception class for Rust compilation errors."""
1368 """Exception class for Rust compilation errors."""
1369
1369
1370
1370
1371 class RustExtension(Extension):
1371 class RustExtension(Extension):
1372 """Base classes for concrete Rust Extension classes."""
1372 """Base classes for concrete Rust Extension classes."""
1373
1373
1374 rusttargetdir = os.path.join('rust', 'target', 'release')
1374 rusttargetdir = os.path.join('rust', 'target', 'release')
1375
1375
1376 def __init__(self, mpath, sources, rustlibname, subcrate, **kw):
1376 def __init__(self, mpath, sources, rustlibname, subcrate, **kw):
1377 Extension.__init__(self, mpath, sources, **kw)
1377 Extension.__init__(self, mpath, sources, **kw)
1378 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1378 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1379
1379
1380 # adding Rust source and control files to depends so that the extension
1380 # adding Rust source and control files to depends so that the extension
1381 # gets rebuilt if they've changed
1381 # gets rebuilt if they've changed
1382 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1382 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1383 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1383 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1384 if os.path.exists(cargo_lock):
1384 if os.path.exists(cargo_lock):
1385 self.depends.append(cargo_lock)
1385 self.depends.append(cargo_lock)
1386 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1386 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1387 self.depends.extend(
1387 self.depends.extend(
1388 os.path.join(dirpath, fname)
1388 os.path.join(dirpath, fname)
1389 for fname in fnames
1389 for fname in fnames
1390 if os.path.splitext(fname)[1] == '.rs'
1390 if os.path.splitext(fname)[1] == '.rs'
1391 )
1391 )
1392
1392
1393 @staticmethod
1393 @staticmethod
1394 def rustdylibsuffix():
1394 def rustdylibsuffix():
1395 """Return the suffix for shared libraries produced by rustc.
1395 """Return the suffix for shared libraries produced by rustc.
1396
1396
1397 See also: https://doc.rust-lang.org/reference/linkage.html
1397 See also: https://doc.rust-lang.org/reference/linkage.html
1398 """
1398 """
1399 if sys.platform == 'darwin':
1399 if sys.platform == 'darwin':
1400 return '.dylib'
1400 return '.dylib'
1401 elif os.name == 'nt':
1401 elif os.name == 'nt':
1402 return '.dll'
1402 return '.dll'
1403 else:
1403 else:
1404 return '.so'
1404 return '.so'
1405
1405
1406 def rustbuild(self):
1406 def rustbuild(self):
1407 env = os.environ.copy()
1407 env = os.environ.copy()
1408 if 'HGTEST_RESTOREENV' in env:
1408 if 'HGTEST_RESTOREENV' in env:
1409 # Mercurial tests change HOME to a temporary directory,
1409 # Mercurial tests change HOME to a temporary directory,
1410 # but, if installed with rustup, the Rust toolchain needs
1410 # but, if installed with rustup, the Rust toolchain needs
1411 # HOME to be correct (otherwise the 'no default toolchain'
1411 # HOME to be correct (otherwise the 'no default toolchain'
1412 # error message is issued and the build fails).
1412 # error message is issued and the build fails).
1413 # This happens currently with test-hghave.t, which does
1413 # This happens currently with test-hghave.t, which does
1414 # invoke this build.
1414 # invoke this build.
1415
1415
1416 # Unix only fix (os.path.expanduser not really reliable if
1416 # Unix only fix (os.path.expanduser not really reliable if
1417 # HOME is shadowed like this)
1417 # HOME is shadowed like this)
1418 import pwd
1418 import pwd
1419
1419
1420 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1420 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1421
1421
1422 cargocmd = ['cargo', 'rustc', '--release']
1422 cargocmd = ['cargo', 'rustc', '--release']
1423
1423
1424 rust_features = env.get("HG_RUST_FEATURES")
1424 rust_features = env.get("HG_RUST_FEATURES")
1425 if rust_features:
1425 if rust_features:
1426 cargocmd.extend(('--features', rust_features))
1426 cargocmd.extend(('--features', rust_features))
1427
1427
1428 cargocmd.append('--')
1428 cargocmd.append('--')
1429 if sys.platform == 'darwin':
1429 if sys.platform == 'darwin':
1430 cargocmd.extend(
1430 cargocmd.extend(
1431 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1431 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1432 )
1432 )
1433 try:
1433 try:
1434 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1434 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1435 except FileNotFoundError:
1435 except FileNotFoundError:
1436 raise RustCompilationError("Cargo not found")
1436 raise RustCompilationError("Cargo not found")
1437 except PermissionError:
1437 except PermissionError:
1438 raise RustCompilationError(
1438 raise RustCompilationError(
1439 "Cargo found, but permission to execute it is denied"
1439 "Cargo found, but permission to execute it is denied"
1440 )
1440 )
1441 except subprocess.CalledProcessError:
1441 except subprocess.CalledProcessError:
1442 raise RustCompilationError(
1442 raise RustCompilationError(
1443 "Cargo failed. Working directory: %r, "
1443 "Cargo failed. Working directory: %r, "
1444 "command: %r, environment: %r"
1444 "command: %r, environment: %r"
1445 % (self.rustsrcdir, cargocmd, env)
1445 % (self.rustsrcdir, cargocmd, env)
1446 )
1446 )
1447
1447
1448
1448
1449 class RustStandaloneExtension(RustExtension):
1449 class RustStandaloneExtension(RustExtension):
1450 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1450 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1451 RustExtension.__init__(
1451 RustExtension.__init__(
1452 self, pydottedname, [], dylibname, rustcrate, **kw
1452 self, pydottedname, [], dylibname, rustcrate, **kw
1453 )
1453 )
1454 self.dylibname = dylibname
1454 self.dylibname = dylibname
1455
1455
1456 def build(self, target_dir):
1456 def build(self, target_dir):
1457 self.rustbuild()
1457 self.rustbuild()
1458 target = [target_dir]
1458 target = [target_dir]
1459 target.extend(self.name.split('.'))
1459 target.extend(self.name.split('.'))
1460 target[-1] += DYLIB_SUFFIX
1460 target[-1] += DYLIB_SUFFIX
1461 shutil.copy2(
1461 shutil.copy2(
1462 os.path.join(
1462 os.path.join(
1463 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1463 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1464 ),
1464 ),
1465 os.path.join(*target),
1465 os.path.join(*target),
1466 )
1466 )
1467
1467
1468
1468
1469 extmodules = [
1469 extmodules = [
1470 Extension(
1470 Extension(
1471 'mercurial.cext.base85',
1471 'mercurial.cext.base85',
1472 ['mercurial/cext/base85.c'],
1472 ['mercurial/cext/base85.c'],
1473 include_dirs=common_include_dirs,
1473 include_dirs=common_include_dirs,
1474 extra_compile_args=common_cflags,
1474 extra_compile_args=common_cflags,
1475 depends=common_depends,
1475 depends=common_depends,
1476 ),
1476 ),
1477 Extension(
1477 Extension(
1478 'mercurial.cext.bdiff',
1478 'mercurial.cext.bdiff',
1479 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1479 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1480 include_dirs=common_include_dirs,
1480 include_dirs=common_include_dirs,
1481 extra_compile_args=common_cflags,
1481 extra_compile_args=common_cflags,
1482 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1482 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1483 ),
1483 ),
1484 Extension(
1484 Extension(
1485 'mercurial.cext.mpatch',
1485 'mercurial.cext.mpatch',
1486 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1486 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1487 include_dirs=common_include_dirs,
1487 include_dirs=common_include_dirs,
1488 extra_compile_args=common_cflags,
1488 extra_compile_args=common_cflags,
1489 depends=common_depends,
1489 depends=common_depends,
1490 ),
1490 ),
1491 Extension(
1491 Extension(
1492 'mercurial.cext.parsers',
1492 'mercurial.cext.parsers',
1493 [
1493 [
1494 'mercurial/cext/charencode.c',
1494 'mercurial/cext/charencode.c',
1495 'mercurial/cext/dirs.c',
1495 'mercurial/cext/dirs.c',
1496 'mercurial/cext/manifest.c',
1496 'mercurial/cext/manifest.c',
1497 'mercurial/cext/parsers.c',
1497 'mercurial/cext/parsers.c',
1498 'mercurial/cext/pathencode.c',
1498 'mercurial/cext/pathencode.c',
1499 'mercurial/cext/revlog.c',
1499 'mercurial/cext/revlog.c',
1500 ],
1500 ],
1501 include_dirs=common_include_dirs,
1501 include_dirs=common_include_dirs,
1502 extra_compile_args=common_cflags,
1502 extra_compile_args=common_cflags,
1503 depends=common_depends
1503 depends=common_depends
1504 + [
1504 + [
1505 'mercurial/cext/charencode.h',
1505 'mercurial/cext/charencode.h',
1506 'mercurial/cext/revlog.h',
1506 'mercurial/cext/revlog.h',
1507 ],
1507 ],
1508 ),
1508 ),
1509 Extension(
1509 Extension(
1510 'mercurial.cext.osutil',
1510 'mercurial.cext.osutil',
1511 ['mercurial/cext/osutil.c'],
1511 ['mercurial/cext/osutil.c'],
1512 include_dirs=common_include_dirs,
1512 include_dirs=common_include_dirs,
1513 extra_compile_args=common_cflags + osutil_cflags,
1513 extra_compile_args=common_cflags + osutil_cflags,
1514 extra_link_args=osutil_ldflags,
1514 extra_link_args=osutil_ldflags,
1515 depends=common_depends,
1515 depends=common_depends,
1516 ),
1516 ),
1517 Extension(
1517 Extension(
1518 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1518 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1519 [
1519 [
1520 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1520 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1521 ],
1521 ],
1522 extra_compile_args=common_cflags,
1522 extra_compile_args=common_cflags,
1523 ),
1523 ),
1524 Extension(
1524 Extension(
1525 'mercurial.thirdparty.sha1dc',
1525 'mercurial.thirdparty.sha1dc',
1526 [
1526 [
1527 'mercurial/thirdparty/sha1dc/cext.c',
1527 'mercurial/thirdparty/sha1dc/cext.c',
1528 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1528 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1529 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1529 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1530 ],
1530 ],
1531 extra_compile_args=common_cflags,
1531 extra_compile_args=common_cflags,
1532 ),
1532 ),
1533 Extension(
1533 Extension(
1534 'hgext.fsmonitor.pywatchman.bser',
1534 'hgext.fsmonitor.pywatchman.bser',
1535 ['hgext/fsmonitor/pywatchman/bser.c'],
1535 ['hgext/fsmonitor/pywatchman/bser.c'],
1536 extra_compile_args=common_cflags,
1536 extra_compile_args=common_cflags,
1537 ),
1537 ),
1538 RustStandaloneExtension(
1538 RustStandaloneExtension(
1539 'mercurial.rustext',
1539 'mercurial.rustext',
1540 'hg-cpython',
1540 'hg-cpython',
1541 'librusthg',
1541 'librusthg',
1542 ),
1542 ),
1543 ]
1543 ]
1544
1544
1545
1545
1546 sys.path.insert(0, 'contrib/python-zstandard')
1546 sys.path.insert(0, 'contrib/python-zstandard')
1547 import setup_zstd
1547 import setup_zstd
1548
1548
1549 zstd = setup_zstd.get_c_extension(
1549 zstd = setup_zstd.get_c_extension(
1550 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1550 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1551 )
1551 )
1552 zstd.extra_compile_args += common_cflags
1552 zstd.extra_compile_args += common_cflags
1553 extmodules.append(zstd)
1553 extmodules.append(zstd)
1554
1554
1555 try:
1555 try:
1556 from distutils import cygwinccompiler
1556 from distutils import cygwinccompiler
1557
1557
1558 # the -mno-cygwin option has been deprecated for years
1558 # the -mno-cygwin option has been deprecated for years
1559 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1559 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1560
1560
1561 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1561 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1562 def __init__(self, *args, **kwargs):
1562 def __init__(self, *args, **kwargs):
1563 mingw32compilerclass.__init__(self, *args, **kwargs)
1563 mingw32compilerclass.__init__(self, *args, **kwargs)
1564 for i in 'compiler compiler_so linker_exe linker_so'.split():
1564 for i in 'compiler compiler_so linker_exe linker_so'.split():
1565 try:
1565 try:
1566 getattr(self, i).remove('-mno-cygwin')
1566 getattr(self, i).remove('-mno-cygwin')
1567 except ValueError:
1567 except ValueError:
1568 pass
1568 pass
1569
1569
1570 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1570 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1571 except ImportError:
1571 except ImportError:
1572 # the cygwinccompiler package is not available on some Python
1572 # the cygwinccompiler package is not available on some Python
1573 # distributions like the ones from the optware project for Synology
1573 # distributions like the ones from the optware project for Synology
1574 # DiskStation boxes
1574 # DiskStation boxes
1575 class HackedMingw32CCompiler:
1575 class HackedMingw32CCompiler:
1576 pass
1576 pass
1577
1577
1578
1578
1579 if os.name == 'nt':
1579 if os.name == 'nt':
1580 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1580 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1581 # extra_link_args to distutils.extensions.Extension() doesn't have any
1581 # extra_link_args to distutils.extensions.Extension() doesn't have any
1582 # effect.
1582 # effect.
1583 from distutils import msvccompiler
1583 from distutils import msvccompiler
1584
1584
1585 msvccompilerclass = msvccompiler.MSVCCompiler
1585 msvccompilerclass = msvccompiler.MSVCCompiler
1586
1586
1587 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1587 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1588 def initialize(self):
1588 def initialize(self):
1589 msvccompilerclass.initialize(self)
1589 msvccompilerclass.initialize(self)
1590 # "warning LNK4197: export 'func' specified multiple times"
1590 # "warning LNK4197: export 'func' specified multiple times"
1591 self.ldflags_shared.append('/ignore:4197')
1591 self.ldflags_shared.append('/ignore:4197')
1592 self.ldflags_shared_debug.append('/ignore:4197')
1592 self.ldflags_shared_debug.append('/ignore:4197')
1593
1593
1594 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1594 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1595
1595
1596 packagedata = {
1596 packagedata = {
1597 'mercurial': [
1597 'mercurial': [
1598 'locale/*/LC_MESSAGES/hg.mo',
1598 'locale/*/LC_MESSAGES/hg.mo',
1599 'dummycert.pem',
1599 'dummycert.pem',
1600 ],
1600 ],
1601 'mercurial.defaultrc': [
1601 'mercurial.defaultrc': [
1602 '*.rc',
1602 '*.rc',
1603 ],
1603 ],
1604 'mercurial.helptext': [
1604 'mercurial.helptext': [
1605 '*.txt',
1605 '*.txt',
1606 ],
1606 ],
1607 'mercurial.helptext.internals': [
1607 'mercurial.helptext.internals': [
1608 '*.txt',
1608 '*.txt',
1609 ],
1609 ],
1610 'mercurial.thirdparty.attr': [
1611 '*.pyi',
1612 'py.typed',
1613 ],
1610 }
1614 }
1611
1615
1612
1616
1613 def ordinarypath(p):
1617 def ordinarypath(p):
1614 return p and p[0] != '.' and p[-1] != '~'
1618 return p and p[0] != '.' and p[-1] != '~'
1615
1619
1616
1620
1617 for root in ('templates',):
1621 for root in ('templates',):
1618 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1622 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1619 packagename = curdir.replace(os.sep, '.')
1623 packagename = curdir.replace(os.sep, '.')
1620 packagedata[packagename] = list(filter(ordinarypath, files))
1624 packagedata[packagename] = list(filter(ordinarypath, files))
1621
1625
1622 datafiles = []
1626 datafiles = []
1623
1627
1624 # distutils expects version to be str/unicode. Converting it to
1628 # distutils expects version to be str/unicode. Converting it to
1625 # unicode on Python 2 still works because it won't contain any
1629 # unicode on Python 2 still works because it won't contain any
1626 # non-ascii bytes and will be implicitly converted back to bytes
1630 # non-ascii bytes and will be implicitly converted back to bytes
1627 # when operated on.
1631 # when operated on.
1628 assert isinstance(version, str)
1632 assert isinstance(version, str)
1629 setupversion = version
1633 setupversion = version
1630
1634
1631 extra = {}
1635 extra = {}
1632
1636
1633 py2exepackages = [
1637 py2exepackages = [
1634 'hgdemandimport',
1638 'hgdemandimport',
1635 'hgext3rd',
1639 'hgext3rd',
1636 'hgext',
1640 'hgext',
1637 'email',
1641 'email',
1638 # implicitly imported per module policy
1642 # implicitly imported per module policy
1639 # (cffi wouldn't be used as a frozen exe)
1643 # (cffi wouldn't be used as a frozen exe)
1640 'mercurial.cext',
1644 'mercurial.cext',
1641 #'mercurial.cffi',
1645 #'mercurial.cffi',
1642 'mercurial.pure',
1646 'mercurial.pure',
1643 ]
1647 ]
1644
1648
1645 py2exe_includes = []
1649 py2exe_includes = []
1646
1650
1647 py2exeexcludes = []
1651 py2exeexcludes = []
1648 py2exedllexcludes = ['crypt32.dll']
1652 py2exedllexcludes = ['crypt32.dll']
1649
1653
1650 if issetuptools:
1654 if issetuptools:
1651 extra['python_requires'] = supportedpy
1655 extra['python_requires'] = supportedpy
1652
1656
1653 if py2exeloaded:
1657 if py2exeloaded:
1654 extra['console'] = [
1658 extra['console'] = [
1655 {
1659 {
1656 'script': 'hg',
1660 'script': 'hg',
1657 'copyright': 'Copyright (C) 2005-2022 Olivia Mackall and others',
1661 'copyright': 'Copyright (C) 2005-2022 Olivia Mackall and others',
1658 'product_version': version,
1662 'product_version': version,
1659 }
1663 }
1660 ]
1664 ]
1661 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1665 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1662 # Need to override hgbuild because it has a private copy of
1666 # Need to override hgbuild because it has a private copy of
1663 # build.sub_commands.
1667 # build.sub_commands.
1664 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1668 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1665 # put dlls in sub directory so that they won't pollute PATH
1669 # put dlls in sub directory so that they won't pollute PATH
1666 extra['zipfile'] = 'lib/library.zip'
1670 extra['zipfile'] = 'lib/library.zip'
1667
1671
1668 # We allow some configuration to be supplemented via environment
1672 # We allow some configuration to be supplemented via environment
1669 # variables. This is better than setup.cfg files because it allows
1673 # variables. This is better than setup.cfg files because it allows
1670 # supplementing configs instead of replacing them.
1674 # supplementing configs instead of replacing them.
1671 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1675 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1672 if extrapackages:
1676 if extrapackages:
1673 py2exepackages.extend(extrapackages.split(' '))
1677 py2exepackages.extend(extrapackages.split(' '))
1674
1678
1675 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
1679 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
1676 if extra_includes:
1680 if extra_includes:
1677 py2exe_includes.extend(extra_includes.split(' '))
1681 py2exe_includes.extend(extra_includes.split(' '))
1678
1682
1679 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1683 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1680 if excludes:
1684 if excludes:
1681 py2exeexcludes.extend(excludes.split(' '))
1685 py2exeexcludes.extend(excludes.split(' '))
1682
1686
1683 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1687 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1684 if dllexcludes:
1688 if dllexcludes:
1685 py2exedllexcludes.extend(dllexcludes.split(' '))
1689 py2exedllexcludes.extend(dllexcludes.split(' '))
1686
1690
1687 if os.environ.get('PYOXIDIZER'):
1691 if os.environ.get('PYOXIDIZER'):
1688 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1692 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1689
1693
1690 if os.name == 'nt':
1694 if os.name == 'nt':
1691 # Windows binary file versions for exe/dll files must have the
1695 # Windows binary file versions for exe/dll files must have the
1692 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1696 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1693 setupversion = setupversion.split(r'+', 1)[0]
1697 setupversion = setupversion.split(r'+', 1)[0]
1694
1698
1695 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1699 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1696 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1700 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1697 if version:
1701 if version:
1698 version = version[0].decode('utf-8')
1702 version = version[0].decode('utf-8')
1699 xcode4 = version.startswith('Xcode') and StrictVersion(
1703 xcode4 = version.startswith('Xcode') and StrictVersion(
1700 version.split()[1]
1704 version.split()[1]
1701 ) >= StrictVersion('4.0')
1705 ) >= StrictVersion('4.0')
1702 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1706 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1703 else:
1707 else:
1704 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1708 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1705 # installed, but instead with only command-line tools. Assume
1709 # installed, but instead with only command-line tools. Assume
1706 # that only happens on >= Lion, thus no PPC support.
1710 # that only happens on >= Lion, thus no PPC support.
1707 xcode4 = True
1711 xcode4 = True
1708 xcode51 = False
1712 xcode51 = False
1709
1713
1710 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1714 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1711 # distutils.sysconfig
1715 # distutils.sysconfig
1712 if xcode4:
1716 if xcode4:
1713 os.environ['ARCHFLAGS'] = ''
1717 os.environ['ARCHFLAGS'] = ''
1714
1718
1715 # XCode 5.1 changes clang such that it now fails to compile if the
1719 # XCode 5.1 changes clang such that it now fails to compile if the
1716 # -mno-fused-madd flag is passed, but the version of Python shipped with
1720 # -mno-fused-madd flag is passed, but the version of Python shipped with
1717 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1721 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1718 # C extension modules, and a bug has been filed upstream at
1722 # C extension modules, and a bug has been filed upstream at
1719 # http://bugs.python.org/issue21244. We also need to patch this here
1723 # http://bugs.python.org/issue21244. We also need to patch this here
1720 # so Mercurial can continue to compile in the meantime.
1724 # so Mercurial can continue to compile in the meantime.
1721 if xcode51:
1725 if xcode51:
1722 cflags = get_config_var('CFLAGS')
1726 cflags = get_config_var('CFLAGS')
1723 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1727 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1724 os.environ['CFLAGS'] = (
1728 os.environ['CFLAGS'] = (
1725 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1729 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1726 )
1730 )
1727
1731
1728 setup(
1732 setup(
1729 name='mercurial',
1733 name='mercurial',
1730 version=setupversion,
1734 version=setupversion,
1731 author='Olivia Mackall and many others',
1735 author='Olivia Mackall and many others',
1732 author_email='mercurial@mercurial-scm.org',
1736 author_email='mercurial@mercurial-scm.org',
1733 url='https://mercurial-scm.org/',
1737 url='https://mercurial-scm.org/',
1734 download_url='https://mercurial-scm.org/release/',
1738 download_url='https://mercurial-scm.org/release/',
1735 description=(
1739 description=(
1736 'Fast scalable distributed SCM (revision control, version '
1740 'Fast scalable distributed SCM (revision control, version '
1737 'control) system'
1741 'control) system'
1738 ),
1742 ),
1739 long_description=(
1743 long_description=(
1740 'Mercurial is a distributed SCM tool written in Python.'
1744 'Mercurial is a distributed SCM tool written in Python.'
1741 ' It is used by a number of large projects that require'
1745 ' It is used by a number of large projects that require'
1742 ' fast, reliable distributed revision control, such as '
1746 ' fast, reliable distributed revision control, such as '
1743 'Mozilla.'
1747 'Mozilla.'
1744 ),
1748 ),
1745 license='GNU GPLv2 or any later version',
1749 license='GNU GPLv2 or any later version',
1746 classifiers=[
1750 classifiers=[
1747 'Development Status :: 6 - Mature',
1751 'Development Status :: 6 - Mature',
1748 'Environment :: Console',
1752 'Environment :: Console',
1749 'Intended Audience :: Developers',
1753 'Intended Audience :: Developers',
1750 'Intended Audience :: System Administrators',
1754 'Intended Audience :: System Administrators',
1751 'License :: OSI Approved :: GNU General Public License (GPL)',
1755 'License :: OSI Approved :: GNU General Public License (GPL)',
1752 'Natural Language :: Danish',
1756 'Natural Language :: Danish',
1753 'Natural Language :: English',
1757 'Natural Language :: English',
1754 'Natural Language :: German',
1758 'Natural Language :: German',
1755 'Natural Language :: Italian',
1759 'Natural Language :: Italian',
1756 'Natural Language :: Japanese',
1760 'Natural Language :: Japanese',
1757 'Natural Language :: Portuguese (Brazilian)',
1761 'Natural Language :: Portuguese (Brazilian)',
1758 'Operating System :: Microsoft :: Windows',
1762 'Operating System :: Microsoft :: Windows',
1759 'Operating System :: OS Independent',
1763 'Operating System :: OS Independent',
1760 'Operating System :: POSIX',
1764 'Operating System :: POSIX',
1761 'Programming Language :: C',
1765 'Programming Language :: C',
1762 'Programming Language :: Python',
1766 'Programming Language :: Python',
1763 'Topic :: Software Development :: Version Control',
1767 'Topic :: Software Development :: Version Control',
1764 ],
1768 ],
1765 scripts=scripts,
1769 scripts=scripts,
1766 packages=packages,
1770 packages=packages,
1767 ext_modules=extmodules,
1771 ext_modules=extmodules,
1768 data_files=datafiles,
1772 data_files=datafiles,
1769 package_data=packagedata,
1773 package_data=packagedata,
1770 cmdclass=cmdclass,
1774 cmdclass=cmdclass,
1771 distclass=hgdist,
1775 distclass=hgdist,
1772 options={
1776 options={
1773 'py2exe': {
1777 'py2exe': {
1774 'bundle_files': 3,
1778 'bundle_files': 3,
1775 'dll_excludes': py2exedllexcludes,
1779 'dll_excludes': py2exedllexcludes,
1776 'includes': py2exe_includes,
1780 'includes': py2exe_includes,
1777 'excludes': py2exeexcludes,
1781 'excludes': py2exeexcludes,
1778 'packages': py2exepackages,
1782 'packages': py2exepackages,
1779 },
1783 },
1780 'bdist_mpkg': {
1784 'bdist_mpkg': {
1781 'zipdist': False,
1785 'zipdist': False,
1782 'license': 'COPYING',
1786 'license': 'COPYING',
1783 'readme': 'contrib/packaging/macosx/Readme.html',
1787 'readme': 'contrib/packaging/macosx/Readme.html',
1784 'welcome': 'contrib/packaging/macosx/Welcome.html',
1788 'welcome': 'contrib/packaging/macosx/Welcome.html',
1785 },
1789 },
1786 },
1790 },
1787 **extra
1791 **extra
1788 )
1792 )
General Comments 0
You need to be logged in to leave comments. Login now