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