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