##// END OF EJS Templates
packaging: extract pre-computed version when running from plain tarball...
Joerg Sonnenberger -
r48144:8d2b62d7 5.8rc1 stable
parent child Browse files
Show More
@@ -1,1852 +1,1855 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'):
441 data = open('mercurial/__version__.py').read()
442 version = re.search('version = b"(.*)"', data).group(1)
440
443
441 if version:
444 if version:
442 versionb = version
445 versionb = version
443 if not isinstance(versionb, bytes):
446 if not isinstance(versionb, bytes):
444 versionb = versionb.encode('ascii')
447 versionb = versionb.encode('ascii')
445
448
446 write_if_changed(
449 write_if_changed(
447 'mercurial/__version__.py',
450 'mercurial/__version__.py',
448 b''.join(
451 b''.join(
449 [
452 [
450 b'# this file is autogenerated by setup.py\n'
453 b'# this file is autogenerated by setup.py\n'
451 b'version = b"%s"\n' % versionb,
454 b'version = b"%s"\n' % versionb,
452 ]
455 ]
453 ),
456 ),
454 )
457 )
455
458
456
459
457 class hgbuild(build):
460 class hgbuild(build):
458 # Insert hgbuildmo first so that files in mercurial/locale/ are found
461 # Insert hgbuildmo first so that files in mercurial/locale/ are found
459 # when build_py is run next.
462 # when build_py is run next.
460 sub_commands = [('build_mo', None)] + build.sub_commands
463 sub_commands = [('build_mo', None)] + build.sub_commands
461
464
462
465
463 class hgbuildmo(build):
466 class hgbuildmo(build):
464
467
465 description = "build translations (.mo files)"
468 description = "build translations (.mo files)"
466
469
467 def run(self):
470 def run(self):
468 if not find_executable('msgfmt'):
471 if not find_executable('msgfmt'):
469 self.warn(
472 self.warn(
470 "could not find msgfmt executable, no translations "
473 "could not find msgfmt executable, no translations "
471 "will be built"
474 "will be built"
472 )
475 )
473 return
476 return
474
477
475 podir = 'i18n'
478 podir = 'i18n'
476 if not os.path.isdir(podir):
479 if not os.path.isdir(podir):
477 self.warn("could not find %s/ directory" % podir)
480 self.warn("could not find %s/ directory" % podir)
478 return
481 return
479
482
480 join = os.path.join
483 join = os.path.join
481 for po in os.listdir(podir):
484 for po in os.listdir(podir):
482 if not po.endswith('.po'):
485 if not po.endswith('.po'):
483 continue
486 continue
484 pofile = join(podir, po)
487 pofile = join(podir, po)
485 modir = join('locale', po[:-3], 'LC_MESSAGES')
488 modir = join('locale', po[:-3], 'LC_MESSAGES')
486 mofile = join(modir, 'hg.mo')
489 mofile = join(modir, 'hg.mo')
487 mobuildfile = join('mercurial', mofile)
490 mobuildfile = join('mercurial', mofile)
488 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
491 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
489 if sys.platform != 'sunos5':
492 if sys.platform != 'sunos5':
490 # msgfmt on Solaris does not know about -c
493 # msgfmt on Solaris does not know about -c
491 cmd.append('-c')
494 cmd.append('-c')
492 self.mkpath(join('mercurial', modir))
495 self.mkpath(join('mercurial', modir))
493 self.make_file([pofile], mobuildfile, spawn, (cmd,))
496 self.make_file([pofile], mobuildfile, spawn, (cmd,))
494
497
495
498
496 class hgdist(Distribution):
499 class hgdist(Distribution):
497 pure = False
500 pure = False
498 rust = False
501 rust = False
499 no_rust = False
502 no_rust = False
500 cffi = ispypy
503 cffi = ispypy
501
504
502 global_options = Distribution.global_options + [
505 global_options = Distribution.global_options + [
503 ('pure', None, "use pure (slow) Python code instead of C extensions"),
506 ('pure', None, "use pure (slow) Python code instead of C extensions"),
504 ('rust', None, "use Rust extensions additionally to C extensions"),
507 ('rust', None, "use Rust extensions additionally to C extensions"),
505 (
508 (
506 'no-rust',
509 'no-rust',
507 None,
510 None,
508 "do not use Rust extensions additionally to C extensions",
511 "do not use Rust extensions additionally to C extensions",
509 ),
512 ),
510 ]
513 ]
511
514
512 negative_opt = Distribution.negative_opt.copy()
515 negative_opt = Distribution.negative_opt.copy()
513 boolean_options = ['pure', 'rust', 'no-rust']
516 boolean_options = ['pure', 'rust', 'no-rust']
514 negative_opt['no-rust'] = 'rust'
517 negative_opt['no-rust'] = 'rust'
515
518
516 def _set_command_options(self, command_obj, option_dict=None):
519 def _set_command_options(self, command_obj, option_dict=None):
517 # Not all distutils versions in the wild have boolean_options.
520 # Not all distutils versions in the wild have boolean_options.
518 # This should be cleaned up when we're Python 3 only.
521 # This should be cleaned up when we're Python 3 only.
519 command_obj.boolean_options = (
522 command_obj.boolean_options = (
520 getattr(command_obj, 'boolean_options', []) + self.boolean_options
523 getattr(command_obj, 'boolean_options', []) + self.boolean_options
521 )
524 )
522 return Distribution._set_command_options(
525 return Distribution._set_command_options(
523 self, command_obj, option_dict=option_dict
526 self, command_obj, option_dict=option_dict
524 )
527 )
525
528
526 def parse_command_line(self):
529 def parse_command_line(self):
527 ret = Distribution.parse_command_line(self)
530 ret = Distribution.parse_command_line(self)
528 if not (self.rust or self.no_rust):
531 if not (self.rust or self.no_rust):
529 hgrustext = os.environ.get('HGWITHRUSTEXT')
532 hgrustext = os.environ.get('HGWITHRUSTEXT')
530 # TODO record it for proper rebuild upon changes
533 # TODO record it for proper rebuild upon changes
531 # (see mercurial/__modulepolicy__.py)
534 # (see mercurial/__modulepolicy__.py)
532 if hgrustext != 'cpython' and hgrustext is not None:
535 if hgrustext != 'cpython' and hgrustext is not None:
533 if hgrustext:
536 if hgrustext:
534 msg = 'unkown HGWITHRUSTEXT value: %s' % hgrustext
537 msg = 'unkown HGWITHRUSTEXT value: %s' % hgrustext
535 printf(msg, file=sys.stderr)
538 printf(msg, file=sys.stderr)
536 hgrustext = None
539 hgrustext = None
537 self.rust = hgrustext is not None
540 self.rust = hgrustext is not None
538 self.no_rust = not self.rust
541 self.no_rust = not self.rust
539 return ret
542 return ret
540
543
541 def has_ext_modules(self):
544 def has_ext_modules(self):
542 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
545 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
543 # too late for some cases
546 # too late for some cases
544 return not self.pure and Distribution.has_ext_modules(self)
547 return not self.pure and Distribution.has_ext_modules(self)
545
548
546
549
547 # This is ugly as a one-liner. So use a variable.
550 # This is ugly as a one-liner. So use a variable.
548 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
551 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
549 buildextnegops['no-zstd'] = 'zstd'
552 buildextnegops['no-zstd'] = 'zstd'
550 buildextnegops['no-rust'] = 'rust'
553 buildextnegops['no-rust'] = 'rust'
551
554
552
555
553 class hgbuildext(build_ext):
556 class hgbuildext(build_ext):
554 user_options = build_ext.user_options + [
557 user_options = build_ext.user_options + [
555 ('zstd', None, 'compile zstd bindings [default]'),
558 ('zstd', None, 'compile zstd bindings [default]'),
556 ('no-zstd', None, 'do not compile zstd bindings'),
559 ('no-zstd', None, 'do not compile zstd bindings'),
557 (
560 (
558 'rust',
561 'rust',
559 None,
562 None,
560 'compile Rust extensions if they are in use '
563 'compile Rust extensions if they are in use '
561 '(requires Cargo) [default]',
564 '(requires Cargo) [default]',
562 ),
565 ),
563 ('no-rust', None, 'do not compile Rust extensions'),
566 ('no-rust', None, 'do not compile Rust extensions'),
564 ]
567 ]
565
568
566 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
569 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
567 negative_opt = buildextnegops
570 negative_opt = buildextnegops
568
571
569 def initialize_options(self):
572 def initialize_options(self):
570 self.zstd = True
573 self.zstd = True
571 self.rust = True
574 self.rust = True
572
575
573 return build_ext.initialize_options(self)
576 return build_ext.initialize_options(self)
574
577
575 def finalize_options(self):
578 def finalize_options(self):
576 # Unless overridden by the end user, build extensions in parallel.
579 # Unless overridden by the end user, build extensions in parallel.
577 # Only influences behavior on Python 3.5+.
580 # Only influences behavior on Python 3.5+.
578 if getattr(self, 'parallel', None) is None:
581 if getattr(self, 'parallel', None) is None:
579 self.parallel = True
582 self.parallel = True
580
583
581 return build_ext.finalize_options(self)
584 return build_ext.finalize_options(self)
582
585
583 def build_extensions(self):
586 def build_extensions(self):
584 ruststandalones = [
587 ruststandalones = [
585 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
588 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
586 ]
589 ]
587 self.extensions = [
590 self.extensions = [
588 e for e in self.extensions if e not in ruststandalones
591 e for e in self.extensions if e not in ruststandalones
589 ]
592 ]
590 # Filter out zstd if disabled via argument.
593 # Filter out zstd if disabled via argument.
591 if not self.zstd:
594 if not self.zstd:
592 self.extensions = [
595 self.extensions = [
593 e for e in self.extensions if e.name != 'mercurial.zstd'
596 e for e in self.extensions if e.name != 'mercurial.zstd'
594 ]
597 ]
595
598
596 # Build Rust standalon extensions if it'll be used
599 # Build Rust standalon extensions if it'll be used
597 # and its build is not explictely disabled (for external build
600 # and its build is not explictely disabled (for external build
598 # as Linux distributions would do)
601 # as Linux distributions would do)
599 if self.distribution.rust and self.rust:
602 if self.distribution.rust and self.rust:
600 if not sys.platform.startswith('linux'):
603 if not sys.platform.startswith('linux'):
601 self.warn(
604 self.warn(
602 "rust extensions have only been tested on Linux "
605 "rust extensions have only been tested on Linux "
603 "and may not behave correctly on other platforms"
606 "and may not behave correctly on other platforms"
604 )
607 )
605
608
606 for rustext in ruststandalones:
609 for rustext in ruststandalones:
607 rustext.build('' if self.inplace else self.build_lib)
610 rustext.build('' if self.inplace else self.build_lib)
608
611
609 return build_ext.build_extensions(self)
612 return build_ext.build_extensions(self)
610
613
611 def build_extension(self, ext):
614 def build_extension(self, ext):
612 if (
615 if (
613 self.distribution.rust
616 self.distribution.rust
614 and self.rust
617 and self.rust
615 and isinstance(ext, RustExtension)
618 and isinstance(ext, RustExtension)
616 ):
619 ):
617 ext.rustbuild()
620 ext.rustbuild()
618 try:
621 try:
619 build_ext.build_extension(self, ext)
622 build_ext.build_extension(self, ext)
620 except CCompilerError:
623 except CCompilerError:
621 if not getattr(ext, 'optional', False):
624 if not getattr(ext, 'optional', False):
622 raise
625 raise
623 log.warn(
626 log.warn(
624 "Failed to build optional extension '%s' (skipping)", ext.name
627 "Failed to build optional extension '%s' (skipping)", ext.name
625 )
628 )
626
629
627
630
628 class hgbuildscripts(build_scripts):
631 class hgbuildscripts(build_scripts):
629 def run(self):
632 def run(self):
630 if os.name != 'nt' or self.distribution.pure:
633 if os.name != 'nt' or self.distribution.pure:
631 return build_scripts.run(self)
634 return build_scripts.run(self)
632
635
633 exebuilt = False
636 exebuilt = False
634 try:
637 try:
635 self.run_command('build_hgexe')
638 self.run_command('build_hgexe')
636 exebuilt = True
639 exebuilt = True
637 except (DistutilsError, CCompilerError):
640 except (DistutilsError, CCompilerError):
638 log.warn('failed to build optional hg.exe')
641 log.warn('failed to build optional hg.exe')
639
642
640 if exebuilt:
643 if exebuilt:
641 # Copying hg.exe to the scripts build directory ensures it is
644 # Copying hg.exe to the scripts build directory ensures it is
642 # installed by the install_scripts command.
645 # installed by the install_scripts command.
643 hgexecommand = self.get_finalized_command('build_hgexe')
646 hgexecommand = self.get_finalized_command('build_hgexe')
644 dest = os.path.join(self.build_dir, 'hg.exe')
647 dest = os.path.join(self.build_dir, 'hg.exe')
645 self.mkpath(self.build_dir)
648 self.mkpath(self.build_dir)
646 self.copy_file(hgexecommand.hgexepath, dest)
649 self.copy_file(hgexecommand.hgexepath, dest)
647
650
648 # Remove hg.bat because it is redundant with hg.exe.
651 # Remove hg.bat because it is redundant with hg.exe.
649 self.scripts.remove('contrib/win32/hg.bat')
652 self.scripts.remove('contrib/win32/hg.bat')
650
653
651 return build_scripts.run(self)
654 return build_scripts.run(self)
652
655
653
656
654 class hgbuildpy(build_py):
657 class hgbuildpy(build_py):
655 def finalize_options(self):
658 def finalize_options(self):
656 build_py.finalize_options(self)
659 build_py.finalize_options(self)
657
660
658 if self.distribution.pure:
661 if self.distribution.pure:
659 self.distribution.ext_modules = []
662 self.distribution.ext_modules = []
660 elif self.distribution.cffi:
663 elif self.distribution.cffi:
661 from mercurial.cffi import (
664 from mercurial.cffi import (
662 bdiffbuild,
665 bdiffbuild,
663 mpatchbuild,
666 mpatchbuild,
664 )
667 )
665
668
666 exts = [
669 exts = [
667 mpatchbuild.ffi.distutils_extension(),
670 mpatchbuild.ffi.distutils_extension(),
668 bdiffbuild.ffi.distutils_extension(),
671 bdiffbuild.ffi.distutils_extension(),
669 ]
672 ]
670 # cffi modules go here
673 # cffi modules go here
671 if sys.platform == 'darwin':
674 if sys.platform == 'darwin':
672 from mercurial.cffi import osutilbuild
675 from mercurial.cffi import osutilbuild
673
676
674 exts.append(osutilbuild.ffi.distutils_extension())
677 exts.append(osutilbuild.ffi.distutils_extension())
675 self.distribution.ext_modules = exts
678 self.distribution.ext_modules = exts
676 else:
679 else:
677 h = os.path.join(get_python_inc(), 'Python.h')
680 h = os.path.join(get_python_inc(), 'Python.h')
678 if not os.path.exists(h):
681 if not os.path.exists(h):
679 raise SystemExit(
682 raise SystemExit(
680 'Python headers are required to build '
683 'Python headers are required to build '
681 'Mercurial but weren\'t found in %s' % h
684 'Mercurial but weren\'t found in %s' % h
682 )
685 )
683
686
684 def run(self):
687 def run(self):
685 basepath = os.path.join(self.build_lib, 'mercurial')
688 basepath = os.path.join(self.build_lib, 'mercurial')
686 self.mkpath(basepath)
689 self.mkpath(basepath)
687
690
688 rust = self.distribution.rust
691 rust = self.distribution.rust
689 if self.distribution.pure:
692 if self.distribution.pure:
690 modulepolicy = 'py'
693 modulepolicy = 'py'
691 elif self.build_lib == '.':
694 elif self.build_lib == '.':
692 # in-place build should run without rebuilding and Rust extensions
695 # in-place build should run without rebuilding and Rust extensions
693 modulepolicy = 'rust+c-allow' if rust else 'allow'
696 modulepolicy = 'rust+c-allow' if rust else 'allow'
694 else:
697 else:
695 modulepolicy = 'rust+c' if rust else 'c'
698 modulepolicy = 'rust+c' if rust else 'c'
696
699
697 content = b''.join(
700 content = b''.join(
698 [
701 [
699 b'# this file is autogenerated by setup.py\n',
702 b'# this file is autogenerated by setup.py\n',
700 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
703 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
701 ]
704 ]
702 )
705 )
703 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
706 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
704
707
705 build_py.run(self)
708 build_py.run(self)
706
709
707
710
708 class buildhgextindex(Command):
711 class buildhgextindex(Command):
709 description = 'generate prebuilt index of hgext (for frozen package)'
712 description = 'generate prebuilt index of hgext (for frozen package)'
710 user_options = []
713 user_options = []
711 _indexfilename = 'hgext/__index__.py'
714 _indexfilename = 'hgext/__index__.py'
712
715
713 def initialize_options(self):
716 def initialize_options(self):
714 pass
717 pass
715
718
716 def finalize_options(self):
719 def finalize_options(self):
717 pass
720 pass
718
721
719 def run(self):
722 def run(self):
720 if os.path.exists(self._indexfilename):
723 if os.path.exists(self._indexfilename):
721 with open(self._indexfilename, 'w') as f:
724 with open(self._indexfilename, 'w') as f:
722 f.write('# empty\n')
725 f.write('# empty\n')
723
726
724 # here no extension enabled, disabled() lists up everything
727 # here no extension enabled, disabled() lists up everything
725 code = (
728 code = (
726 'import pprint; from mercurial import extensions; '
729 'import pprint; from mercurial import extensions; '
727 'ext = extensions.disabled();'
730 'ext = extensions.disabled();'
728 'ext.pop("__index__", None);'
731 'ext.pop("__index__", None);'
729 'pprint.pprint(ext)'
732 'pprint.pprint(ext)'
730 )
733 )
731 returncode, out, err = runcmd(
734 returncode, out, err = runcmd(
732 [sys.executable, '-c', code], localhgenv()
735 [sys.executable, '-c', code], localhgenv()
733 )
736 )
734 if err or returncode != 0:
737 if err or returncode != 0:
735 raise DistutilsExecError(err)
738 raise DistutilsExecError(err)
736
739
737 with open(self._indexfilename, 'wb') as f:
740 with open(self._indexfilename, 'wb') as f:
738 f.write(b'# this file is autogenerated by setup.py\n')
741 f.write(b'# this file is autogenerated by setup.py\n')
739 f.write(b'docs = ')
742 f.write(b'docs = ')
740 f.write(out)
743 f.write(out)
741
744
742
745
743 class buildhgexe(build_ext):
746 class buildhgexe(build_ext):
744 description = 'compile hg.exe from mercurial/exewrapper.c'
747 description = 'compile hg.exe from mercurial/exewrapper.c'
745 user_options = build_ext.user_options + [
748 user_options = build_ext.user_options + [
746 (
749 (
747 'long-paths-support',
750 'long-paths-support',
748 None,
751 None,
749 'enable support for long paths on '
752 'enable support for long paths on '
750 'Windows (off by default and '
753 'Windows (off by default and '
751 'experimental)',
754 'experimental)',
752 ),
755 ),
753 ]
756 ]
754
757
755 LONG_PATHS_MANIFEST = """
758 LONG_PATHS_MANIFEST = """
756 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
759 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
757 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
760 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
758 <application>
761 <application>
759 <windowsSettings
762 <windowsSettings
760 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
763 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
761 <ws2:longPathAware>true</ws2:longPathAware>
764 <ws2:longPathAware>true</ws2:longPathAware>
762 </windowsSettings>
765 </windowsSettings>
763 </application>
766 </application>
764 </assembly>"""
767 </assembly>"""
765
768
766 def initialize_options(self):
769 def initialize_options(self):
767 build_ext.initialize_options(self)
770 build_ext.initialize_options(self)
768 self.long_paths_support = False
771 self.long_paths_support = False
769
772
770 def build_extensions(self):
773 def build_extensions(self):
771 if os.name != 'nt':
774 if os.name != 'nt':
772 return
775 return
773 if isinstance(self.compiler, HackedMingw32CCompiler):
776 if isinstance(self.compiler, HackedMingw32CCompiler):
774 self.compiler.compiler_so = self.compiler.compiler # no -mdll
777 self.compiler.compiler_so = self.compiler.compiler # no -mdll
775 self.compiler.dll_libraries = [] # no -lmsrvc90
778 self.compiler.dll_libraries = [] # no -lmsrvc90
776
779
777 pythonlib = None
780 pythonlib = None
778
781
779 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
782 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
780 self.hgtarget = os.path.join(dir, 'hg')
783 self.hgtarget = os.path.join(dir, 'hg')
781
784
782 if getattr(sys, 'dllhandle', None):
785 if getattr(sys, 'dllhandle', None):
783 # Different Python installs can have different Python library
786 # Different Python installs can have different Python library
784 # names. e.g. the official CPython distribution uses pythonXY.dll
787 # names. e.g. the official CPython distribution uses pythonXY.dll
785 # and MinGW uses libpythonX.Y.dll.
788 # and MinGW uses libpythonX.Y.dll.
786 _kernel32 = ctypes.windll.kernel32
789 _kernel32 = ctypes.windll.kernel32
787 _kernel32.GetModuleFileNameA.argtypes = [
790 _kernel32.GetModuleFileNameA.argtypes = [
788 ctypes.c_void_p,
791 ctypes.c_void_p,
789 ctypes.c_void_p,
792 ctypes.c_void_p,
790 ctypes.c_ulong,
793 ctypes.c_ulong,
791 ]
794 ]
792 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
795 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
793 size = 1000
796 size = 1000
794 buf = ctypes.create_string_buffer(size + 1)
797 buf = ctypes.create_string_buffer(size + 1)
795 filelen = _kernel32.GetModuleFileNameA(
798 filelen = _kernel32.GetModuleFileNameA(
796 sys.dllhandle, ctypes.byref(buf), size
799 sys.dllhandle, ctypes.byref(buf), size
797 )
800 )
798
801
799 if filelen > 0 and filelen != size:
802 if filelen > 0 and filelen != size:
800 dllbasename = os.path.basename(buf.value)
803 dllbasename = os.path.basename(buf.value)
801 if not dllbasename.lower().endswith(b'.dll'):
804 if not dllbasename.lower().endswith(b'.dll'):
802 raise SystemExit(
805 raise SystemExit(
803 'Python DLL does not end with .dll: %s' % dllbasename
806 'Python DLL does not end with .dll: %s' % dllbasename
804 )
807 )
805 pythonlib = dllbasename[:-4]
808 pythonlib = dllbasename[:-4]
806
809
807 # Copy the pythonXY.dll next to the binary so that it runs
810 # Copy the pythonXY.dll next to the binary so that it runs
808 # without tampering with PATH.
811 # without tampering with PATH.
809 fsdecode = lambda x: x
812 fsdecode = lambda x: x
810 if sys.version_info[0] >= 3:
813 if sys.version_info[0] >= 3:
811 fsdecode = os.fsdecode
814 fsdecode = os.fsdecode
812 dest = os.path.join(
815 dest = os.path.join(
813 os.path.dirname(self.hgtarget),
816 os.path.dirname(self.hgtarget),
814 fsdecode(dllbasename),
817 fsdecode(dllbasename),
815 )
818 )
816
819
817 if not os.path.exists(dest):
820 if not os.path.exists(dest):
818 shutil.copy(buf.value, dest)
821 shutil.copy(buf.value, dest)
819
822
820 # Also overwrite python3.dll so that hgext.git is usable.
823 # Also overwrite python3.dll so that hgext.git is usable.
821 # TODO: also handle the MSYS flavor
824 # TODO: also handle the MSYS flavor
822 if sys.version_info[0] >= 3:
825 if sys.version_info[0] >= 3:
823 python_x = os.path.join(
826 python_x = os.path.join(
824 os.path.dirname(fsdecode(buf.value)),
827 os.path.dirname(fsdecode(buf.value)),
825 "python3.dll",
828 "python3.dll",
826 )
829 )
827
830
828 if os.path.exists(python_x):
831 if os.path.exists(python_x):
829 dest = os.path.join(
832 dest = os.path.join(
830 os.path.dirname(self.hgtarget),
833 os.path.dirname(self.hgtarget),
831 os.path.basename(python_x),
834 os.path.basename(python_x),
832 )
835 )
833
836
834 shutil.copy(python_x, dest)
837 shutil.copy(python_x, dest)
835
838
836 if not pythonlib:
839 if not pythonlib:
837 log.warn(
840 log.warn(
838 'could not determine Python DLL filename; assuming pythonXY'
841 'could not determine Python DLL filename; assuming pythonXY'
839 )
842 )
840
843
841 hv = sys.hexversion
844 hv = sys.hexversion
842 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
845 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
843
846
844 log.info('using %s as Python library name' % pythonlib)
847 log.info('using %s as Python library name' % pythonlib)
845 with open('mercurial/hgpythonlib.h', 'wb') as f:
848 with open('mercurial/hgpythonlib.h', 'wb') as f:
846 f.write(b'/* this file is autogenerated by setup.py */\n')
849 f.write(b'/* this file is autogenerated by setup.py */\n')
847 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
850 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
848
851
849 macros = None
852 macros = None
850 if sys.version_info[0] >= 3:
853 if sys.version_info[0] >= 3:
851 macros = [('_UNICODE', None), ('UNICODE', None)]
854 macros = [('_UNICODE', None), ('UNICODE', None)]
852
855
853 objects = self.compiler.compile(
856 objects = self.compiler.compile(
854 ['mercurial/exewrapper.c'],
857 ['mercurial/exewrapper.c'],
855 output_dir=self.build_temp,
858 output_dir=self.build_temp,
856 macros=macros,
859 macros=macros,
857 )
860 )
858 self.compiler.link_executable(
861 self.compiler.link_executable(
859 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
862 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
860 )
863 )
861 if self.long_paths_support:
864 if self.long_paths_support:
862 self.addlongpathsmanifest()
865 self.addlongpathsmanifest()
863
866
864 def addlongpathsmanifest(self):
867 def addlongpathsmanifest(self):
865 r"""Add manifest pieces so that hg.exe understands long paths
868 r"""Add manifest pieces so that hg.exe understands long paths
866
869
867 This is an EXPERIMENTAL feature, use with care.
870 This is an EXPERIMENTAL feature, use with care.
868 To enable long paths support, one needs to do two things:
871 To enable long paths support, one needs to do two things:
869 - build Mercurial with --long-paths-support option
872 - build Mercurial with --long-paths-support option
870 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
873 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
871 LongPathsEnabled to have value 1.
874 LongPathsEnabled to have value 1.
872
875
873 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
876 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
874 it happens because Mercurial uses mt.exe circa 2008, which is not
877 it happens because Mercurial uses mt.exe circa 2008, which is not
875 yet aware of long paths support in the manifest (I think so at least).
878 yet aware of long paths support in the manifest (I think so at least).
876 This does not stop mt.exe from embedding/merging the XML properly.
879 This does not stop mt.exe from embedding/merging the XML properly.
877
880
878 Why resource #1 should be used for .exe manifests? I don't know and
881 Why resource #1 should be used for .exe manifests? I don't know and
879 wasn't able to find an explanation for mortals. But it seems to work.
882 wasn't able to find an explanation for mortals. But it seems to work.
880 """
883 """
881 exefname = self.compiler.executable_filename(self.hgtarget)
884 exefname = self.compiler.executable_filename(self.hgtarget)
882 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
885 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
883 os.close(fdauto)
886 os.close(fdauto)
884 with open(manfname, 'w') as f:
887 with open(manfname, 'w') as f:
885 f.write(self.LONG_PATHS_MANIFEST)
888 f.write(self.LONG_PATHS_MANIFEST)
886 log.info("long paths manifest is written to '%s'" % manfname)
889 log.info("long paths manifest is written to '%s'" % manfname)
887 inputresource = '-inputresource:%s;#1' % exefname
890 inputresource = '-inputresource:%s;#1' % exefname
888 outputresource = '-outputresource:%s;#1' % exefname
891 outputresource = '-outputresource:%s;#1' % exefname
889 log.info("running mt.exe to update hg.exe's manifest in-place")
892 log.info("running mt.exe to update hg.exe's manifest in-place")
890 # supplying both -manifest and -inputresource to mt.exe makes
893 # supplying both -manifest and -inputresource to mt.exe makes
891 # it merge the embedded and supplied manifests in the -outputresource
894 # it merge the embedded and supplied manifests in the -outputresource
892 self.spawn(
895 self.spawn(
893 [
896 [
894 'mt.exe',
897 'mt.exe',
895 '-nologo',
898 '-nologo',
896 '-manifest',
899 '-manifest',
897 manfname,
900 manfname,
898 inputresource,
901 inputresource,
899 outputresource,
902 outputresource,
900 ]
903 ]
901 )
904 )
902 log.info("done updating hg.exe's manifest")
905 log.info("done updating hg.exe's manifest")
903 os.remove(manfname)
906 os.remove(manfname)
904
907
905 @property
908 @property
906 def hgexepath(self):
909 def hgexepath(self):
907 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
910 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
908 return os.path.join(self.build_temp, dir, 'hg.exe')
911 return os.path.join(self.build_temp, dir, 'hg.exe')
909
912
910
913
911 class hgbuilddoc(Command):
914 class hgbuilddoc(Command):
912 description = 'build documentation'
915 description = 'build documentation'
913 user_options = [
916 user_options = [
914 ('man', None, 'generate man pages'),
917 ('man', None, 'generate man pages'),
915 ('html', None, 'generate html pages'),
918 ('html', None, 'generate html pages'),
916 ]
919 ]
917
920
918 def initialize_options(self):
921 def initialize_options(self):
919 self.man = None
922 self.man = None
920 self.html = None
923 self.html = None
921
924
922 def finalize_options(self):
925 def finalize_options(self):
923 # If --man or --html are set, only generate what we're told to.
926 # If --man or --html are set, only generate what we're told to.
924 # Otherwise generate everything.
927 # Otherwise generate everything.
925 have_subset = self.man is not None or self.html is not None
928 have_subset = self.man is not None or self.html is not None
926
929
927 if have_subset:
930 if have_subset:
928 self.man = True if self.man else False
931 self.man = True if self.man else False
929 self.html = True if self.html else False
932 self.html = True if self.html else False
930 else:
933 else:
931 self.man = True
934 self.man = True
932 self.html = True
935 self.html = True
933
936
934 def run(self):
937 def run(self):
935 def normalizecrlf(p):
938 def normalizecrlf(p):
936 with open(p, 'rb') as fh:
939 with open(p, 'rb') as fh:
937 orig = fh.read()
940 orig = fh.read()
938
941
939 if b'\r\n' not in orig:
942 if b'\r\n' not in orig:
940 return
943 return
941
944
942 log.info('normalizing %s to LF line endings' % p)
945 log.info('normalizing %s to LF line endings' % p)
943 with open(p, 'wb') as fh:
946 with open(p, 'wb') as fh:
944 fh.write(orig.replace(b'\r\n', b'\n'))
947 fh.write(orig.replace(b'\r\n', b'\n'))
945
948
946 def gentxt(root):
949 def gentxt(root):
947 txt = 'doc/%s.txt' % root
950 txt = 'doc/%s.txt' % root
948 log.info('generating %s' % txt)
951 log.info('generating %s' % txt)
949 res, out, err = runcmd(
952 res, out, err = runcmd(
950 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
953 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
951 )
954 )
952 if res:
955 if res:
953 raise SystemExit(
956 raise SystemExit(
954 'error running gendoc.py: %s'
957 'error running gendoc.py: %s'
955 % '\n'.join([sysstr(out), sysstr(err)])
958 % '\n'.join([sysstr(out), sysstr(err)])
956 )
959 )
957
960
958 with open(txt, 'wb') as fh:
961 with open(txt, 'wb') as fh:
959 fh.write(out)
962 fh.write(out)
960
963
961 def gengendoc(root):
964 def gengendoc(root):
962 gendoc = 'doc/%s.gendoc.txt' % root
965 gendoc = 'doc/%s.gendoc.txt' % root
963
966
964 log.info('generating %s' % gendoc)
967 log.info('generating %s' % gendoc)
965 res, out, err = runcmd(
968 res, out, err = runcmd(
966 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
969 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
967 os.environ,
970 os.environ,
968 cwd='doc',
971 cwd='doc',
969 )
972 )
970 if res:
973 if res:
971 raise SystemExit(
974 raise SystemExit(
972 'error running gendoc: %s'
975 'error running gendoc: %s'
973 % '\n'.join([sysstr(out), sysstr(err)])
976 % '\n'.join([sysstr(out), sysstr(err)])
974 )
977 )
975
978
976 with open(gendoc, 'wb') as fh:
979 with open(gendoc, 'wb') as fh:
977 fh.write(out)
980 fh.write(out)
978
981
979 def genman(root):
982 def genman(root):
980 log.info('generating doc/%s' % root)
983 log.info('generating doc/%s' % root)
981 res, out, err = runcmd(
984 res, out, err = runcmd(
982 [
985 [
983 sys.executable,
986 sys.executable,
984 'runrst',
987 'runrst',
985 'hgmanpage',
988 'hgmanpage',
986 '--halt',
989 '--halt',
987 'warning',
990 'warning',
988 '--strip-elements-with-class',
991 '--strip-elements-with-class',
989 'htmlonly',
992 'htmlonly',
990 '%s.txt' % root,
993 '%s.txt' % root,
991 root,
994 root,
992 ],
995 ],
993 os.environ,
996 os.environ,
994 cwd='doc',
997 cwd='doc',
995 )
998 )
996 if res:
999 if res:
997 raise SystemExit(
1000 raise SystemExit(
998 'error running runrst: %s'
1001 'error running runrst: %s'
999 % '\n'.join([sysstr(out), sysstr(err)])
1002 % '\n'.join([sysstr(out), sysstr(err)])
1000 )
1003 )
1001
1004
1002 normalizecrlf('doc/%s' % root)
1005 normalizecrlf('doc/%s' % root)
1003
1006
1004 def genhtml(root):
1007 def genhtml(root):
1005 log.info('generating doc/%s.html' % root)
1008 log.info('generating doc/%s.html' % root)
1006 res, out, err = runcmd(
1009 res, out, err = runcmd(
1007 [
1010 [
1008 sys.executable,
1011 sys.executable,
1009 'runrst',
1012 'runrst',
1010 'html',
1013 'html',
1011 '--halt',
1014 '--halt',
1012 'warning',
1015 'warning',
1013 '--link-stylesheet',
1016 '--link-stylesheet',
1014 '--stylesheet-path',
1017 '--stylesheet-path',
1015 'style.css',
1018 'style.css',
1016 '%s.txt' % root,
1019 '%s.txt' % root,
1017 '%s.html' % root,
1020 '%s.html' % root,
1018 ],
1021 ],
1019 os.environ,
1022 os.environ,
1020 cwd='doc',
1023 cwd='doc',
1021 )
1024 )
1022 if res:
1025 if res:
1023 raise SystemExit(
1026 raise SystemExit(
1024 'error running runrst: %s'
1027 'error running runrst: %s'
1025 % '\n'.join([sysstr(out), sysstr(err)])
1028 % '\n'.join([sysstr(out), sysstr(err)])
1026 )
1029 )
1027
1030
1028 normalizecrlf('doc/%s.html' % root)
1031 normalizecrlf('doc/%s.html' % root)
1029
1032
1030 # This logic is duplicated in doc/Makefile.
1033 # This logic is duplicated in doc/Makefile.
1031 sources = {
1034 sources = {
1032 f
1035 f
1033 for f in os.listdir('mercurial/helptext')
1036 for f in os.listdir('mercurial/helptext')
1034 if re.search(r'[0-9]\.txt$', f)
1037 if re.search(r'[0-9]\.txt$', f)
1035 }
1038 }
1036
1039
1037 # common.txt is a one-off.
1040 # common.txt is a one-off.
1038 gentxt('common')
1041 gentxt('common')
1039
1042
1040 for source in sorted(sources):
1043 for source in sorted(sources):
1041 assert source[-4:] == '.txt'
1044 assert source[-4:] == '.txt'
1042 root = source[:-4]
1045 root = source[:-4]
1043
1046
1044 gentxt(root)
1047 gentxt(root)
1045 gengendoc(root)
1048 gengendoc(root)
1046
1049
1047 if self.man:
1050 if self.man:
1048 genman(root)
1051 genman(root)
1049 if self.html:
1052 if self.html:
1050 genhtml(root)
1053 genhtml(root)
1051
1054
1052
1055
1053 class hginstall(install):
1056 class hginstall(install):
1054
1057
1055 user_options = install.user_options + [
1058 user_options = install.user_options + [
1056 (
1059 (
1057 'old-and-unmanageable',
1060 'old-and-unmanageable',
1058 None,
1061 None,
1059 'noop, present for eggless setuptools compat',
1062 'noop, present for eggless setuptools compat',
1060 ),
1063 ),
1061 (
1064 (
1062 'single-version-externally-managed',
1065 'single-version-externally-managed',
1063 None,
1066 None,
1064 'noop, present for eggless setuptools compat',
1067 'noop, present for eggless setuptools compat',
1065 ),
1068 ),
1066 ]
1069 ]
1067
1070
1068 # Also helps setuptools not be sad while we refuse to create eggs.
1071 # Also helps setuptools not be sad while we refuse to create eggs.
1069 single_version_externally_managed = True
1072 single_version_externally_managed = True
1070
1073
1071 def get_sub_commands(self):
1074 def get_sub_commands(self):
1072 # Screen out egg related commands to prevent egg generation. But allow
1075 # Screen out egg related commands to prevent egg generation. But allow
1073 # mercurial.egg-info generation, since that is part of modern
1076 # mercurial.egg-info generation, since that is part of modern
1074 # packaging.
1077 # packaging.
1075 excl = {'bdist_egg'}
1078 excl = {'bdist_egg'}
1076 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1079 return filter(lambda x: x not in excl, install.get_sub_commands(self))
1077
1080
1078
1081
1079 class hginstalllib(install_lib):
1082 class hginstalllib(install_lib):
1080 """
1083 """
1081 This is a specialization of install_lib that replaces the copy_file used
1084 This is a specialization of install_lib that replaces the copy_file used
1082 there so that it supports setting the mode of files after copying them,
1085 there so that it supports setting the mode of files after copying them,
1083 instead of just preserving the mode that the files originally had. If your
1086 instead of just preserving the mode that the files originally had. If your
1084 system has a umask of something like 027, preserving the permissions when
1087 system has a umask of something like 027, preserving the permissions when
1085 copying will lead to a broken install.
1088 copying will lead to a broken install.
1086
1089
1087 Note that just passing keep_permissions=False to copy_file would be
1090 Note that just passing keep_permissions=False to copy_file would be
1088 insufficient, as it might still be applying a umask.
1091 insufficient, as it might still be applying a umask.
1089 """
1092 """
1090
1093
1091 def run(self):
1094 def run(self):
1092 realcopyfile = file_util.copy_file
1095 realcopyfile = file_util.copy_file
1093
1096
1094 def copyfileandsetmode(*args, **kwargs):
1097 def copyfileandsetmode(*args, **kwargs):
1095 src, dst = args[0], args[1]
1098 src, dst = args[0], args[1]
1096 dst, copied = realcopyfile(*args, **kwargs)
1099 dst, copied = realcopyfile(*args, **kwargs)
1097 if copied:
1100 if copied:
1098 st = os.stat(src)
1101 st = os.stat(src)
1099 # Persist executable bit (apply it to group and other if user
1102 # Persist executable bit (apply it to group and other if user
1100 # has it)
1103 # has it)
1101 if st[stat.ST_MODE] & stat.S_IXUSR:
1104 if st[stat.ST_MODE] & stat.S_IXUSR:
1102 setmode = int('0755', 8)
1105 setmode = int('0755', 8)
1103 else:
1106 else:
1104 setmode = int('0644', 8)
1107 setmode = int('0644', 8)
1105 m = stat.S_IMODE(st[stat.ST_MODE])
1108 m = stat.S_IMODE(st[stat.ST_MODE])
1106 m = (m & ~int('0777', 8)) | setmode
1109 m = (m & ~int('0777', 8)) | setmode
1107 os.chmod(dst, m)
1110 os.chmod(dst, m)
1108
1111
1109 file_util.copy_file = copyfileandsetmode
1112 file_util.copy_file = copyfileandsetmode
1110 try:
1113 try:
1111 install_lib.run(self)
1114 install_lib.run(self)
1112 finally:
1115 finally:
1113 file_util.copy_file = realcopyfile
1116 file_util.copy_file = realcopyfile
1114
1117
1115
1118
1116 class hginstallscripts(install_scripts):
1119 class hginstallscripts(install_scripts):
1117 """
1120 """
1118 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1121 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1119 the configured directory for modules. If possible, the path is made relative
1122 the configured directory for modules. If possible, the path is made relative
1120 to the directory for scripts.
1123 to the directory for scripts.
1121 """
1124 """
1122
1125
1123 def initialize_options(self):
1126 def initialize_options(self):
1124 install_scripts.initialize_options(self)
1127 install_scripts.initialize_options(self)
1125
1128
1126 self.install_lib = None
1129 self.install_lib = None
1127
1130
1128 def finalize_options(self):
1131 def finalize_options(self):
1129 install_scripts.finalize_options(self)
1132 install_scripts.finalize_options(self)
1130 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1133 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1131
1134
1132 def run(self):
1135 def run(self):
1133 install_scripts.run(self)
1136 install_scripts.run(self)
1134
1137
1135 # It only makes sense to replace @LIBDIR@ with the install path if
1138 # It only makes sense to replace @LIBDIR@ with the install path if
1136 # the install path is known. For wheels, the logic below calculates
1139 # the install path is known. For wheels, the logic below calculates
1137 # the libdir to be "../..". This is because the internal layout of a
1140 # the libdir to be "../..". This is because the internal layout of a
1138 # wheel archive looks like:
1141 # wheel archive looks like:
1139 #
1142 #
1140 # mercurial-3.6.1.data/scripts/hg
1143 # mercurial-3.6.1.data/scripts/hg
1141 # mercurial/__init__.py
1144 # mercurial/__init__.py
1142 #
1145 #
1143 # When installing wheels, the subdirectories of the "<pkg>.data"
1146 # When installing wheels, the subdirectories of the "<pkg>.data"
1144 # directory are translated to system local paths and files therein
1147 # directory are translated to system local paths and files therein
1145 # are copied in place. The mercurial/* files are installed into the
1148 # are copied in place. The mercurial/* files are installed into the
1146 # site-packages directory. However, the site-packages directory
1149 # site-packages directory. However, the site-packages directory
1147 # isn't known until wheel install time. This means we have no clue
1150 # isn't known until wheel install time. This means we have no clue
1148 # at wheel generation time what the installed site-packages directory
1151 # at wheel generation time what the installed site-packages directory
1149 # will be. And, wheels don't appear to provide the ability to register
1152 # will be. And, wheels don't appear to provide the ability to register
1150 # custom code to run during wheel installation. This all means that
1153 # custom code to run during wheel installation. This all means that
1151 # we can't reliably set the libdir in wheels: the default behavior
1154 # we can't reliably set the libdir in wheels: the default behavior
1152 # of looking in sys.path must do.
1155 # of looking in sys.path must do.
1153
1156
1154 if (
1157 if (
1155 os.path.splitdrive(self.install_dir)[0]
1158 os.path.splitdrive(self.install_dir)[0]
1156 != os.path.splitdrive(self.install_lib)[0]
1159 != os.path.splitdrive(self.install_lib)[0]
1157 ):
1160 ):
1158 # can't make relative paths from one drive to another, so use an
1161 # can't make relative paths from one drive to another, so use an
1159 # absolute path instead
1162 # absolute path instead
1160 libdir = self.install_lib
1163 libdir = self.install_lib
1161 else:
1164 else:
1162 libdir = os.path.relpath(self.install_lib, self.install_dir)
1165 libdir = os.path.relpath(self.install_lib, self.install_dir)
1163
1166
1164 for outfile in self.outfiles:
1167 for outfile in self.outfiles:
1165 with open(outfile, 'rb') as fp:
1168 with open(outfile, 'rb') as fp:
1166 data = fp.read()
1169 data = fp.read()
1167
1170
1168 # skip binary files
1171 # skip binary files
1169 if b'\0' in data:
1172 if b'\0' in data:
1170 continue
1173 continue
1171
1174
1172 # During local installs, the shebang will be rewritten to the final
1175 # During local installs, the shebang will be rewritten to the final
1173 # install path. During wheel packaging, the shebang has a special
1176 # install path. During wheel packaging, the shebang has a special
1174 # value.
1177 # value.
1175 if data.startswith(b'#!python'):
1178 if data.startswith(b'#!python'):
1176 log.info(
1179 log.info(
1177 'not rewriting @LIBDIR@ in %s because install path '
1180 'not rewriting @LIBDIR@ in %s because install path '
1178 'not known' % outfile
1181 'not known' % outfile
1179 )
1182 )
1180 continue
1183 continue
1181
1184
1182 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
1185 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
1183 with open(outfile, 'wb') as fp:
1186 with open(outfile, 'wb') as fp:
1184 fp.write(data)
1187 fp.write(data)
1185
1188
1186
1189
1187 # virtualenv installs custom distutils/__init__.py and
1190 # virtualenv installs custom distutils/__init__.py and
1188 # distutils/distutils.cfg files which essentially proxy back to the
1191 # distutils/distutils.cfg files which essentially proxy back to the
1189 # "real" distutils in the main Python install. The presence of this
1192 # "real" distutils in the main Python install. The presence of this
1190 # directory causes py2exe to pick up the "hacked" distutils package
1193 # directory causes py2exe to pick up the "hacked" distutils package
1191 # from the virtualenv and "import distutils" will fail from the py2exe
1194 # from the virtualenv and "import distutils" will fail from the py2exe
1192 # build because the "real" distutils files can't be located.
1195 # build because the "real" distutils files can't be located.
1193 #
1196 #
1194 # We work around this by monkeypatching the py2exe code finding Python
1197 # We work around this by monkeypatching the py2exe code finding Python
1195 # modules to replace the found virtualenv distutils modules with the
1198 # modules to replace the found virtualenv distutils modules with the
1196 # original versions via filesystem scanning. This is a bit hacky. But
1199 # original versions via filesystem scanning. This is a bit hacky. But
1197 # it allows us to use virtualenvs for py2exe packaging, which is more
1200 # it allows us to use virtualenvs for py2exe packaging, which is more
1198 # deterministic and reproducible.
1201 # deterministic and reproducible.
1199 #
1202 #
1200 # It's worth noting that the common StackOverflow suggestions for this
1203 # It's worth noting that the common StackOverflow suggestions for this
1201 # problem involve copying the original distutils files into the
1204 # problem involve copying the original distutils files into the
1202 # virtualenv or into the staging directory after setup() is invoked.
1205 # virtualenv or into the staging directory after setup() is invoked.
1203 # The former is very brittle and can easily break setup(). Our hacking
1206 # The former is very brittle and can easily break setup(). Our hacking
1204 # of the found modules routine has a similar result as copying the files
1207 # of the found modules routine has a similar result as copying the files
1205 # manually. But it makes fewer assumptions about how py2exe works and
1208 # manually. But it makes fewer assumptions about how py2exe works and
1206 # is less brittle.
1209 # is less brittle.
1207
1210
1208 # This only catches virtualenvs made with virtualenv (as opposed to
1211 # This only catches virtualenvs made with virtualenv (as opposed to
1209 # venv, which is likely what Python 3 uses).
1212 # venv, which is likely what Python 3 uses).
1210 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1213 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1211
1214
1212 if py2exehacked:
1215 if py2exehacked:
1213 from distutils.command.py2exe import py2exe as buildpy2exe
1216 from distutils.command.py2exe import py2exe as buildpy2exe
1214 from py2exe.mf import Module as py2exemodule
1217 from py2exe.mf import Module as py2exemodule
1215
1218
1216 class hgbuildpy2exe(buildpy2exe):
1219 class hgbuildpy2exe(buildpy2exe):
1217 def find_needed_modules(self, mf, files, modules):
1220 def find_needed_modules(self, mf, files, modules):
1218 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1221 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1219
1222
1220 # Replace virtualenv's distutils modules with the real ones.
1223 # Replace virtualenv's distutils modules with the real ones.
1221 modules = {}
1224 modules = {}
1222 for k, v in res.modules.items():
1225 for k, v in res.modules.items():
1223 if k != 'distutils' and not k.startswith('distutils.'):
1226 if k != 'distutils' and not k.startswith('distutils.'):
1224 modules[k] = v
1227 modules[k] = v
1225
1228
1226 res.modules = modules
1229 res.modules = modules
1227
1230
1228 import opcode
1231 import opcode
1229
1232
1230 distutilsreal = os.path.join(
1233 distutilsreal = os.path.join(
1231 os.path.dirname(opcode.__file__), 'distutils'
1234 os.path.dirname(opcode.__file__), 'distutils'
1232 )
1235 )
1233
1236
1234 for root, dirs, files in os.walk(distutilsreal):
1237 for root, dirs, files in os.walk(distutilsreal):
1235 for f in sorted(files):
1238 for f in sorted(files):
1236 if not f.endswith('.py'):
1239 if not f.endswith('.py'):
1237 continue
1240 continue
1238
1241
1239 full = os.path.join(root, f)
1242 full = os.path.join(root, f)
1240
1243
1241 parents = ['distutils']
1244 parents = ['distutils']
1242
1245
1243 if root != distutilsreal:
1246 if root != distutilsreal:
1244 rel = os.path.relpath(root, distutilsreal)
1247 rel = os.path.relpath(root, distutilsreal)
1245 parents.extend(p for p in rel.split(os.sep))
1248 parents.extend(p for p in rel.split(os.sep))
1246
1249
1247 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1250 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1248
1251
1249 if modname.startswith('distutils.tests.'):
1252 if modname.startswith('distutils.tests.'):
1250 continue
1253 continue
1251
1254
1252 if modname.endswith('.__init__'):
1255 if modname.endswith('.__init__'):
1253 modname = modname[: -len('.__init__')]
1256 modname = modname[: -len('.__init__')]
1254 path = os.path.dirname(full)
1257 path = os.path.dirname(full)
1255 else:
1258 else:
1256 path = None
1259 path = None
1257
1260
1258 res.modules[modname] = py2exemodule(
1261 res.modules[modname] = py2exemodule(
1259 modname, full, path=path
1262 modname, full, path=path
1260 )
1263 )
1261
1264
1262 if 'distutils' not in res.modules:
1265 if 'distutils' not in res.modules:
1263 raise SystemExit('could not find distutils modules')
1266 raise SystemExit('could not find distutils modules')
1264
1267
1265 return res
1268 return res
1266
1269
1267
1270
1268 cmdclass = {
1271 cmdclass = {
1269 'build': hgbuild,
1272 'build': hgbuild,
1270 'build_doc': hgbuilddoc,
1273 'build_doc': hgbuilddoc,
1271 'build_mo': hgbuildmo,
1274 'build_mo': hgbuildmo,
1272 'build_ext': hgbuildext,
1275 'build_ext': hgbuildext,
1273 'build_py': hgbuildpy,
1276 'build_py': hgbuildpy,
1274 'build_scripts': hgbuildscripts,
1277 'build_scripts': hgbuildscripts,
1275 'build_hgextindex': buildhgextindex,
1278 'build_hgextindex': buildhgextindex,
1276 'install': hginstall,
1279 'install': hginstall,
1277 'install_lib': hginstalllib,
1280 'install_lib': hginstalllib,
1278 'install_scripts': hginstallscripts,
1281 'install_scripts': hginstallscripts,
1279 'build_hgexe': buildhgexe,
1282 'build_hgexe': buildhgexe,
1280 }
1283 }
1281
1284
1282 if py2exehacked:
1285 if py2exehacked:
1283 cmdclass['py2exe'] = hgbuildpy2exe
1286 cmdclass['py2exe'] = hgbuildpy2exe
1284
1287
1285 packages = [
1288 packages = [
1286 'mercurial',
1289 'mercurial',
1287 'mercurial.cext',
1290 'mercurial.cext',
1288 'mercurial.cffi',
1291 'mercurial.cffi',
1289 'mercurial.defaultrc',
1292 'mercurial.defaultrc',
1290 'mercurial.helptext',
1293 'mercurial.helptext',
1291 'mercurial.helptext.internals',
1294 'mercurial.helptext.internals',
1292 'mercurial.hgweb',
1295 'mercurial.hgweb',
1293 'mercurial.interfaces',
1296 'mercurial.interfaces',
1294 'mercurial.pure',
1297 'mercurial.pure',
1295 'mercurial.templates',
1298 'mercurial.templates',
1296 'mercurial.thirdparty',
1299 'mercurial.thirdparty',
1297 'mercurial.thirdparty.attr',
1300 'mercurial.thirdparty.attr',
1298 'mercurial.thirdparty.zope',
1301 'mercurial.thirdparty.zope',
1299 'mercurial.thirdparty.zope.interface',
1302 'mercurial.thirdparty.zope.interface',
1300 'mercurial.upgrade_utils',
1303 'mercurial.upgrade_utils',
1301 'mercurial.utils',
1304 'mercurial.utils',
1302 'mercurial.revlogutils',
1305 'mercurial.revlogutils',
1303 'mercurial.testing',
1306 'mercurial.testing',
1304 'hgext',
1307 'hgext',
1305 'hgext.convert',
1308 'hgext.convert',
1306 'hgext.fsmonitor',
1309 'hgext.fsmonitor',
1307 'hgext.fastannotate',
1310 'hgext.fastannotate',
1308 'hgext.fsmonitor.pywatchman',
1311 'hgext.fsmonitor.pywatchman',
1309 'hgext.git',
1312 'hgext.git',
1310 'hgext.highlight',
1313 'hgext.highlight',
1311 'hgext.hooklib',
1314 'hgext.hooklib',
1312 'hgext.infinitepush',
1315 'hgext.infinitepush',
1313 'hgext.largefiles',
1316 'hgext.largefiles',
1314 'hgext.lfs',
1317 'hgext.lfs',
1315 'hgext.narrow',
1318 'hgext.narrow',
1316 'hgext.remotefilelog',
1319 'hgext.remotefilelog',
1317 'hgext.zeroconf',
1320 'hgext.zeroconf',
1318 'hgext3rd',
1321 'hgext3rd',
1319 'hgdemandimport',
1322 'hgdemandimport',
1320 ]
1323 ]
1321
1324
1322 # The pygit2 dependency dropped py2 support with the 1.0 release in Dec 2019.
1325 # The pygit2 dependency dropped py2 support with the 1.0 release in Dec 2019.
1323 # Prior releases do not build at all on Windows, because Visual Studio 2008
1326 # Prior releases do not build at all on Windows, because Visual Studio 2008
1324 # doesn't understand C 11. Older Linux releases are buggy.
1327 # doesn't understand C 11. Older Linux releases are buggy.
1325 if sys.version_info[0] == 2:
1328 if sys.version_info[0] == 2:
1326 packages.remove('hgext.git')
1329 packages.remove('hgext.git')
1327
1330
1328
1331
1329 for name in os.listdir(os.path.join('mercurial', 'templates')):
1332 for name in os.listdir(os.path.join('mercurial', 'templates')):
1330 if name != '__pycache__' and os.path.isdir(
1333 if name != '__pycache__' and os.path.isdir(
1331 os.path.join('mercurial', 'templates', name)
1334 os.path.join('mercurial', 'templates', name)
1332 ):
1335 ):
1333 packages.append('mercurial.templates.%s' % name)
1336 packages.append('mercurial.templates.%s' % name)
1334
1337
1335 if sys.version_info[0] == 2:
1338 if sys.version_info[0] == 2:
1336 packages.extend(
1339 packages.extend(
1337 [
1340 [
1338 'mercurial.thirdparty.concurrent',
1341 'mercurial.thirdparty.concurrent',
1339 'mercurial.thirdparty.concurrent.futures',
1342 'mercurial.thirdparty.concurrent.futures',
1340 ]
1343 ]
1341 )
1344 )
1342
1345
1343 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1346 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1344 # py2exe can't cope with namespace packages very well, so we have to
1347 # py2exe can't cope with namespace packages very well, so we have to
1345 # install any hgext3rd.* extensions that we want in the final py2exe
1348 # install any hgext3rd.* extensions that we want in the final py2exe
1346 # image here. This is gross, but you gotta do what you gotta do.
1349 # image here. This is gross, but you gotta do what you gotta do.
1347 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1350 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1348
1351
1349 common_depends = [
1352 common_depends = [
1350 'mercurial/bitmanipulation.h',
1353 'mercurial/bitmanipulation.h',
1351 'mercurial/compat.h',
1354 'mercurial/compat.h',
1352 'mercurial/cext/util.h',
1355 'mercurial/cext/util.h',
1353 ]
1356 ]
1354 common_include_dirs = ['mercurial']
1357 common_include_dirs = ['mercurial']
1355
1358
1356 common_cflags = []
1359 common_cflags = []
1357
1360
1358 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1361 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1359 # makes declarations not at the top of a scope in the headers.
1362 # makes declarations not at the top of a scope in the headers.
1360 if os.name != 'nt' and sys.version_info[1] < 9:
1363 if os.name != 'nt' and sys.version_info[1] < 9:
1361 common_cflags = ['-Werror=declaration-after-statement']
1364 common_cflags = ['-Werror=declaration-after-statement']
1362
1365
1363 osutil_cflags = []
1366 osutil_cflags = []
1364 osutil_ldflags = []
1367 osutil_ldflags = []
1365
1368
1366 # platform specific macros
1369 # platform specific macros
1367 for plat, func in [('bsd', 'setproctitle')]:
1370 for plat, func in [('bsd', 'setproctitle')]:
1368 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1371 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1369 osutil_cflags.append('-DHAVE_%s' % func.upper())
1372 osutil_cflags.append('-DHAVE_%s' % func.upper())
1370
1373
1371 for plat, macro, code in [
1374 for plat, macro, code in [
1372 (
1375 (
1373 'bsd|darwin',
1376 'bsd|darwin',
1374 'BSD_STATFS',
1377 'BSD_STATFS',
1375 '''
1378 '''
1376 #include <sys/param.h>
1379 #include <sys/param.h>
1377 #include <sys/mount.h>
1380 #include <sys/mount.h>
1378 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1381 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1379 ''',
1382 ''',
1380 ),
1383 ),
1381 (
1384 (
1382 'linux',
1385 'linux',
1383 'LINUX_STATFS',
1386 'LINUX_STATFS',
1384 '''
1387 '''
1385 #include <linux/magic.h>
1388 #include <linux/magic.h>
1386 #include <sys/vfs.h>
1389 #include <sys/vfs.h>
1387 int main() { struct statfs s; return sizeof(s.f_type); }
1390 int main() { struct statfs s; return sizeof(s.f_type); }
1388 ''',
1391 ''',
1389 ),
1392 ),
1390 ]:
1393 ]:
1391 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1394 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1392 osutil_cflags.append('-DHAVE_%s' % macro)
1395 osutil_cflags.append('-DHAVE_%s' % macro)
1393
1396
1394 if sys.platform == 'darwin':
1397 if sys.platform == 'darwin':
1395 osutil_ldflags += ['-framework', 'ApplicationServices']
1398 osutil_ldflags += ['-framework', 'ApplicationServices']
1396
1399
1397 if sys.platform == 'sunos5':
1400 if sys.platform == 'sunos5':
1398 osutil_ldflags += ['-lsocket']
1401 osutil_ldflags += ['-lsocket']
1399
1402
1400 xdiff_srcs = [
1403 xdiff_srcs = [
1401 'mercurial/thirdparty/xdiff/xdiffi.c',
1404 'mercurial/thirdparty/xdiff/xdiffi.c',
1402 'mercurial/thirdparty/xdiff/xprepare.c',
1405 'mercurial/thirdparty/xdiff/xprepare.c',
1403 'mercurial/thirdparty/xdiff/xutils.c',
1406 'mercurial/thirdparty/xdiff/xutils.c',
1404 ]
1407 ]
1405
1408
1406 xdiff_headers = [
1409 xdiff_headers = [
1407 'mercurial/thirdparty/xdiff/xdiff.h',
1410 'mercurial/thirdparty/xdiff/xdiff.h',
1408 'mercurial/thirdparty/xdiff/xdiffi.h',
1411 'mercurial/thirdparty/xdiff/xdiffi.h',
1409 'mercurial/thirdparty/xdiff/xinclude.h',
1412 'mercurial/thirdparty/xdiff/xinclude.h',
1410 'mercurial/thirdparty/xdiff/xmacros.h',
1413 'mercurial/thirdparty/xdiff/xmacros.h',
1411 'mercurial/thirdparty/xdiff/xprepare.h',
1414 'mercurial/thirdparty/xdiff/xprepare.h',
1412 'mercurial/thirdparty/xdiff/xtypes.h',
1415 'mercurial/thirdparty/xdiff/xtypes.h',
1413 'mercurial/thirdparty/xdiff/xutils.h',
1416 'mercurial/thirdparty/xdiff/xutils.h',
1414 ]
1417 ]
1415
1418
1416
1419
1417 class RustCompilationError(CCompilerError):
1420 class RustCompilationError(CCompilerError):
1418 """Exception class for Rust compilation errors."""
1421 """Exception class for Rust compilation errors."""
1419
1422
1420
1423
1421 class RustExtension(Extension):
1424 class RustExtension(Extension):
1422 """Base classes for concrete Rust Extension classes."""
1425 """Base classes for concrete Rust Extension classes."""
1423
1426
1424 rusttargetdir = os.path.join('rust', 'target', 'release')
1427 rusttargetdir = os.path.join('rust', 'target', 'release')
1425
1428
1426 def __init__(
1429 def __init__(
1427 self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
1430 self, mpath, sources, rustlibname, subcrate, py3_features=None, **kw
1428 ):
1431 ):
1429 Extension.__init__(self, mpath, sources, **kw)
1432 Extension.__init__(self, mpath, sources, **kw)
1430 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1433 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1431 self.py3_features = py3_features
1434 self.py3_features = py3_features
1432
1435
1433 # adding Rust source and control files to depends so that the extension
1436 # adding Rust source and control files to depends so that the extension
1434 # gets rebuilt if they've changed
1437 # gets rebuilt if they've changed
1435 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1438 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1436 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1439 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1437 if os.path.exists(cargo_lock):
1440 if os.path.exists(cargo_lock):
1438 self.depends.append(cargo_lock)
1441 self.depends.append(cargo_lock)
1439 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1442 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1440 self.depends.extend(
1443 self.depends.extend(
1441 os.path.join(dirpath, fname)
1444 os.path.join(dirpath, fname)
1442 for fname in fnames
1445 for fname in fnames
1443 if os.path.splitext(fname)[1] == '.rs'
1446 if os.path.splitext(fname)[1] == '.rs'
1444 )
1447 )
1445
1448
1446 @staticmethod
1449 @staticmethod
1447 def rustdylibsuffix():
1450 def rustdylibsuffix():
1448 """Return the suffix for shared libraries produced by rustc.
1451 """Return the suffix for shared libraries produced by rustc.
1449
1452
1450 See also: https://doc.rust-lang.org/reference/linkage.html
1453 See also: https://doc.rust-lang.org/reference/linkage.html
1451 """
1454 """
1452 if sys.platform == 'darwin':
1455 if sys.platform == 'darwin':
1453 return '.dylib'
1456 return '.dylib'
1454 elif os.name == 'nt':
1457 elif os.name == 'nt':
1455 return '.dll'
1458 return '.dll'
1456 else:
1459 else:
1457 return '.so'
1460 return '.so'
1458
1461
1459 def rustbuild(self):
1462 def rustbuild(self):
1460 env = os.environ.copy()
1463 env = os.environ.copy()
1461 if 'HGTEST_RESTOREENV' in env:
1464 if 'HGTEST_RESTOREENV' in env:
1462 # Mercurial tests change HOME to a temporary directory,
1465 # Mercurial tests change HOME to a temporary directory,
1463 # but, if installed with rustup, the Rust toolchain needs
1466 # but, if installed with rustup, the Rust toolchain needs
1464 # HOME to be correct (otherwise the 'no default toolchain'
1467 # HOME to be correct (otherwise the 'no default toolchain'
1465 # error message is issued and the build fails).
1468 # error message is issued and the build fails).
1466 # This happens currently with test-hghave.t, which does
1469 # This happens currently with test-hghave.t, which does
1467 # invoke this build.
1470 # invoke this build.
1468
1471
1469 # Unix only fix (os.path.expanduser not really reliable if
1472 # Unix only fix (os.path.expanduser not really reliable if
1470 # HOME is shadowed like this)
1473 # HOME is shadowed like this)
1471 import pwd
1474 import pwd
1472
1475
1473 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1476 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1474
1477
1475 cargocmd = ['cargo', 'rustc', '--release']
1478 cargocmd = ['cargo', 'rustc', '--release']
1476
1479
1477 feature_flags = []
1480 feature_flags = []
1478
1481
1479 if sys.version_info[0] == 3 and self.py3_features is not None:
1482 if sys.version_info[0] == 3 and self.py3_features is not None:
1480 feature_flags.append(self.py3_features)
1483 feature_flags.append(self.py3_features)
1481 cargocmd.append('--no-default-features')
1484 cargocmd.append('--no-default-features')
1482
1485
1483 rust_features = env.get("HG_RUST_FEATURES")
1486 rust_features = env.get("HG_RUST_FEATURES")
1484 if rust_features:
1487 if rust_features:
1485 feature_flags.append(rust_features)
1488 feature_flags.append(rust_features)
1486
1489
1487 cargocmd.extend(('--features', " ".join(feature_flags)))
1490 cargocmd.extend(('--features', " ".join(feature_flags)))
1488
1491
1489 cargocmd.append('--')
1492 cargocmd.append('--')
1490 if sys.platform == 'darwin':
1493 if sys.platform == 'darwin':
1491 cargocmd.extend(
1494 cargocmd.extend(
1492 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1495 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1493 )
1496 )
1494 try:
1497 try:
1495 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1498 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1496 except OSError as exc:
1499 except OSError as exc:
1497 if exc.errno == errno.ENOENT:
1500 if exc.errno == errno.ENOENT:
1498 raise RustCompilationError("Cargo not found")
1501 raise RustCompilationError("Cargo not found")
1499 elif exc.errno == errno.EACCES:
1502 elif exc.errno == errno.EACCES:
1500 raise RustCompilationError(
1503 raise RustCompilationError(
1501 "Cargo found, but permisssion to execute it is denied"
1504 "Cargo found, but permisssion to execute it is denied"
1502 )
1505 )
1503 else:
1506 else:
1504 raise
1507 raise
1505 except subprocess.CalledProcessError:
1508 except subprocess.CalledProcessError:
1506 raise RustCompilationError(
1509 raise RustCompilationError(
1507 "Cargo failed. Working directory: %r, "
1510 "Cargo failed. Working directory: %r, "
1508 "command: %r, environment: %r"
1511 "command: %r, environment: %r"
1509 % (self.rustsrcdir, cargocmd, env)
1512 % (self.rustsrcdir, cargocmd, env)
1510 )
1513 )
1511
1514
1512
1515
1513 class RustStandaloneExtension(RustExtension):
1516 class RustStandaloneExtension(RustExtension):
1514 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1517 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1515 RustExtension.__init__(
1518 RustExtension.__init__(
1516 self, pydottedname, [], dylibname, rustcrate, **kw
1519 self, pydottedname, [], dylibname, rustcrate, **kw
1517 )
1520 )
1518 self.dylibname = dylibname
1521 self.dylibname = dylibname
1519
1522
1520 def build(self, target_dir):
1523 def build(self, target_dir):
1521 self.rustbuild()
1524 self.rustbuild()
1522 target = [target_dir]
1525 target = [target_dir]
1523 target.extend(self.name.split('.'))
1526 target.extend(self.name.split('.'))
1524 target[-1] += DYLIB_SUFFIX
1527 target[-1] += DYLIB_SUFFIX
1525 shutil.copy2(
1528 shutil.copy2(
1526 os.path.join(
1529 os.path.join(
1527 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1530 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1528 ),
1531 ),
1529 os.path.join(*target),
1532 os.path.join(*target),
1530 )
1533 )
1531
1534
1532
1535
1533 extmodules = [
1536 extmodules = [
1534 Extension(
1537 Extension(
1535 'mercurial.cext.base85',
1538 'mercurial.cext.base85',
1536 ['mercurial/cext/base85.c'],
1539 ['mercurial/cext/base85.c'],
1537 include_dirs=common_include_dirs,
1540 include_dirs=common_include_dirs,
1538 extra_compile_args=common_cflags,
1541 extra_compile_args=common_cflags,
1539 depends=common_depends,
1542 depends=common_depends,
1540 ),
1543 ),
1541 Extension(
1544 Extension(
1542 'mercurial.cext.bdiff',
1545 'mercurial.cext.bdiff',
1543 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1546 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1544 include_dirs=common_include_dirs,
1547 include_dirs=common_include_dirs,
1545 extra_compile_args=common_cflags,
1548 extra_compile_args=common_cflags,
1546 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1549 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1547 ),
1550 ),
1548 Extension(
1551 Extension(
1549 'mercurial.cext.mpatch',
1552 'mercurial.cext.mpatch',
1550 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1553 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1551 include_dirs=common_include_dirs,
1554 include_dirs=common_include_dirs,
1552 extra_compile_args=common_cflags,
1555 extra_compile_args=common_cflags,
1553 depends=common_depends,
1556 depends=common_depends,
1554 ),
1557 ),
1555 Extension(
1558 Extension(
1556 'mercurial.cext.parsers',
1559 'mercurial.cext.parsers',
1557 [
1560 [
1558 'mercurial/cext/charencode.c',
1561 'mercurial/cext/charencode.c',
1559 'mercurial/cext/dirs.c',
1562 'mercurial/cext/dirs.c',
1560 'mercurial/cext/manifest.c',
1563 'mercurial/cext/manifest.c',
1561 'mercurial/cext/parsers.c',
1564 'mercurial/cext/parsers.c',
1562 'mercurial/cext/pathencode.c',
1565 'mercurial/cext/pathencode.c',
1563 'mercurial/cext/revlog.c',
1566 'mercurial/cext/revlog.c',
1564 ],
1567 ],
1565 include_dirs=common_include_dirs,
1568 include_dirs=common_include_dirs,
1566 extra_compile_args=common_cflags,
1569 extra_compile_args=common_cflags,
1567 depends=common_depends
1570 depends=common_depends
1568 + [
1571 + [
1569 'mercurial/cext/charencode.h',
1572 'mercurial/cext/charencode.h',
1570 'mercurial/cext/revlog.h',
1573 'mercurial/cext/revlog.h',
1571 ],
1574 ],
1572 ),
1575 ),
1573 Extension(
1576 Extension(
1574 'mercurial.cext.osutil',
1577 'mercurial.cext.osutil',
1575 ['mercurial/cext/osutil.c'],
1578 ['mercurial/cext/osutil.c'],
1576 include_dirs=common_include_dirs,
1579 include_dirs=common_include_dirs,
1577 extra_compile_args=common_cflags + osutil_cflags,
1580 extra_compile_args=common_cflags + osutil_cflags,
1578 extra_link_args=osutil_ldflags,
1581 extra_link_args=osutil_ldflags,
1579 depends=common_depends,
1582 depends=common_depends,
1580 ),
1583 ),
1581 Extension(
1584 Extension(
1582 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1585 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1583 [
1586 [
1584 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1587 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1585 ],
1588 ],
1586 extra_compile_args=common_cflags,
1589 extra_compile_args=common_cflags,
1587 ),
1590 ),
1588 Extension(
1591 Extension(
1589 'mercurial.thirdparty.sha1dc',
1592 'mercurial.thirdparty.sha1dc',
1590 [
1593 [
1591 'mercurial/thirdparty/sha1dc/cext.c',
1594 'mercurial/thirdparty/sha1dc/cext.c',
1592 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1595 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1593 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1596 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1594 ],
1597 ],
1595 extra_compile_args=common_cflags,
1598 extra_compile_args=common_cflags,
1596 ),
1599 ),
1597 Extension(
1600 Extension(
1598 'hgext.fsmonitor.pywatchman.bser',
1601 'hgext.fsmonitor.pywatchman.bser',
1599 ['hgext/fsmonitor/pywatchman/bser.c'],
1602 ['hgext/fsmonitor/pywatchman/bser.c'],
1600 extra_compile_args=common_cflags,
1603 extra_compile_args=common_cflags,
1601 ),
1604 ),
1602 RustStandaloneExtension(
1605 RustStandaloneExtension(
1603 'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
1606 'mercurial.rustext', 'hg-cpython', 'librusthg', py3_features='python3'
1604 ),
1607 ),
1605 ]
1608 ]
1606
1609
1607
1610
1608 sys.path.insert(0, 'contrib/python-zstandard')
1611 sys.path.insert(0, 'contrib/python-zstandard')
1609 import setup_zstd
1612 import setup_zstd
1610
1613
1611 zstd = setup_zstd.get_c_extension(
1614 zstd = setup_zstd.get_c_extension(
1612 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1615 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1613 )
1616 )
1614 zstd.extra_compile_args += common_cflags
1617 zstd.extra_compile_args += common_cflags
1615 extmodules.append(zstd)
1618 extmodules.append(zstd)
1616
1619
1617 try:
1620 try:
1618 from distutils import cygwinccompiler
1621 from distutils import cygwinccompiler
1619
1622
1620 # the -mno-cygwin option has been deprecated for years
1623 # the -mno-cygwin option has been deprecated for years
1621 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1624 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1622
1625
1623 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1626 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1624 def __init__(self, *args, **kwargs):
1627 def __init__(self, *args, **kwargs):
1625 mingw32compilerclass.__init__(self, *args, **kwargs)
1628 mingw32compilerclass.__init__(self, *args, **kwargs)
1626 for i in 'compiler compiler_so linker_exe linker_so'.split():
1629 for i in 'compiler compiler_so linker_exe linker_so'.split():
1627 try:
1630 try:
1628 getattr(self, i).remove('-mno-cygwin')
1631 getattr(self, i).remove('-mno-cygwin')
1629 except ValueError:
1632 except ValueError:
1630 pass
1633 pass
1631
1634
1632 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1635 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1633 except ImportError:
1636 except ImportError:
1634 # the cygwinccompiler package is not available on some Python
1637 # the cygwinccompiler package is not available on some Python
1635 # distributions like the ones from the optware project for Synology
1638 # distributions like the ones from the optware project for Synology
1636 # DiskStation boxes
1639 # DiskStation boxes
1637 class HackedMingw32CCompiler(object):
1640 class HackedMingw32CCompiler(object):
1638 pass
1641 pass
1639
1642
1640
1643
1641 if os.name == 'nt':
1644 if os.name == 'nt':
1642 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1645 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1643 # extra_link_args to distutils.extensions.Extension() doesn't have any
1646 # extra_link_args to distutils.extensions.Extension() doesn't have any
1644 # effect.
1647 # effect.
1645 from distutils import msvccompiler
1648 from distutils import msvccompiler
1646
1649
1647 msvccompilerclass = msvccompiler.MSVCCompiler
1650 msvccompilerclass = msvccompiler.MSVCCompiler
1648
1651
1649 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1652 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1650 def initialize(self):
1653 def initialize(self):
1651 msvccompilerclass.initialize(self)
1654 msvccompilerclass.initialize(self)
1652 # "warning LNK4197: export 'func' specified multiple times"
1655 # "warning LNK4197: export 'func' specified multiple times"
1653 self.ldflags_shared.append('/ignore:4197')
1656 self.ldflags_shared.append('/ignore:4197')
1654 self.ldflags_shared_debug.append('/ignore:4197')
1657 self.ldflags_shared_debug.append('/ignore:4197')
1655
1658
1656 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1659 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1657
1660
1658 packagedata = {
1661 packagedata = {
1659 'mercurial': [
1662 'mercurial': [
1660 'locale/*/LC_MESSAGES/hg.mo',
1663 'locale/*/LC_MESSAGES/hg.mo',
1661 'dummycert.pem',
1664 'dummycert.pem',
1662 ],
1665 ],
1663 'mercurial.defaultrc': [
1666 'mercurial.defaultrc': [
1664 '*.rc',
1667 '*.rc',
1665 ],
1668 ],
1666 'mercurial.helptext': [
1669 'mercurial.helptext': [
1667 '*.txt',
1670 '*.txt',
1668 ],
1671 ],
1669 'mercurial.helptext.internals': [
1672 'mercurial.helptext.internals': [
1670 '*.txt',
1673 '*.txt',
1671 ],
1674 ],
1672 }
1675 }
1673
1676
1674
1677
1675 def ordinarypath(p):
1678 def ordinarypath(p):
1676 return p and p[0] != '.' and p[-1] != '~'
1679 return p and p[0] != '.' and p[-1] != '~'
1677
1680
1678
1681
1679 for root in ('templates',):
1682 for root in ('templates',):
1680 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1683 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1681 packagename = curdir.replace(os.sep, '.')
1684 packagename = curdir.replace(os.sep, '.')
1682 packagedata[packagename] = list(filter(ordinarypath, files))
1685 packagedata[packagename] = list(filter(ordinarypath, files))
1683
1686
1684 datafiles = []
1687 datafiles = []
1685
1688
1686 # distutils expects version to be str/unicode. Converting it to
1689 # distutils expects version to be str/unicode. Converting it to
1687 # unicode on Python 2 still works because it won't contain any
1690 # unicode on Python 2 still works because it won't contain any
1688 # non-ascii bytes and will be implicitly converted back to bytes
1691 # non-ascii bytes and will be implicitly converted back to bytes
1689 # when operated on.
1692 # when operated on.
1690 assert isinstance(version, str)
1693 assert isinstance(version, str)
1691 setupversion = version
1694 setupversion = version
1692
1695
1693 extra = {}
1696 extra = {}
1694
1697
1695 py2exepackages = [
1698 py2exepackages = [
1696 'hgdemandimport',
1699 'hgdemandimport',
1697 'hgext3rd',
1700 'hgext3rd',
1698 'hgext',
1701 'hgext',
1699 'email',
1702 'email',
1700 # implicitly imported per module policy
1703 # implicitly imported per module policy
1701 # (cffi wouldn't be used as a frozen exe)
1704 # (cffi wouldn't be used as a frozen exe)
1702 'mercurial.cext',
1705 'mercurial.cext',
1703 #'mercurial.cffi',
1706 #'mercurial.cffi',
1704 'mercurial.pure',
1707 'mercurial.pure',
1705 ]
1708 ]
1706
1709
1707 py2exe_includes = []
1710 py2exe_includes = []
1708
1711
1709 py2exeexcludes = []
1712 py2exeexcludes = []
1710 py2exedllexcludes = ['crypt32.dll']
1713 py2exedllexcludes = ['crypt32.dll']
1711
1714
1712 if issetuptools:
1715 if issetuptools:
1713 extra['python_requires'] = supportedpy
1716 extra['python_requires'] = supportedpy
1714
1717
1715 if py2exeloaded:
1718 if py2exeloaded:
1716 extra['console'] = [
1719 extra['console'] = [
1717 {
1720 {
1718 'script': 'hg',
1721 'script': 'hg',
1719 'copyright': 'Copyright (C) 2005-2021 Olivia Mackall and others',
1722 'copyright': 'Copyright (C) 2005-2021 Olivia Mackall and others',
1720 'product_version': version,
1723 'product_version': version,
1721 }
1724 }
1722 ]
1725 ]
1723 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1726 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1724 # Need to override hgbuild because it has a private copy of
1727 # Need to override hgbuild because it has a private copy of
1725 # build.sub_commands.
1728 # build.sub_commands.
1726 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1729 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1727 # put dlls in sub directory so that they won't pollute PATH
1730 # put dlls in sub directory so that they won't pollute PATH
1728 extra['zipfile'] = 'lib/library.zip'
1731 extra['zipfile'] = 'lib/library.zip'
1729
1732
1730 # We allow some configuration to be supplemented via environment
1733 # We allow some configuration to be supplemented via environment
1731 # variables. This is better than setup.cfg files because it allows
1734 # variables. This is better than setup.cfg files because it allows
1732 # supplementing configs instead of replacing them.
1735 # supplementing configs instead of replacing them.
1733 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1736 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1734 if extrapackages:
1737 if extrapackages:
1735 py2exepackages.extend(extrapackages.split(' '))
1738 py2exepackages.extend(extrapackages.split(' '))
1736
1739
1737 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
1740 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
1738 if extra_includes:
1741 if extra_includes:
1739 py2exe_includes.extend(extra_includes.split(' '))
1742 py2exe_includes.extend(extra_includes.split(' '))
1740
1743
1741 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1744 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1742 if excludes:
1745 if excludes:
1743 py2exeexcludes.extend(excludes.split(' '))
1746 py2exeexcludes.extend(excludes.split(' '))
1744
1747
1745 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1748 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1746 if dllexcludes:
1749 if dllexcludes:
1747 py2exedllexcludes.extend(dllexcludes.split(' '))
1750 py2exedllexcludes.extend(dllexcludes.split(' '))
1748
1751
1749 if os.environ.get('PYOXIDIZER'):
1752 if os.environ.get('PYOXIDIZER'):
1750 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1753 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1751
1754
1752 if os.name == 'nt':
1755 if os.name == 'nt':
1753 # Windows binary file versions for exe/dll files must have the
1756 # Windows binary file versions for exe/dll files must have the
1754 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1757 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1755 setupversion = setupversion.split(r'+', 1)[0]
1758 setupversion = setupversion.split(r'+', 1)[0]
1756
1759
1757 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1760 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1758 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1761 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1759 if version:
1762 if version:
1760 version = version[0]
1763 version = version[0]
1761 if sys.version_info[0] == 3:
1764 if sys.version_info[0] == 3:
1762 version = version.decode('utf-8')
1765 version = version.decode('utf-8')
1763 xcode4 = version.startswith('Xcode') and StrictVersion(
1766 xcode4 = version.startswith('Xcode') and StrictVersion(
1764 version.split()[1]
1767 version.split()[1]
1765 ) >= StrictVersion('4.0')
1768 ) >= StrictVersion('4.0')
1766 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1769 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1767 else:
1770 else:
1768 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1771 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1769 # installed, but instead with only command-line tools. Assume
1772 # installed, but instead with only command-line tools. Assume
1770 # that only happens on >= Lion, thus no PPC support.
1773 # that only happens on >= Lion, thus no PPC support.
1771 xcode4 = True
1774 xcode4 = True
1772 xcode51 = False
1775 xcode51 = False
1773
1776
1774 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1777 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1775 # distutils.sysconfig
1778 # distutils.sysconfig
1776 if xcode4:
1779 if xcode4:
1777 os.environ['ARCHFLAGS'] = ''
1780 os.environ['ARCHFLAGS'] = ''
1778
1781
1779 # XCode 5.1 changes clang such that it now fails to compile if the
1782 # XCode 5.1 changes clang such that it now fails to compile if the
1780 # -mno-fused-madd flag is passed, but the version of Python shipped with
1783 # -mno-fused-madd flag is passed, but the version of Python shipped with
1781 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1784 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1782 # C extension modules, and a bug has been filed upstream at
1785 # C extension modules, and a bug has been filed upstream at
1783 # http://bugs.python.org/issue21244. We also need to patch this here
1786 # http://bugs.python.org/issue21244. We also need to patch this here
1784 # so Mercurial can continue to compile in the meantime.
1787 # so Mercurial can continue to compile in the meantime.
1785 if xcode51:
1788 if xcode51:
1786 cflags = get_config_var('CFLAGS')
1789 cflags = get_config_var('CFLAGS')
1787 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1790 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1788 os.environ['CFLAGS'] = (
1791 os.environ['CFLAGS'] = (
1789 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1792 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1790 )
1793 )
1791
1794
1792 setup(
1795 setup(
1793 name='mercurial',
1796 name='mercurial',
1794 version=setupversion,
1797 version=setupversion,
1795 author='Olivia Mackall and many others',
1798 author='Olivia Mackall and many others',
1796 author_email='mercurial@mercurial-scm.org',
1799 author_email='mercurial@mercurial-scm.org',
1797 url='https://mercurial-scm.org/',
1800 url='https://mercurial-scm.org/',
1798 download_url='https://mercurial-scm.org/release/',
1801 download_url='https://mercurial-scm.org/release/',
1799 description=(
1802 description=(
1800 'Fast scalable distributed SCM (revision control, version '
1803 'Fast scalable distributed SCM (revision control, version '
1801 'control) system'
1804 'control) system'
1802 ),
1805 ),
1803 long_description=(
1806 long_description=(
1804 'Mercurial is a distributed SCM tool written in Python.'
1807 'Mercurial is a distributed SCM tool written in Python.'
1805 ' It is used by a number of large projects that require'
1808 ' It is used by a number of large projects that require'
1806 ' fast, reliable distributed revision control, such as '
1809 ' fast, reliable distributed revision control, such as '
1807 'Mozilla.'
1810 'Mozilla.'
1808 ),
1811 ),
1809 license='GNU GPLv2 or any later version',
1812 license='GNU GPLv2 or any later version',
1810 classifiers=[
1813 classifiers=[
1811 'Development Status :: 6 - Mature',
1814 'Development Status :: 6 - Mature',
1812 'Environment :: Console',
1815 'Environment :: Console',
1813 'Intended Audience :: Developers',
1816 'Intended Audience :: Developers',
1814 'Intended Audience :: System Administrators',
1817 'Intended Audience :: System Administrators',
1815 'License :: OSI Approved :: GNU General Public License (GPL)',
1818 'License :: OSI Approved :: GNU General Public License (GPL)',
1816 'Natural Language :: Danish',
1819 'Natural Language :: Danish',
1817 'Natural Language :: English',
1820 'Natural Language :: English',
1818 'Natural Language :: German',
1821 'Natural Language :: German',
1819 'Natural Language :: Italian',
1822 'Natural Language :: Italian',
1820 'Natural Language :: Japanese',
1823 'Natural Language :: Japanese',
1821 'Natural Language :: Portuguese (Brazilian)',
1824 'Natural Language :: Portuguese (Brazilian)',
1822 'Operating System :: Microsoft :: Windows',
1825 'Operating System :: Microsoft :: Windows',
1823 'Operating System :: OS Independent',
1826 'Operating System :: OS Independent',
1824 'Operating System :: POSIX',
1827 'Operating System :: POSIX',
1825 'Programming Language :: C',
1828 'Programming Language :: C',
1826 'Programming Language :: Python',
1829 'Programming Language :: Python',
1827 'Topic :: Software Development :: Version Control',
1830 'Topic :: Software Development :: Version Control',
1828 ],
1831 ],
1829 scripts=scripts,
1832 scripts=scripts,
1830 packages=packages,
1833 packages=packages,
1831 ext_modules=extmodules,
1834 ext_modules=extmodules,
1832 data_files=datafiles,
1835 data_files=datafiles,
1833 package_data=packagedata,
1836 package_data=packagedata,
1834 cmdclass=cmdclass,
1837 cmdclass=cmdclass,
1835 distclass=hgdist,
1838 distclass=hgdist,
1836 options={
1839 options={
1837 'py2exe': {
1840 'py2exe': {
1838 'bundle_files': 3,
1841 'bundle_files': 3,
1839 'dll_excludes': py2exedllexcludes,
1842 'dll_excludes': py2exedllexcludes,
1840 'includes': py2exe_includes,
1843 'includes': py2exe_includes,
1841 'excludes': py2exeexcludes,
1844 'excludes': py2exeexcludes,
1842 'packages': py2exepackages,
1845 'packages': py2exepackages,
1843 },
1846 },
1844 'bdist_mpkg': {
1847 'bdist_mpkg': {
1845 'zipdist': False,
1848 'zipdist': False,
1846 'license': 'COPYING',
1849 'license': 'COPYING',
1847 'readme': 'contrib/packaging/macosx/Readme.html',
1850 'readme': 'contrib/packaging/macosx/Readme.html',
1848 'welcome': 'contrib/packaging/macosx/Welcome.html',
1851 'welcome': 'contrib/packaging/macosx/Welcome.html',
1849 },
1852 },
1850 },
1853 },
1851 **extra
1854 **extra
1852 )
1855 )
General Comments 0
You need to be logged in to leave comments. Login now