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