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