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