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