##// END OF EJS Templates
setup: exclude vendored futures package on Python 3...
Yuya Nishihara -
r39649:96bffce4 default
parent child Browse files
Show More
@@ -1,1097 +1,1098
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
6
7 import os
7 import os
8
8
9 supportedpy = '~= 2.7'
9 supportedpy = '~= 2.7'
10 if os.environ.get('HGALLOWPYTHON3', ''):
10 if os.environ.get('HGALLOWPYTHON3', ''):
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 # due to a bug in % formatting in bytestrings.
13 # due to a bug in % formatting in bytestrings.
14 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
14 # We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
15 # codecs.escape_encode() where it raises SystemError on empty bytestring
15 # codecs.escape_encode() where it raises SystemError on empty bytestring
16 # bug link: https://bugs.python.org/issue25270
16 # bug link: https://bugs.python.org/issue25270
17 #
17 #
18 # TODO: when we actually work on Python 3, use this string as the
18 # TODO: when we actually work on Python 3, use this string as the
19 # actual supportedpy string.
19 # actual supportedpy string.
20 supportedpy = ','.join([
20 supportedpy = ','.join([
21 '>=2.7',
21 '>=2.7',
22 '!=3.0.*',
22 '!=3.0.*',
23 '!=3.1.*',
23 '!=3.1.*',
24 '!=3.2.*',
24 '!=3.2.*',
25 '!=3.3.*',
25 '!=3.3.*',
26 '!=3.4.*',
26 '!=3.4.*',
27 '!=3.5.0',
27 '!=3.5.0',
28 '!=3.5.1',
28 '!=3.5.1',
29 '!=3.5.2',
29 '!=3.5.2',
30 '!=3.6.0',
30 '!=3.6.0',
31 '!=3.6.1',
31 '!=3.6.1',
32 ])
32 ])
33
33
34 import sys, platform
34 import sys, platform
35 if sys.version_info[0] >= 3:
35 if sys.version_info[0] >= 3:
36 printf = eval('print')
36 printf = eval('print')
37 libdir_escape = 'unicode_escape'
37 libdir_escape = 'unicode_escape'
38 def sysstr(s):
38 def sysstr(s):
39 return s.decode('latin-1')
39 return s.decode('latin-1')
40 else:
40 else:
41 libdir_escape = 'string_escape'
41 libdir_escape = 'string_escape'
42 def printf(*args, **kwargs):
42 def printf(*args, **kwargs):
43 f = kwargs.get('file', sys.stdout)
43 f = kwargs.get('file', sys.stdout)
44 end = kwargs.get('end', '\n')
44 end = kwargs.get('end', '\n')
45 f.write(b' '.join(args) + end)
45 f.write(b' '.join(args) + end)
46 def sysstr(s):
46 def sysstr(s):
47 return s
47 return s
48
48
49 # Attempt to guide users to a modern pip - this means that 2.6 users
49 # Attempt to guide users to a modern pip - this means that 2.6 users
50 # should have a chance of getting a 4.2 release, and when we ratchet
50 # should have a chance of getting a 4.2 release, and when we ratchet
51 # the version requirement forward again hopefully everyone will get
51 # the version requirement forward again hopefully everyone will get
52 # something that works for them.
52 # something that works for them.
53 if sys.version_info < (2, 7, 0, 'final'):
53 if sys.version_info < (2, 7, 0, 'final'):
54 pip_message = ('This may be due to an out of date pip. '
54 pip_message = ('This may be due to an out of date pip. '
55 'Make sure you have pip >= 9.0.1.')
55 'Make sure you have pip >= 9.0.1.')
56 try:
56 try:
57 import pip
57 import pip
58 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
58 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
59 if pip_version < (9, 0, 1) :
59 if pip_version < (9, 0, 1) :
60 pip_message = (
60 pip_message = (
61 'Your pip version is out of date, please install '
61 'Your pip version is out of date, please install '
62 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
62 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
63 else:
63 else:
64 # pip is new enough - it must be something else
64 # pip is new enough - it must be something else
65 pip_message = ''
65 pip_message = ''
66 except Exception:
66 except Exception:
67 pass
67 pass
68 error = """
68 error = """
69 Mercurial does not support Python older than 2.7.
69 Mercurial does not support Python older than 2.7.
70 Python {py} detected.
70 Python {py} detected.
71 {pip}
71 {pip}
72 """.format(py=sys.version_info, pip=pip_message)
72 """.format(py=sys.version_info, pip=pip_message)
73 printf(error, file=sys.stderr)
73 printf(error, file=sys.stderr)
74 sys.exit(1)
74 sys.exit(1)
75
75
76 # We don't yet officially support Python 3. But we want to allow developers to
76 # We don't yet officially support Python 3. But we want to allow developers to
77 # hack on. Detect and disallow running on Python 3 by default. But provide a
77 # hack on. Detect and disallow running on Python 3 by default. But provide a
78 # backdoor to enable working on Python 3.
78 # backdoor to enable working on Python 3.
79 if sys.version_info[0] != 2:
79 if sys.version_info[0] != 2:
80 badpython = True
80 badpython = True
81
81
82 # Allow Python 3 from source checkouts.
82 # Allow Python 3 from source checkouts.
83 if os.path.isdir('.hg') or 'HGPYTHON3' in os.environ:
83 if os.path.isdir('.hg') or 'HGPYTHON3' in os.environ:
84 badpython = False
84 badpython = False
85
85
86 if badpython:
86 if badpython:
87 error = """
87 error = """
88 Mercurial only supports Python 2.7.
88 Mercurial only supports Python 2.7.
89 Python {py} detected.
89 Python {py} detected.
90 Please re-run with Python 2.7.
90 Please re-run with Python 2.7.
91 """.format(py=sys.version_info)
91 """.format(py=sys.version_info)
92
92
93 printf(error, file=sys.stderr)
93 printf(error, file=sys.stderr)
94 sys.exit(1)
94 sys.exit(1)
95
95
96 # Solaris Python packaging brain damage
96 # Solaris Python packaging brain damage
97 try:
97 try:
98 import hashlib
98 import hashlib
99 sha = hashlib.sha1()
99 sha = hashlib.sha1()
100 except ImportError:
100 except ImportError:
101 try:
101 try:
102 import sha
102 import sha
103 sha.sha # silence unused import warning
103 sha.sha # silence unused import warning
104 except ImportError:
104 except ImportError:
105 raise SystemExit(
105 raise SystemExit(
106 "Couldn't import standard hashlib (incomplete Python install).")
106 "Couldn't import standard hashlib (incomplete Python install).")
107
107
108 try:
108 try:
109 import zlib
109 import zlib
110 zlib.compressobj # silence unused import warning
110 zlib.compressobj # silence unused import warning
111 except ImportError:
111 except ImportError:
112 raise SystemExit(
112 raise SystemExit(
113 "Couldn't import standard zlib (incomplete Python install).")
113 "Couldn't import standard zlib (incomplete Python install).")
114
114
115 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
115 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
116 isironpython = False
116 isironpython = False
117 try:
117 try:
118 isironpython = (platform.python_implementation()
118 isironpython = (platform.python_implementation()
119 .lower().find("ironpython") != -1)
119 .lower().find("ironpython") != -1)
120 except AttributeError:
120 except AttributeError:
121 pass
121 pass
122
122
123 if isironpython:
123 if isironpython:
124 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
124 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
125 else:
125 else:
126 try:
126 try:
127 import bz2
127 import bz2
128 bz2.BZ2Compressor # silence unused import warning
128 bz2.BZ2Compressor # silence unused import warning
129 except ImportError:
129 except ImportError:
130 raise SystemExit(
130 raise SystemExit(
131 "Couldn't import standard bz2 (incomplete Python install).")
131 "Couldn't import standard bz2 (incomplete Python install).")
132
132
133 ispypy = "PyPy" in sys.version
133 ispypy = "PyPy" in sys.version
134
134
135 import ctypes
135 import ctypes
136 import stat, subprocess, time
136 import stat, subprocess, time
137 import re
137 import re
138 import shutil
138 import shutil
139 import tempfile
139 import tempfile
140 from distutils import log
140 from distutils import log
141 # We have issues with setuptools on some platforms and builders. Until
141 # We have issues with setuptools on some platforms and builders. Until
142 # those are resolved, setuptools is opt-in except for platforms where
142 # those are resolved, setuptools is opt-in except for platforms where
143 # we don't have issues.
143 # we don't have issues.
144 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
144 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
145 if issetuptools:
145 if issetuptools:
146 from setuptools import setup
146 from setuptools import setup
147 else:
147 else:
148 from distutils.core import setup
148 from distutils.core import setup
149 from distutils.ccompiler import new_compiler
149 from distutils.ccompiler import new_compiler
150 from distutils.core import Command, Extension
150 from distutils.core import Command, Extension
151 from distutils.dist import Distribution
151 from distutils.dist import Distribution
152 from distutils.command.build import build
152 from distutils.command.build import build
153 from distutils.command.build_ext import build_ext
153 from distutils.command.build_ext import build_ext
154 from distutils.command.build_py import build_py
154 from distutils.command.build_py import build_py
155 from distutils.command.build_scripts import build_scripts
155 from distutils.command.build_scripts import build_scripts
156 from distutils.command.install import install
156 from distutils.command.install import install
157 from distutils.command.install_lib import install_lib
157 from distutils.command.install_lib import install_lib
158 from distutils.command.install_scripts import install_scripts
158 from distutils.command.install_scripts import install_scripts
159 from distutils.spawn import spawn, find_executable
159 from distutils.spawn import spawn, find_executable
160 from distutils import file_util
160 from distutils import file_util
161 from distutils.errors import (
161 from distutils.errors import (
162 CCompilerError,
162 CCompilerError,
163 DistutilsError,
163 DistutilsError,
164 DistutilsExecError,
164 DistutilsExecError,
165 )
165 )
166 from distutils.sysconfig import get_python_inc, get_config_var
166 from distutils.sysconfig import get_python_inc, get_config_var
167 from distutils.version import StrictVersion
167 from distutils.version import StrictVersion
168
168
169 def write_if_changed(path, content):
169 def write_if_changed(path, content):
170 """Write content to a file iff the content hasn't changed."""
170 """Write content to a file iff the content hasn't changed."""
171 if os.path.exists(path):
171 if os.path.exists(path):
172 with open(path, 'rb') as fh:
172 with open(path, 'rb') as fh:
173 current = fh.read()
173 current = fh.read()
174 else:
174 else:
175 current = b''
175 current = b''
176
176
177 if current != content:
177 if current != content:
178 with open(path, 'wb') as fh:
178 with open(path, 'wb') as fh:
179 fh.write(content)
179 fh.write(content)
180
180
181 scripts = ['hg']
181 scripts = ['hg']
182 if os.name == 'nt':
182 if os.name == 'nt':
183 # We remove hg.bat if we are able to build hg.exe.
183 # We remove hg.bat if we are able to build hg.exe.
184 scripts.append('contrib/win32/hg.bat')
184 scripts.append('contrib/win32/hg.bat')
185
185
186 def cancompile(cc, code):
186 def cancompile(cc, code):
187 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
187 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
188 devnull = oldstderr = None
188 devnull = oldstderr = None
189 try:
189 try:
190 fname = os.path.join(tmpdir, 'testcomp.c')
190 fname = os.path.join(tmpdir, 'testcomp.c')
191 f = open(fname, 'w')
191 f = open(fname, 'w')
192 f.write(code)
192 f.write(code)
193 f.close()
193 f.close()
194 # Redirect stderr to /dev/null to hide any error messages
194 # Redirect stderr to /dev/null to hide any error messages
195 # from the compiler.
195 # from the compiler.
196 # This will have to be changed if we ever have to check
196 # This will have to be changed if we ever have to check
197 # for a function on Windows.
197 # for a function on Windows.
198 devnull = open('/dev/null', 'w')
198 devnull = open('/dev/null', 'w')
199 oldstderr = os.dup(sys.stderr.fileno())
199 oldstderr = os.dup(sys.stderr.fileno())
200 os.dup2(devnull.fileno(), sys.stderr.fileno())
200 os.dup2(devnull.fileno(), sys.stderr.fileno())
201 objects = cc.compile([fname], output_dir=tmpdir)
201 objects = cc.compile([fname], output_dir=tmpdir)
202 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
202 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
203 return True
203 return True
204 except Exception:
204 except Exception:
205 return False
205 return False
206 finally:
206 finally:
207 if oldstderr is not None:
207 if oldstderr is not None:
208 os.dup2(oldstderr, sys.stderr.fileno())
208 os.dup2(oldstderr, sys.stderr.fileno())
209 if devnull is not None:
209 if devnull is not None:
210 devnull.close()
210 devnull.close()
211 shutil.rmtree(tmpdir)
211 shutil.rmtree(tmpdir)
212
212
213 # simplified version of distutils.ccompiler.CCompiler.has_function
213 # simplified version of distutils.ccompiler.CCompiler.has_function
214 # that actually removes its temporary files.
214 # that actually removes its temporary files.
215 def hasfunction(cc, funcname):
215 def hasfunction(cc, funcname):
216 code = 'int main(void) { %s(); }\n' % funcname
216 code = 'int main(void) { %s(); }\n' % funcname
217 return cancompile(cc, code)
217 return cancompile(cc, code)
218
218
219 def hasheader(cc, headername):
219 def hasheader(cc, headername):
220 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
220 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
221 return cancompile(cc, code)
221 return cancompile(cc, code)
222
222
223 # py2exe needs to be installed to work
223 # py2exe needs to be installed to work
224 try:
224 try:
225 import py2exe
225 import py2exe
226 py2exe.Distribution # silence unused import warning
226 py2exe.Distribution # silence unused import warning
227 py2exeloaded = True
227 py2exeloaded = True
228 # import py2exe's patched Distribution class
228 # import py2exe's patched Distribution class
229 from distutils.core import Distribution
229 from distutils.core import Distribution
230 except ImportError:
230 except ImportError:
231 py2exeloaded = False
231 py2exeloaded = False
232
232
233 def runcmd(cmd, env):
233 def runcmd(cmd, env):
234 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
234 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
235 stderr=subprocess.PIPE, env=env)
235 stderr=subprocess.PIPE, env=env)
236 out, err = p.communicate()
236 out, err = p.communicate()
237 return p.returncode, out, err
237 return p.returncode, out, err
238
238
239 class hgcommand(object):
239 class hgcommand(object):
240 def __init__(self, cmd, env):
240 def __init__(self, cmd, env):
241 self.cmd = cmd
241 self.cmd = cmd
242 self.env = env
242 self.env = env
243
243
244 def run(self, args):
244 def run(self, args):
245 cmd = self.cmd + args
245 cmd = self.cmd + args
246 returncode, out, err = runcmd(cmd, self.env)
246 returncode, out, err = runcmd(cmd, self.env)
247 err = filterhgerr(err)
247 err = filterhgerr(err)
248 if err or returncode != 0:
248 if err or returncode != 0:
249 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
249 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
250 printf(err, file=sys.stderr)
250 printf(err, file=sys.stderr)
251 return ''
251 return ''
252 return out
252 return out
253
253
254 def filterhgerr(err):
254 def filterhgerr(err):
255 # If root is executing setup.py, but the repository is owned by
255 # If root is executing setup.py, but the repository is owned by
256 # another user (as in "sudo python setup.py install") we will get
256 # another user (as in "sudo python setup.py install") we will get
257 # trust warnings since the .hg/hgrc file is untrusted. That is
257 # trust warnings since the .hg/hgrc file is untrusted. That is
258 # fine, we don't want to load it anyway. Python may warn about
258 # fine, we don't want to load it anyway. Python may warn about
259 # a missing __init__.py in mercurial/locale, we also ignore that.
259 # a missing __init__.py in mercurial/locale, we also ignore that.
260 err = [e for e in err.splitlines()
260 err = [e for e in err.splitlines()
261 if (not e.startswith(b'not trusting file')
261 if (not e.startswith(b'not trusting file')
262 and not e.startswith(b'warning: Not importing')
262 and not e.startswith(b'warning: Not importing')
263 and not e.startswith(b'obsolete feature not enabled')
263 and not e.startswith(b'obsolete feature not enabled')
264 and not e.startswith(b'*** failed to import extension')
264 and not e.startswith(b'*** failed to import extension')
265 and not e.startswith(b'devel-warn:'))]
265 and not e.startswith(b'devel-warn:'))]
266 return b'\n'.join(b' ' + e for e in err)
266 return b'\n'.join(b' ' + e for e in err)
267
267
268 def findhg():
268 def findhg():
269 """Try to figure out how we should invoke hg for examining the local
269 """Try to figure out how we should invoke hg for examining the local
270 repository contents.
270 repository contents.
271
271
272 Returns an hgcommand object."""
272 Returns an hgcommand object."""
273 # By default, prefer the "hg" command in the user's path. This was
273 # By default, prefer the "hg" command in the user's path. This was
274 # presumably the hg command that the user used to create this repository.
274 # presumably the hg command that the user used to create this repository.
275 #
275 #
276 # This repository may require extensions or other settings that would not
276 # This repository may require extensions or other settings that would not
277 # be enabled by running the hg script directly from this local repository.
277 # be enabled by running the hg script directly from this local repository.
278 hgenv = os.environ.copy()
278 hgenv = os.environ.copy()
279 # Use HGPLAIN to disable hgrc settings that would change output formatting,
279 # Use HGPLAIN to disable hgrc settings that would change output formatting,
280 # and disable localization for the same reasons.
280 # and disable localization for the same reasons.
281 hgenv['HGPLAIN'] = '1'
281 hgenv['HGPLAIN'] = '1'
282 hgenv['LANGUAGE'] = 'C'
282 hgenv['LANGUAGE'] = 'C'
283 hgcmd = ['hg']
283 hgcmd = ['hg']
284 # Run a simple "hg log" command just to see if using hg from the user's
284 # Run a simple "hg log" command just to see if using hg from the user's
285 # path works and can successfully interact with this repository.
285 # path works and can successfully interact with this repository.
286 check_cmd = ['log', '-r.', '-Ttest']
286 check_cmd = ['log', '-r.', '-Ttest']
287 try:
287 try:
288 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
288 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
289 except EnvironmentError:
289 except EnvironmentError:
290 retcode = -1
290 retcode = -1
291 if retcode == 0 and not filterhgerr(err):
291 if retcode == 0 and not filterhgerr(err):
292 return hgcommand(hgcmd, hgenv)
292 return hgcommand(hgcmd, hgenv)
293
293
294 # Fall back to trying the local hg installation.
294 # Fall back to trying the local hg installation.
295 hgenv = localhgenv()
295 hgenv = localhgenv()
296 hgcmd = [sys.executable, 'hg']
296 hgcmd = [sys.executable, 'hg']
297 try:
297 try:
298 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
298 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
299 except EnvironmentError:
299 except EnvironmentError:
300 retcode = -1
300 retcode = -1
301 if retcode == 0 and not filterhgerr(err):
301 if retcode == 0 and not filterhgerr(err):
302 return hgcommand(hgcmd, hgenv)
302 return hgcommand(hgcmd, hgenv)
303
303
304 raise SystemExit('Unable to find a working hg binary to extract the '
304 raise SystemExit('Unable to find a working hg binary to extract the '
305 'version from the repository tags')
305 'version from the repository tags')
306
306
307 def localhgenv():
307 def localhgenv():
308 """Get an environment dictionary to use for invoking or importing
308 """Get an environment dictionary to use for invoking or importing
309 mercurial from the local repository."""
309 mercurial from the local repository."""
310 # Execute hg out of this directory with a custom environment which takes
310 # Execute hg out of this directory with a custom environment which takes
311 # care to not use any hgrc files and do no localization.
311 # care to not use any hgrc files and do no localization.
312 env = {'HGMODULEPOLICY': 'py',
312 env = {'HGMODULEPOLICY': 'py',
313 'HGRCPATH': '',
313 'HGRCPATH': '',
314 'LANGUAGE': 'C',
314 'LANGUAGE': 'C',
315 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
315 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
316 if 'LD_LIBRARY_PATH' in os.environ:
316 if 'LD_LIBRARY_PATH' in os.environ:
317 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
317 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
318 if 'SystemRoot' in os.environ:
318 if 'SystemRoot' in os.environ:
319 # SystemRoot is required by Windows to load various DLLs. See:
319 # SystemRoot is required by Windows to load various DLLs. See:
320 # https://bugs.python.org/issue13524#msg148850
320 # https://bugs.python.org/issue13524#msg148850
321 env['SystemRoot'] = os.environ['SystemRoot']
321 env['SystemRoot'] = os.environ['SystemRoot']
322 return env
322 return env
323
323
324 version = ''
324 version = ''
325
325
326 if os.path.isdir('.hg'):
326 if os.path.isdir('.hg'):
327 hg = findhg()
327 hg = findhg()
328 cmd = ['log', '-r', '.', '--template', '{tags}\n']
328 cmd = ['log', '-r', '.', '--template', '{tags}\n']
329 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
329 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
330 hgid = sysstr(hg.run(['id', '-i'])).strip()
330 hgid = sysstr(hg.run(['id', '-i'])).strip()
331 if not hgid:
331 if not hgid:
332 # Bail out if hg is having problems interacting with this repository,
332 # Bail out if hg is having problems interacting with this repository,
333 # rather than falling through and producing a bogus version number.
333 # rather than falling through and producing a bogus version number.
334 # Continuing with an invalid version number will break extensions
334 # Continuing with an invalid version number will break extensions
335 # that define minimumhgversion.
335 # that define minimumhgversion.
336 raise SystemExit('Unable to determine hg version from local repository')
336 raise SystemExit('Unable to determine hg version from local repository')
337 if numerictags: # tag(s) found
337 if numerictags: # tag(s) found
338 version = numerictags[-1]
338 version = numerictags[-1]
339 if hgid.endswith('+'): # propagate the dirty status to the tag
339 if hgid.endswith('+'): # propagate the dirty status to the tag
340 version += '+'
340 version += '+'
341 else: # no tag found
341 else: # no tag found
342 ltagcmd = ['parents', '--template', '{latesttag}']
342 ltagcmd = ['parents', '--template', '{latesttag}']
343 ltag = sysstr(hg.run(ltagcmd))
343 ltag = sysstr(hg.run(ltagcmd))
344 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
344 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
345 changessince = len(hg.run(changessincecmd).splitlines())
345 changessince = len(hg.run(changessincecmd).splitlines())
346 version = '%s+%s-%s' % (ltag, changessince, hgid)
346 version = '%s+%s-%s' % (ltag, changessince, hgid)
347 if version.endswith('+'):
347 if version.endswith('+'):
348 version += time.strftime('%Y%m%d')
348 version += time.strftime('%Y%m%d')
349 elif os.path.exists('.hg_archival.txt'):
349 elif os.path.exists('.hg_archival.txt'):
350 kw = dict([[t.strip() for t in l.split(':', 1)]
350 kw = dict([[t.strip() for t in l.split(':', 1)]
351 for l in open('.hg_archival.txt')])
351 for l in open('.hg_archival.txt')])
352 if 'tag' in kw:
352 if 'tag' in kw:
353 version = kw['tag']
353 version = kw['tag']
354 elif 'latesttag' in kw:
354 elif 'latesttag' in kw:
355 if 'changessincelatesttag' in kw:
355 if 'changessincelatesttag' in kw:
356 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
356 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
357 else:
357 else:
358 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
358 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
359 else:
359 else:
360 version = kw.get('node', '')[:12]
360 version = kw.get('node', '')[:12]
361
361
362 if version:
362 if version:
363 versionb = version
363 versionb = version
364 if not isinstance(versionb, bytes):
364 if not isinstance(versionb, bytes):
365 versionb = versionb.encode('ascii')
365 versionb = versionb.encode('ascii')
366
366
367 write_if_changed('mercurial/__version__.py', b''.join([
367 write_if_changed('mercurial/__version__.py', b''.join([
368 b'# this file is autogenerated by setup.py\n'
368 b'# this file is autogenerated by setup.py\n'
369 b'version = b"%s"\n' % versionb,
369 b'version = b"%s"\n' % versionb,
370 ]))
370 ]))
371
371
372 try:
372 try:
373 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
373 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
374 os.environ['HGMODULEPOLICY'] = 'py'
374 os.environ['HGMODULEPOLICY'] = 'py'
375 from mercurial import __version__
375 from mercurial import __version__
376 version = __version__.version
376 version = __version__.version
377 except ImportError:
377 except ImportError:
378 version = b'unknown'
378 version = b'unknown'
379 finally:
379 finally:
380 if oldpolicy is None:
380 if oldpolicy is None:
381 del os.environ['HGMODULEPOLICY']
381 del os.environ['HGMODULEPOLICY']
382 else:
382 else:
383 os.environ['HGMODULEPOLICY'] = oldpolicy
383 os.environ['HGMODULEPOLICY'] = oldpolicy
384
384
385 class hgbuild(build):
385 class hgbuild(build):
386 # Insert hgbuildmo first so that files in mercurial/locale/ are found
386 # Insert hgbuildmo first so that files in mercurial/locale/ are found
387 # when build_py is run next.
387 # when build_py is run next.
388 sub_commands = [('build_mo', None)] + build.sub_commands
388 sub_commands = [('build_mo', None)] + build.sub_commands
389
389
390 class hgbuildmo(build):
390 class hgbuildmo(build):
391
391
392 description = "build translations (.mo files)"
392 description = "build translations (.mo files)"
393
393
394 def run(self):
394 def run(self):
395 if not find_executable('msgfmt'):
395 if not find_executable('msgfmt'):
396 self.warn("could not find msgfmt executable, no translations "
396 self.warn("could not find msgfmt executable, no translations "
397 "will be built")
397 "will be built")
398 return
398 return
399
399
400 podir = 'i18n'
400 podir = 'i18n'
401 if not os.path.isdir(podir):
401 if not os.path.isdir(podir):
402 self.warn("could not find %s/ directory" % podir)
402 self.warn("could not find %s/ directory" % podir)
403 return
403 return
404
404
405 join = os.path.join
405 join = os.path.join
406 for po in os.listdir(podir):
406 for po in os.listdir(podir):
407 if not po.endswith('.po'):
407 if not po.endswith('.po'):
408 continue
408 continue
409 pofile = join(podir, po)
409 pofile = join(podir, po)
410 modir = join('locale', po[:-3], 'LC_MESSAGES')
410 modir = join('locale', po[:-3], 'LC_MESSAGES')
411 mofile = join(modir, 'hg.mo')
411 mofile = join(modir, 'hg.mo')
412 mobuildfile = join('mercurial', mofile)
412 mobuildfile = join('mercurial', mofile)
413 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
413 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
414 if sys.platform != 'sunos5':
414 if sys.platform != 'sunos5':
415 # msgfmt on Solaris does not know about -c
415 # msgfmt on Solaris does not know about -c
416 cmd.append('-c')
416 cmd.append('-c')
417 self.mkpath(join('mercurial', modir))
417 self.mkpath(join('mercurial', modir))
418 self.make_file([pofile], mobuildfile, spawn, (cmd,))
418 self.make_file([pofile], mobuildfile, spawn, (cmd,))
419
419
420
420
421 class hgdist(Distribution):
421 class hgdist(Distribution):
422 pure = False
422 pure = False
423 cffi = ispypy
423 cffi = ispypy
424
424
425 global_options = Distribution.global_options + \
425 global_options = Distribution.global_options + \
426 [('pure', None, "use pure (slow) Python "
426 [('pure', None, "use pure (slow) Python "
427 "code instead of C extensions"),
427 "code instead of C extensions"),
428 ]
428 ]
429
429
430 def has_ext_modules(self):
430 def has_ext_modules(self):
431 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
431 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
432 # too late for some cases
432 # too late for some cases
433 return not self.pure and Distribution.has_ext_modules(self)
433 return not self.pure and Distribution.has_ext_modules(self)
434
434
435 # This is ugly as a one-liner. So use a variable.
435 # This is ugly as a one-liner. So use a variable.
436 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
436 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
437 buildextnegops['no-zstd'] = 'zstd'
437 buildextnegops['no-zstd'] = 'zstd'
438
438
439 class hgbuildext(build_ext):
439 class hgbuildext(build_ext):
440 user_options = build_ext.user_options + [
440 user_options = build_ext.user_options + [
441 ('zstd', None, 'compile zstd bindings [default]'),
441 ('zstd', None, 'compile zstd bindings [default]'),
442 ('no-zstd', None, 'do not compile zstd bindings'),
442 ('no-zstd', None, 'do not compile zstd bindings'),
443 ]
443 ]
444
444
445 boolean_options = build_ext.boolean_options + ['zstd']
445 boolean_options = build_ext.boolean_options + ['zstd']
446 negative_opt = buildextnegops
446 negative_opt = buildextnegops
447
447
448 def initialize_options(self):
448 def initialize_options(self):
449 self.zstd = True
449 self.zstd = True
450 return build_ext.initialize_options(self)
450 return build_ext.initialize_options(self)
451
451
452 def build_extensions(self):
452 def build_extensions(self):
453 # Filter out zstd if disabled via argument.
453 # Filter out zstd if disabled via argument.
454 if not self.zstd:
454 if not self.zstd:
455 self.extensions = [e for e in self.extensions
455 self.extensions = [e for e in self.extensions
456 if e.name != 'mercurial.zstd']
456 if e.name != 'mercurial.zstd']
457
457
458 return build_ext.build_extensions(self)
458 return build_ext.build_extensions(self)
459
459
460 def build_extension(self, ext):
460 def build_extension(self, ext):
461 try:
461 try:
462 build_ext.build_extension(self, ext)
462 build_ext.build_extension(self, ext)
463 except CCompilerError:
463 except CCompilerError:
464 if not getattr(ext, 'optional', False):
464 if not getattr(ext, 'optional', False):
465 raise
465 raise
466 log.warn("Failed to build optional extension '%s' (skipping)",
466 log.warn("Failed to build optional extension '%s' (skipping)",
467 ext.name)
467 ext.name)
468
468
469 class hgbuildscripts(build_scripts):
469 class hgbuildscripts(build_scripts):
470 def run(self):
470 def run(self):
471 if os.name != 'nt' or self.distribution.pure:
471 if os.name != 'nt' or self.distribution.pure:
472 return build_scripts.run(self)
472 return build_scripts.run(self)
473
473
474 exebuilt = False
474 exebuilt = False
475 try:
475 try:
476 self.run_command('build_hgexe')
476 self.run_command('build_hgexe')
477 exebuilt = True
477 exebuilt = True
478 except (DistutilsError, CCompilerError):
478 except (DistutilsError, CCompilerError):
479 log.warn('failed to build optional hg.exe')
479 log.warn('failed to build optional hg.exe')
480
480
481 if exebuilt:
481 if exebuilt:
482 # Copying hg.exe to the scripts build directory ensures it is
482 # Copying hg.exe to the scripts build directory ensures it is
483 # installed by the install_scripts command.
483 # installed by the install_scripts command.
484 hgexecommand = self.get_finalized_command('build_hgexe')
484 hgexecommand = self.get_finalized_command('build_hgexe')
485 dest = os.path.join(self.build_dir, 'hg.exe')
485 dest = os.path.join(self.build_dir, 'hg.exe')
486 self.mkpath(self.build_dir)
486 self.mkpath(self.build_dir)
487 self.copy_file(hgexecommand.hgexepath, dest)
487 self.copy_file(hgexecommand.hgexepath, dest)
488
488
489 # Remove hg.bat because it is redundant with hg.exe.
489 # Remove hg.bat because it is redundant with hg.exe.
490 self.scripts.remove('contrib/win32/hg.bat')
490 self.scripts.remove('contrib/win32/hg.bat')
491
491
492 return build_scripts.run(self)
492 return build_scripts.run(self)
493
493
494 class hgbuildpy(build_py):
494 class hgbuildpy(build_py):
495 def finalize_options(self):
495 def finalize_options(self):
496 build_py.finalize_options(self)
496 build_py.finalize_options(self)
497
497
498 if self.distribution.pure:
498 if self.distribution.pure:
499 self.distribution.ext_modules = []
499 self.distribution.ext_modules = []
500 elif self.distribution.cffi:
500 elif self.distribution.cffi:
501 from mercurial.cffi import (
501 from mercurial.cffi import (
502 bdiffbuild,
502 bdiffbuild,
503 mpatchbuild,
503 mpatchbuild,
504 )
504 )
505 exts = [mpatchbuild.ffi.distutils_extension(),
505 exts = [mpatchbuild.ffi.distutils_extension(),
506 bdiffbuild.ffi.distutils_extension()]
506 bdiffbuild.ffi.distutils_extension()]
507 # cffi modules go here
507 # cffi modules go here
508 if sys.platform == 'darwin':
508 if sys.platform == 'darwin':
509 from mercurial.cffi import osutilbuild
509 from mercurial.cffi import osutilbuild
510 exts.append(osutilbuild.ffi.distutils_extension())
510 exts.append(osutilbuild.ffi.distutils_extension())
511 self.distribution.ext_modules = exts
511 self.distribution.ext_modules = exts
512 else:
512 else:
513 h = os.path.join(get_python_inc(), 'Python.h')
513 h = os.path.join(get_python_inc(), 'Python.h')
514 if not os.path.exists(h):
514 if not os.path.exists(h):
515 raise SystemExit('Python headers are required to build '
515 raise SystemExit('Python headers are required to build '
516 'Mercurial but weren\'t found in %s' % h)
516 'Mercurial but weren\'t found in %s' % h)
517
517
518 def run(self):
518 def run(self):
519 basepath = os.path.join(self.build_lib, 'mercurial')
519 basepath = os.path.join(self.build_lib, 'mercurial')
520 self.mkpath(basepath)
520 self.mkpath(basepath)
521
521
522 if self.distribution.pure:
522 if self.distribution.pure:
523 modulepolicy = 'py'
523 modulepolicy = 'py'
524 elif self.build_lib == '.':
524 elif self.build_lib == '.':
525 # in-place build should run without rebuilding C extensions
525 # in-place build should run without rebuilding C extensions
526 modulepolicy = 'allow'
526 modulepolicy = 'allow'
527 else:
527 else:
528 modulepolicy = 'c'
528 modulepolicy = 'c'
529
529
530 content = b''.join([
530 content = b''.join([
531 b'# this file is autogenerated by setup.py\n',
531 b'# this file is autogenerated by setup.py\n',
532 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
532 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
533 ])
533 ])
534 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'),
534 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'),
535 content)
535 content)
536
536
537 build_py.run(self)
537 build_py.run(self)
538
538
539 class buildhgextindex(Command):
539 class buildhgextindex(Command):
540 description = 'generate prebuilt index of hgext (for frozen package)'
540 description = 'generate prebuilt index of hgext (for frozen package)'
541 user_options = []
541 user_options = []
542 _indexfilename = 'hgext/__index__.py'
542 _indexfilename = 'hgext/__index__.py'
543
543
544 def initialize_options(self):
544 def initialize_options(self):
545 pass
545 pass
546
546
547 def finalize_options(self):
547 def finalize_options(self):
548 pass
548 pass
549
549
550 def run(self):
550 def run(self):
551 if os.path.exists(self._indexfilename):
551 if os.path.exists(self._indexfilename):
552 with open(self._indexfilename, 'w') as f:
552 with open(self._indexfilename, 'w') as f:
553 f.write('# empty\n')
553 f.write('# empty\n')
554
554
555 # here no extension enabled, disabled() lists up everything
555 # here no extension enabled, disabled() lists up everything
556 code = ('import pprint; from mercurial import extensions; '
556 code = ('import pprint; from mercurial import extensions; '
557 'pprint.pprint(extensions.disabled())')
557 'pprint.pprint(extensions.disabled())')
558 returncode, out, err = runcmd([sys.executable, '-c', code],
558 returncode, out, err = runcmd([sys.executable, '-c', code],
559 localhgenv())
559 localhgenv())
560 if err or returncode != 0:
560 if err or returncode != 0:
561 raise DistutilsExecError(err)
561 raise DistutilsExecError(err)
562
562
563 with open(self._indexfilename, 'w') as f:
563 with open(self._indexfilename, 'w') as f:
564 f.write('# this file is autogenerated by setup.py\n')
564 f.write('# this file is autogenerated by setup.py\n')
565 f.write('docs = ')
565 f.write('docs = ')
566 f.write(out)
566 f.write(out)
567
567
568 class buildhgexe(build_ext):
568 class buildhgexe(build_ext):
569 description = 'compile hg.exe from mercurial/exewrapper.c'
569 description = 'compile hg.exe from mercurial/exewrapper.c'
570 user_options = build_ext.user_options + [
570 user_options = build_ext.user_options + [
571 ('long-paths-support', None, 'enable support for long paths on '
571 ('long-paths-support', None, 'enable support for long paths on '
572 'Windows (off by default and '
572 'Windows (off by default and '
573 'experimental)'),
573 'experimental)'),
574 ]
574 ]
575
575
576 LONG_PATHS_MANIFEST = """
576 LONG_PATHS_MANIFEST = """
577 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
577 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
578 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
578 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
579 <application>
579 <application>
580 <windowsSettings
580 <windowsSettings
581 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
581 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
582 <ws2:longPathAware>true</ws2:longPathAware>
582 <ws2:longPathAware>true</ws2:longPathAware>
583 </windowsSettings>
583 </windowsSettings>
584 </application>
584 </application>
585 </assembly>"""
585 </assembly>"""
586
586
587 def initialize_options(self):
587 def initialize_options(self):
588 build_ext.initialize_options(self)
588 build_ext.initialize_options(self)
589 self.long_paths_support = False
589 self.long_paths_support = False
590
590
591 def build_extensions(self):
591 def build_extensions(self):
592 if os.name != 'nt':
592 if os.name != 'nt':
593 return
593 return
594 if isinstance(self.compiler, HackedMingw32CCompiler):
594 if isinstance(self.compiler, HackedMingw32CCompiler):
595 self.compiler.compiler_so = self.compiler.compiler # no -mdll
595 self.compiler.compiler_so = self.compiler.compiler # no -mdll
596 self.compiler.dll_libraries = [] # no -lmsrvc90
596 self.compiler.dll_libraries = [] # no -lmsrvc90
597
597
598 # Different Python installs can have different Python library
598 # Different Python installs can have different Python library
599 # names. e.g. the official CPython distribution uses pythonXY.dll
599 # names. e.g. the official CPython distribution uses pythonXY.dll
600 # and MinGW uses libpythonX.Y.dll.
600 # and MinGW uses libpythonX.Y.dll.
601 _kernel32 = ctypes.windll.kernel32
601 _kernel32 = ctypes.windll.kernel32
602 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
602 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
603 ctypes.c_void_p,
603 ctypes.c_void_p,
604 ctypes.c_ulong]
604 ctypes.c_ulong]
605 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
605 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
606 size = 1000
606 size = 1000
607 buf = ctypes.create_string_buffer(size + 1)
607 buf = ctypes.create_string_buffer(size + 1)
608 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
608 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
609 size)
609 size)
610
610
611 if filelen > 0 and filelen != size:
611 if filelen > 0 and filelen != size:
612 dllbasename = os.path.basename(buf.value)
612 dllbasename = os.path.basename(buf.value)
613 if not dllbasename.lower().endswith(b'.dll'):
613 if not dllbasename.lower().endswith(b'.dll'):
614 raise SystemExit('Python DLL does not end with .dll: %s' %
614 raise SystemExit('Python DLL does not end with .dll: %s' %
615 dllbasename)
615 dllbasename)
616 pythonlib = dllbasename[:-4]
616 pythonlib = dllbasename[:-4]
617 else:
617 else:
618 log.warn('could not determine Python DLL filename; '
618 log.warn('could not determine Python DLL filename; '
619 'assuming pythonXY')
619 'assuming pythonXY')
620
620
621 hv = sys.hexversion
621 hv = sys.hexversion
622 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
622 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
623
623
624 log.info('using %s as Python library name' % pythonlib)
624 log.info('using %s as Python library name' % pythonlib)
625 with open('mercurial/hgpythonlib.h', 'wb') as f:
625 with open('mercurial/hgpythonlib.h', 'wb') as f:
626 f.write(b'/* this file is autogenerated by setup.py */\n')
626 f.write(b'/* this file is autogenerated by setup.py */\n')
627 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
627 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
628 objects = self.compiler.compile(['mercurial/exewrapper.c'],
628 objects = self.compiler.compile(['mercurial/exewrapper.c'],
629 output_dir=self.build_temp)
629 output_dir=self.build_temp)
630 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
630 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
631 self.hgtarget = os.path.join(dir, 'hg')
631 self.hgtarget = os.path.join(dir, 'hg')
632 self.compiler.link_executable(objects, self.hgtarget,
632 self.compiler.link_executable(objects, self.hgtarget,
633 libraries=[],
633 libraries=[],
634 output_dir=self.build_temp)
634 output_dir=self.build_temp)
635 if self.long_paths_support:
635 if self.long_paths_support:
636 self.addlongpathsmanifest()
636 self.addlongpathsmanifest()
637
637
638 def addlongpathsmanifest(self):
638 def addlongpathsmanifest(self):
639 """Add manifest pieces so that hg.exe understands long paths
639 """Add manifest pieces so that hg.exe understands long paths
640
640
641 This is an EXPERIMENTAL feature, use with care.
641 This is an EXPERIMENTAL feature, use with care.
642 To enable long paths support, one needs to do two things:
642 To enable long paths support, one needs to do two things:
643 - build Mercurial with --long-paths-support option
643 - build Mercurial with --long-paths-support option
644 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
644 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
645 LongPathsEnabled to have value 1.
645 LongPathsEnabled to have value 1.
646
646
647 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
647 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
648 it happens because Mercurial uses mt.exe circa 2008, which is not
648 it happens because Mercurial uses mt.exe circa 2008, which is not
649 yet aware of long paths support in the manifest (I think so at least).
649 yet aware of long paths support in the manifest (I think so at least).
650 This does not stop mt.exe from embedding/merging the XML properly.
650 This does not stop mt.exe from embedding/merging the XML properly.
651
651
652 Why resource #1 should be used for .exe manifests? I don't know and
652 Why resource #1 should be used for .exe manifests? I don't know and
653 wasn't able to find an explanation for mortals. But it seems to work.
653 wasn't able to find an explanation for mortals. But it seems to work.
654 """
654 """
655 exefname = self.compiler.executable_filename(self.hgtarget)
655 exefname = self.compiler.executable_filename(self.hgtarget)
656 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
656 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
657 os.close(fdauto)
657 os.close(fdauto)
658 with open(manfname, 'w') as f:
658 with open(manfname, 'w') as f:
659 f.write(self.LONG_PATHS_MANIFEST)
659 f.write(self.LONG_PATHS_MANIFEST)
660 log.info("long paths manifest is written to '%s'" % manfname)
660 log.info("long paths manifest is written to '%s'" % manfname)
661 inputresource = '-inputresource:%s;#1' % exefname
661 inputresource = '-inputresource:%s;#1' % exefname
662 outputresource = '-outputresource:%s;#1' % exefname
662 outputresource = '-outputresource:%s;#1' % exefname
663 log.info("running mt.exe to update hg.exe's manifest in-place")
663 log.info("running mt.exe to update hg.exe's manifest in-place")
664 # supplying both -manifest and -inputresource to mt.exe makes
664 # supplying both -manifest and -inputresource to mt.exe makes
665 # it merge the embedded and supplied manifests in the -outputresource
665 # it merge the embedded and supplied manifests in the -outputresource
666 self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
666 self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
667 inputresource, outputresource])
667 inputresource, outputresource])
668 log.info("done updating hg.exe's manifest")
668 log.info("done updating hg.exe's manifest")
669 os.remove(manfname)
669 os.remove(manfname)
670
670
671 @property
671 @property
672 def hgexepath(self):
672 def hgexepath(self):
673 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
673 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
674 return os.path.join(self.build_temp, dir, 'hg.exe')
674 return os.path.join(self.build_temp, dir, 'hg.exe')
675
675
676 class hginstall(install):
676 class hginstall(install):
677
677
678 user_options = install.user_options + [
678 user_options = install.user_options + [
679 ('old-and-unmanageable', None,
679 ('old-and-unmanageable', None,
680 'noop, present for eggless setuptools compat'),
680 'noop, present for eggless setuptools compat'),
681 ('single-version-externally-managed', None,
681 ('single-version-externally-managed', None,
682 'noop, present for eggless setuptools compat'),
682 'noop, present for eggless setuptools compat'),
683 ]
683 ]
684
684
685 # Also helps setuptools not be sad while we refuse to create eggs.
685 # Also helps setuptools not be sad while we refuse to create eggs.
686 single_version_externally_managed = True
686 single_version_externally_managed = True
687
687
688 def get_sub_commands(self):
688 def get_sub_commands(self):
689 # Screen out egg related commands to prevent egg generation. But allow
689 # Screen out egg related commands to prevent egg generation. But allow
690 # mercurial.egg-info generation, since that is part of modern
690 # mercurial.egg-info generation, since that is part of modern
691 # packaging.
691 # packaging.
692 excl = set(['bdist_egg'])
692 excl = set(['bdist_egg'])
693 return filter(lambda x: x not in excl, install.get_sub_commands(self))
693 return filter(lambda x: x not in excl, install.get_sub_commands(self))
694
694
695 class hginstalllib(install_lib):
695 class hginstalllib(install_lib):
696 '''
696 '''
697 This is a specialization of install_lib that replaces the copy_file used
697 This is a specialization of install_lib that replaces the copy_file used
698 there so that it supports setting the mode of files after copying them,
698 there so that it supports setting the mode of files after copying them,
699 instead of just preserving the mode that the files originally had. If your
699 instead of just preserving the mode that the files originally had. If your
700 system has a umask of something like 027, preserving the permissions when
700 system has a umask of something like 027, preserving the permissions when
701 copying will lead to a broken install.
701 copying will lead to a broken install.
702
702
703 Note that just passing keep_permissions=False to copy_file would be
703 Note that just passing keep_permissions=False to copy_file would be
704 insufficient, as it might still be applying a umask.
704 insufficient, as it might still be applying a umask.
705 '''
705 '''
706
706
707 def run(self):
707 def run(self):
708 realcopyfile = file_util.copy_file
708 realcopyfile = file_util.copy_file
709 def copyfileandsetmode(*args, **kwargs):
709 def copyfileandsetmode(*args, **kwargs):
710 src, dst = args[0], args[1]
710 src, dst = args[0], args[1]
711 dst, copied = realcopyfile(*args, **kwargs)
711 dst, copied = realcopyfile(*args, **kwargs)
712 if copied:
712 if copied:
713 st = os.stat(src)
713 st = os.stat(src)
714 # Persist executable bit (apply it to group and other if user
714 # Persist executable bit (apply it to group and other if user
715 # has it)
715 # has it)
716 if st[stat.ST_MODE] & stat.S_IXUSR:
716 if st[stat.ST_MODE] & stat.S_IXUSR:
717 setmode = int('0755', 8)
717 setmode = int('0755', 8)
718 else:
718 else:
719 setmode = int('0644', 8)
719 setmode = int('0644', 8)
720 m = stat.S_IMODE(st[stat.ST_MODE])
720 m = stat.S_IMODE(st[stat.ST_MODE])
721 m = (m & ~int('0777', 8)) | setmode
721 m = (m & ~int('0777', 8)) | setmode
722 os.chmod(dst, m)
722 os.chmod(dst, m)
723 file_util.copy_file = copyfileandsetmode
723 file_util.copy_file = copyfileandsetmode
724 try:
724 try:
725 install_lib.run(self)
725 install_lib.run(self)
726 finally:
726 finally:
727 file_util.copy_file = realcopyfile
727 file_util.copy_file = realcopyfile
728
728
729 class hginstallscripts(install_scripts):
729 class hginstallscripts(install_scripts):
730 '''
730 '''
731 This is a specialization of install_scripts that replaces the @LIBDIR@ with
731 This is a specialization of install_scripts that replaces the @LIBDIR@ with
732 the configured directory for modules. If possible, the path is made relative
732 the configured directory for modules. If possible, the path is made relative
733 to the directory for scripts.
733 to the directory for scripts.
734 '''
734 '''
735
735
736 def initialize_options(self):
736 def initialize_options(self):
737 install_scripts.initialize_options(self)
737 install_scripts.initialize_options(self)
738
738
739 self.install_lib = None
739 self.install_lib = None
740
740
741 def finalize_options(self):
741 def finalize_options(self):
742 install_scripts.finalize_options(self)
742 install_scripts.finalize_options(self)
743 self.set_undefined_options('install',
743 self.set_undefined_options('install',
744 ('install_lib', 'install_lib'))
744 ('install_lib', 'install_lib'))
745
745
746 def run(self):
746 def run(self):
747 install_scripts.run(self)
747 install_scripts.run(self)
748
748
749 # It only makes sense to replace @LIBDIR@ with the install path if
749 # It only makes sense to replace @LIBDIR@ with the install path if
750 # the install path is known. For wheels, the logic below calculates
750 # the install path is known. For wheels, the logic below calculates
751 # the libdir to be "../..". This is because the internal layout of a
751 # the libdir to be "../..". This is because the internal layout of a
752 # wheel archive looks like:
752 # wheel archive looks like:
753 #
753 #
754 # mercurial-3.6.1.data/scripts/hg
754 # mercurial-3.6.1.data/scripts/hg
755 # mercurial/__init__.py
755 # mercurial/__init__.py
756 #
756 #
757 # When installing wheels, the subdirectories of the "<pkg>.data"
757 # When installing wheels, the subdirectories of the "<pkg>.data"
758 # directory are translated to system local paths and files therein
758 # directory are translated to system local paths and files therein
759 # are copied in place. The mercurial/* files are installed into the
759 # are copied in place. The mercurial/* files are installed into the
760 # site-packages directory. However, the site-packages directory
760 # site-packages directory. However, the site-packages directory
761 # isn't known until wheel install time. This means we have no clue
761 # isn't known until wheel install time. This means we have no clue
762 # at wheel generation time what the installed site-packages directory
762 # at wheel generation time what the installed site-packages directory
763 # will be. And, wheels don't appear to provide the ability to register
763 # will be. And, wheels don't appear to provide the ability to register
764 # custom code to run during wheel installation. This all means that
764 # custom code to run during wheel installation. This all means that
765 # we can't reliably set the libdir in wheels: the default behavior
765 # we can't reliably set the libdir in wheels: the default behavior
766 # of looking in sys.path must do.
766 # of looking in sys.path must do.
767
767
768 if (os.path.splitdrive(self.install_dir)[0] !=
768 if (os.path.splitdrive(self.install_dir)[0] !=
769 os.path.splitdrive(self.install_lib)[0]):
769 os.path.splitdrive(self.install_lib)[0]):
770 # can't make relative paths from one drive to another, so use an
770 # can't make relative paths from one drive to another, so use an
771 # absolute path instead
771 # absolute path instead
772 libdir = self.install_lib
772 libdir = self.install_lib
773 else:
773 else:
774 common = os.path.commonprefix((self.install_dir, self.install_lib))
774 common = os.path.commonprefix((self.install_dir, self.install_lib))
775 rest = self.install_dir[len(common):]
775 rest = self.install_dir[len(common):]
776 uplevel = len([n for n in os.path.split(rest) if n])
776 uplevel = len([n for n in os.path.split(rest) if n])
777
777
778 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
778 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
779
779
780 for outfile in self.outfiles:
780 for outfile in self.outfiles:
781 with open(outfile, 'rb') as fp:
781 with open(outfile, 'rb') as fp:
782 data = fp.read()
782 data = fp.read()
783
783
784 # skip binary files
784 # skip binary files
785 if b'\0' in data:
785 if b'\0' in data:
786 continue
786 continue
787
787
788 # During local installs, the shebang will be rewritten to the final
788 # During local installs, the shebang will be rewritten to the final
789 # install path. During wheel packaging, the shebang has a special
789 # install path. During wheel packaging, the shebang has a special
790 # value.
790 # value.
791 if data.startswith(b'#!python'):
791 if data.startswith(b'#!python'):
792 log.info('not rewriting @LIBDIR@ in %s because install path '
792 log.info('not rewriting @LIBDIR@ in %s because install path '
793 'not known' % outfile)
793 'not known' % outfile)
794 continue
794 continue
795
795
796 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
796 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
797 with open(outfile, 'wb') as fp:
797 with open(outfile, 'wb') as fp:
798 fp.write(data)
798 fp.write(data)
799
799
800 cmdclass = {'build': hgbuild,
800 cmdclass = {'build': hgbuild,
801 'build_mo': hgbuildmo,
801 'build_mo': hgbuildmo,
802 'build_ext': hgbuildext,
802 'build_ext': hgbuildext,
803 'build_py': hgbuildpy,
803 'build_py': hgbuildpy,
804 'build_scripts': hgbuildscripts,
804 'build_scripts': hgbuildscripts,
805 'build_hgextindex': buildhgextindex,
805 'build_hgextindex': buildhgextindex,
806 'install': hginstall,
806 'install': hginstall,
807 'install_lib': hginstalllib,
807 'install_lib': hginstalllib,
808 'install_scripts': hginstallscripts,
808 'install_scripts': hginstallscripts,
809 'build_hgexe': buildhgexe,
809 'build_hgexe': buildhgexe,
810 }
810 }
811
811
812 packages = ['mercurial',
812 packages = ['mercurial',
813 'mercurial.cext',
813 'mercurial.cext',
814 'mercurial.cffi',
814 'mercurial.cffi',
815 'mercurial.hgweb',
815 'mercurial.hgweb',
816 'mercurial.pure',
816 'mercurial.pure',
817 'mercurial.thirdparty',
817 'mercurial.thirdparty',
818 'mercurial.thirdparty.attr',
818 'mercurial.thirdparty.attr',
819 'mercurial.thirdparty.cbor',
819 'mercurial.thirdparty.cbor',
820 'mercurial.thirdparty.cbor.cbor2',
820 'mercurial.thirdparty.cbor.cbor2',
821 'mercurial.thirdparty.concurrent',
822 'mercurial.thirdparty.concurrent.futures',
823 'mercurial.thirdparty.zope',
821 'mercurial.thirdparty.zope',
824 'mercurial.thirdparty.zope.interface',
822 'mercurial.thirdparty.zope.interface',
825 'mercurial.utils',
823 'mercurial.utils',
826 'mercurial.revlogutils',
824 'mercurial.revlogutils',
827 'hgext', 'hgext.convert', 'hgext.fsmonitor',
825 'hgext', 'hgext.convert', 'hgext.fsmonitor',
828 'hgext.fastannotate',
826 'hgext.fastannotate',
829 'hgext.fsmonitor.pywatchman',
827 'hgext.fsmonitor.pywatchman',
830 'hgext.infinitepush',
828 'hgext.infinitepush',
831 'hgext.highlight',
829 'hgext.highlight',
832 'hgext.largefiles', 'hgext.lfs', 'hgext.narrow',
830 'hgext.largefiles', 'hgext.lfs', 'hgext.narrow',
833 'hgext.zeroconf', 'hgext3rd',
831 'hgext.zeroconf', 'hgext3rd',
834 'hgdemandimport']
832 'hgdemandimport']
833 if sys.version_info[0] == 2:
834 packages.extend(['mercurial.thirdparty.concurrent',
835 'mercurial.thirdparty.concurrent.futures'])
835
836
836 common_depends = ['mercurial/bitmanipulation.h',
837 common_depends = ['mercurial/bitmanipulation.h',
837 'mercurial/compat.h',
838 'mercurial/compat.h',
838 'mercurial/cext/util.h']
839 'mercurial/cext/util.h']
839 common_include_dirs = ['mercurial']
840 common_include_dirs = ['mercurial']
840
841
841 osutil_cflags = []
842 osutil_cflags = []
842 osutil_ldflags = []
843 osutil_ldflags = []
843
844
844 # platform specific macros
845 # platform specific macros
845 for plat, func in [('bsd', 'setproctitle')]:
846 for plat, func in [('bsd', 'setproctitle')]:
846 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
847 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
847 osutil_cflags.append('-DHAVE_%s' % func.upper())
848 osutil_cflags.append('-DHAVE_%s' % func.upper())
848
849
849 for plat, macro, code in [
850 for plat, macro, code in [
850 ('bsd|darwin', 'BSD_STATFS', '''
851 ('bsd|darwin', 'BSD_STATFS', '''
851 #include <sys/param.h>
852 #include <sys/param.h>
852 #include <sys/mount.h>
853 #include <sys/mount.h>
853 int main() { struct statfs s; return sizeof(s.f_fstypename); }
854 int main() { struct statfs s; return sizeof(s.f_fstypename); }
854 '''),
855 '''),
855 ('linux', 'LINUX_STATFS', '''
856 ('linux', 'LINUX_STATFS', '''
856 #include <linux/magic.h>
857 #include <linux/magic.h>
857 #include <sys/vfs.h>
858 #include <sys/vfs.h>
858 int main() { struct statfs s; return sizeof(s.f_type); }
859 int main() { struct statfs s; return sizeof(s.f_type); }
859 '''),
860 '''),
860 ]:
861 ]:
861 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
862 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
862 osutil_cflags.append('-DHAVE_%s' % macro)
863 osutil_cflags.append('-DHAVE_%s' % macro)
863
864
864 if sys.platform == 'darwin':
865 if sys.platform == 'darwin':
865 osutil_ldflags += ['-framework', 'ApplicationServices']
866 osutil_ldflags += ['-framework', 'ApplicationServices']
866
867
867 xdiff_srcs = [
868 xdiff_srcs = [
868 'mercurial/thirdparty/xdiff/xdiffi.c',
869 'mercurial/thirdparty/xdiff/xdiffi.c',
869 'mercurial/thirdparty/xdiff/xprepare.c',
870 'mercurial/thirdparty/xdiff/xprepare.c',
870 'mercurial/thirdparty/xdiff/xutils.c',
871 'mercurial/thirdparty/xdiff/xutils.c',
871 ]
872 ]
872
873
873 xdiff_headers = [
874 xdiff_headers = [
874 'mercurial/thirdparty/xdiff/xdiff.h',
875 'mercurial/thirdparty/xdiff/xdiff.h',
875 'mercurial/thirdparty/xdiff/xdiffi.h',
876 'mercurial/thirdparty/xdiff/xdiffi.h',
876 'mercurial/thirdparty/xdiff/xinclude.h',
877 'mercurial/thirdparty/xdiff/xinclude.h',
877 'mercurial/thirdparty/xdiff/xmacros.h',
878 'mercurial/thirdparty/xdiff/xmacros.h',
878 'mercurial/thirdparty/xdiff/xprepare.h',
879 'mercurial/thirdparty/xdiff/xprepare.h',
879 'mercurial/thirdparty/xdiff/xtypes.h',
880 'mercurial/thirdparty/xdiff/xtypes.h',
880 'mercurial/thirdparty/xdiff/xutils.h',
881 'mercurial/thirdparty/xdiff/xutils.h',
881 ]
882 ]
882
883
883 extmodules = [
884 extmodules = [
884 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
885 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
885 include_dirs=common_include_dirs,
886 include_dirs=common_include_dirs,
886 depends=common_depends),
887 depends=common_depends),
887 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
888 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
888 'mercurial/cext/bdiff.c'] + xdiff_srcs,
889 'mercurial/cext/bdiff.c'] + xdiff_srcs,
889 include_dirs=common_include_dirs,
890 include_dirs=common_include_dirs,
890 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers),
891 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers),
891 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
892 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
892 'mercurial/cext/mpatch.c'],
893 'mercurial/cext/mpatch.c'],
893 include_dirs=common_include_dirs,
894 include_dirs=common_include_dirs,
894 depends=common_depends),
895 depends=common_depends),
895 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
896 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
896 'mercurial/cext/dirs.c',
897 'mercurial/cext/dirs.c',
897 'mercurial/cext/manifest.c',
898 'mercurial/cext/manifest.c',
898 'mercurial/cext/parsers.c',
899 'mercurial/cext/parsers.c',
899 'mercurial/cext/pathencode.c',
900 'mercurial/cext/pathencode.c',
900 'mercurial/cext/revlog.c'],
901 'mercurial/cext/revlog.c'],
901 include_dirs=common_include_dirs,
902 include_dirs=common_include_dirs,
902 depends=common_depends + ['mercurial/cext/charencode.h']),
903 depends=common_depends + ['mercurial/cext/charencode.h']),
903 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
904 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
904 include_dirs=common_include_dirs,
905 include_dirs=common_include_dirs,
905 extra_compile_args=osutil_cflags,
906 extra_compile_args=osutil_cflags,
906 extra_link_args=osutil_ldflags,
907 extra_link_args=osutil_ldflags,
907 depends=common_depends),
908 depends=common_depends),
908 Extension(
909 Extension(
909 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations', [
910 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations', [
910 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
911 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
911 ]),
912 ]),
912 Extension('hgext.fsmonitor.pywatchman.bser',
913 Extension('hgext.fsmonitor.pywatchman.bser',
913 ['hgext/fsmonitor/pywatchman/bser.c']),
914 ['hgext/fsmonitor/pywatchman/bser.c']),
914 ]
915 ]
915
916
916 sys.path.insert(0, 'contrib/python-zstandard')
917 sys.path.insert(0, 'contrib/python-zstandard')
917 import setup_zstd
918 import setup_zstd
918 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
919 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
919
920
920 try:
921 try:
921 from distutils import cygwinccompiler
922 from distutils import cygwinccompiler
922
923
923 # the -mno-cygwin option has been deprecated for years
924 # the -mno-cygwin option has been deprecated for years
924 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
925 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
925
926
926 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
927 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
927 def __init__(self, *args, **kwargs):
928 def __init__(self, *args, **kwargs):
928 mingw32compilerclass.__init__(self, *args, **kwargs)
929 mingw32compilerclass.__init__(self, *args, **kwargs)
929 for i in 'compiler compiler_so linker_exe linker_so'.split():
930 for i in 'compiler compiler_so linker_exe linker_so'.split():
930 try:
931 try:
931 getattr(self, i).remove('-mno-cygwin')
932 getattr(self, i).remove('-mno-cygwin')
932 except ValueError:
933 except ValueError:
933 pass
934 pass
934
935
935 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
936 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
936 except ImportError:
937 except ImportError:
937 # the cygwinccompiler package is not available on some Python
938 # the cygwinccompiler package is not available on some Python
938 # distributions like the ones from the optware project for Synology
939 # distributions like the ones from the optware project for Synology
939 # DiskStation boxes
940 # DiskStation boxes
940 class HackedMingw32CCompiler(object):
941 class HackedMingw32CCompiler(object):
941 pass
942 pass
942
943
943 if os.name == 'nt':
944 if os.name == 'nt':
944 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
945 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
945 # extra_link_args to distutils.extensions.Extension() doesn't have any
946 # extra_link_args to distutils.extensions.Extension() doesn't have any
946 # effect.
947 # effect.
947 from distutils import msvccompiler
948 from distutils import msvccompiler
948
949
949 msvccompilerclass = msvccompiler.MSVCCompiler
950 msvccompilerclass = msvccompiler.MSVCCompiler
950
951
951 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
952 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
952 def initialize(self):
953 def initialize(self):
953 msvccompilerclass.initialize(self)
954 msvccompilerclass.initialize(self)
954 # "warning LNK4197: export 'func' specified multiple times"
955 # "warning LNK4197: export 'func' specified multiple times"
955 self.ldflags_shared.append('/ignore:4197')
956 self.ldflags_shared.append('/ignore:4197')
956 self.ldflags_shared_debug.append('/ignore:4197')
957 self.ldflags_shared_debug.append('/ignore:4197')
957
958
958 msvccompiler.MSVCCompiler = HackedMSVCCompiler
959 msvccompiler.MSVCCompiler = HackedMSVCCompiler
959
960
960 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
961 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
961 'help/*.txt',
962 'help/*.txt',
962 'help/internals/*.txt',
963 'help/internals/*.txt',
963 'default.d/*.rc',
964 'default.d/*.rc',
964 'dummycert.pem']}
965 'dummycert.pem']}
965
966
966 def ordinarypath(p):
967 def ordinarypath(p):
967 return p and p[0] != '.' and p[-1] != '~'
968 return p and p[0] != '.' and p[-1] != '~'
968
969
969 for root in ('templates',):
970 for root in ('templates',):
970 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
971 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
971 curdir = curdir.split(os.sep, 1)[1]
972 curdir = curdir.split(os.sep, 1)[1]
972 dirs[:] = filter(ordinarypath, dirs)
973 dirs[:] = filter(ordinarypath, dirs)
973 for f in filter(ordinarypath, files):
974 for f in filter(ordinarypath, files):
974 f = os.path.join(curdir, f)
975 f = os.path.join(curdir, f)
975 packagedata['mercurial'].append(f)
976 packagedata['mercurial'].append(f)
976
977
977 datafiles = []
978 datafiles = []
978
979
979 # distutils expects version to be str/unicode. Converting it to
980 # distutils expects version to be str/unicode. Converting it to
980 # unicode on Python 2 still works because it won't contain any
981 # unicode on Python 2 still works because it won't contain any
981 # non-ascii bytes and will be implicitly converted back to bytes
982 # non-ascii bytes and will be implicitly converted back to bytes
982 # when operated on.
983 # when operated on.
983 assert isinstance(version, bytes)
984 assert isinstance(version, bytes)
984 setupversion = version.decode('ascii')
985 setupversion = version.decode('ascii')
985
986
986 extra = {}
987 extra = {}
987
988
988 if issetuptools:
989 if issetuptools:
989 extra['python_requires'] = supportedpy
990 extra['python_requires'] = supportedpy
990 if py2exeloaded:
991 if py2exeloaded:
991 extra['console'] = [
992 extra['console'] = [
992 {'script':'hg',
993 {'script':'hg',
993 'copyright':'Copyright (C) 2005-2018 Matt Mackall and others',
994 'copyright':'Copyright (C) 2005-2018 Matt Mackall and others',
994 'product_version':version}]
995 'product_version':version}]
995 # sub command of 'build' because 'py2exe' does not handle sub_commands
996 # sub command of 'build' because 'py2exe' does not handle sub_commands
996 build.sub_commands.insert(0, ('build_hgextindex', None))
997 build.sub_commands.insert(0, ('build_hgextindex', None))
997 # put dlls in sub directory so that they won't pollute PATH
998 # put dlls in sub directory so that they won't pollute PATH
998 extra['zipfile'] = 'lib/library.zip'
999 extra['zipfile'] = 'lib/library.zip'
999
1000
1000 if os.name == 'nt':
1001 if os.name == 'nt':
1001 # Windows binary file versions for exe/dll files must have the
1002 # Windows binary file versions for exe/dll files must have the
1002 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1003 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1003 setupversion = version.split(b'+', 1)[0]
1004 setupversion = version.split(b'+', 1)[0]
1004
1005
1005 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1006 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1006 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1007 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1007 if version:
1008 if version:
1008 version = version[0]
1009 version = version[0]
1009 if sys.version_info[0] == 3:
1010 if sys.version_info[0] == 3:
1010 version = version.decode('utf-8')
1011 version = version.decode('utf-8')
1011 xcode4 = (version.startswith('Xcode') and
1012 xcode4 = (version.startswith('Xcode') and
1012 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
1013 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
1013 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1014 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1014 else:
1015 else:
1015 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1016 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1016 # installed, but instead with only command-line tools. Assume
1017 # installed, but instead with only command-line tools. Assume
1017 # that only happens on >= Lion, thus no PPC support.
1018 # that only happens on >= Lion, thus no PPC support.
1018 xcode4 = True
1019 xcode4 = True
1019 xcode51 = False
1020 xcode51 = False
1020
1021
1021 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1022 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1022 # distutils.sysconfig
1023 # distutils.sysconfig
1023 if xcode4:
1024 if xcode4:
1024 os.environ['ARCHFLAGS'] = ''
1025 os.environ['ARCHFLAGS'] = ''
1025
1026
1026 # XCode 5.1 changes clang such that it now fails to compile if the
1027 # XCode 5.1 changes clang such that it now fails to compile if the
1027 # -mno-fused-madd flag is passed, but the version of Python shipped with
1028 # -mno-fused-madd flag is passed, but the version of Python shipped with
1028 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1029 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1029 # C extension modules, and a bug has been filed upstream at
1030 # C extension modules, and a bug has been filed upstream at
1030 # http://bugs.python.org/issue21244. We also need to patch this here
1031 # http://bugs.python.org/issue21244. We also need to patch this here
1031 # so Mercurial can continue to compile in the meantime.
1032 # so Mercurial can continue to compile in the meantime.
1032 if xcode51:
1033 if xcode51:
1033 cflags = get_config_var('CFLAGS')
1034 cflags = get_config_var('CFLAGS')
1034 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1035 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1035 os.environ['CFLAGS'] = (
1036 os.environ['CFLAGS'] = (
1036 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
1037 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
1037
1038
1038 setup(name='mercurial',
1039 setup(name='mercurial',
1039 version=setupversion,
1040 version=setupversion,
1040 author='Matt Mackall and many others',
1041 author='Matt Mackall and many others',
1041 author_email='mercurial@mercurial-scm.org',
1042 author_email='mercurial@mercurial-scm.org',
1042 url='https://mercurial-scm.org/',
1043 url='https://mercurial-scm.org/',
1043 download_url='https://mercurial-scm.org/release/',
1044 download_url='https://mercurial-scm.org/release/',
1044 description=('Fast scalable distributed SCM (revision control, version '
1045 description=('Fast scalable distributed SCM (revision control, version '
1045 'control) system'),
1046 'control) system'),
1046 long_description=('Mercurial is a distributed SCM tool written in Python.'
1047 long_description=('Mercurial is a distributed SCM tool written in Python.'
1047 ' It is used by a number of large projects that require'
1048 ' It is used by a number of large projects that require'
1048 ' fast, reliable distributed revision control, such as '
1049 ' fast, reliable distributed revision control, such as '
1049 'Mozilla.'),
1050 'Mozilla.'),
1050 license='GNU GPLv2 or any later version',
1051 license='GNU GPLv2 or any later version',
1051 classifiers=[
1052 classifiers=[
1052 'Development Status :: 6 - Mature',
1053 'Development Status :: 6 - Mature',
1053 'Environment :: Console',
1054 'Environment :: Console',
1054 'Intended Audience :: Developers',
1055 'Intended Audience :: Developers',
1055 'Intended Audience :: System Administrators',
1056 'Intended Audience :: System Administrators',
1056 'License :: OSI Approved :: GNU General Public License (GPL)',
1057 'License :: OSI Approved :: GNU General Public License (GPL)',
1057 'Natural Language :: Danish',
1058 'Natural Language :: Danish',
1058 'Natural Language :: English',
1059 'Natural Language :: English',
1059 'Natural Language :: German',
1060 'Natural Language :: German',
1060 'Natural Language :: Italian',
1061 'Natural Language :: Italian',
1061 'Natural Language :: Japanese',
1062 'Natural Language :: Japanese',
1062 'Natural Language :: Portuguese (Brazilian)',
1063 'Natural Language :: Portuguese (Brazilian)',
1063 'Operating System :: Microsoft :: Windows',
1064 'Operating System :: Microsoft :: Windows',
1064 'Operating System :: OS Independent',
1065 'Operating System :: OS Independent',
1065 'Operating System :: POSIX',
1066 'Operating System :: POSIX',
1066 'Programming Language :: C',
1067 'Programming Language :: C',
1067 'Programming Language :: Python',
1068 'Programming Language :: Python',
1068 'Topic :: Software Development :: Version Control',
1069 'Topic :: Software Development :: Version Control',
1069 ],
1070 ],
1070 scripts=scripts,
1071 scripts=scripts,
1071 packages=packages,
1072 packages=packages,
1072 ext_modules=extmodules,
1073 ext_modules=extmodules,
1073 data_files=datafiles,
1074 data_files=datafiles,
1074 package_data=packagedata,
1075 package_data=packagedata,
1075 cmdclass=cmdclass,
1076 cmdclass=cmdclass,
1076 distclass=hgdist,
1077 distclass=hgdist,
1077 options={
1078 options={
1078 'py2exe': {
1079 'py2exe': {
1079 'packages': [
1080 'packages': [
1080 'hgdemandimport',
1081 'hgdemandimport',
1081 'hgext',
1082 'hgext',
1082 'email',
1083 'email',
1083 # implicitly imported per module policy
1084 # implicitly imported per module policy
1084 # (cffi wouldn't be used as a frozen exe)
1085 # (cffi wouldn't be used as a frozen exe)
1085 'mercurial.cext',
1086 'mercurial.cext',
1086 #'mercurial.cffi',
1087 #'mercurial.cffi',
1087 'mercurial.pure',
1088 'mercurial.pure',
1088 ],
1089 ],
1089 },
1090 },
1090 'bdist_mpkg': {
1091 'bdist_mpkg': {
1091 'zipdist': False,
1092 'zipdist': False,
1092 'license': 'COPYING',
1093 'license': 'COPYING',
1093 'readme': 'contrib/packaging/macosx/Readme.html',
1094 'readme': 'contrib/packaging/macosx/Readme.html',
1094 'welcome': 'contrib/packaging/macosx/Welcome.html',
1095 'welcome': 'contrib/packaging/macosx/Welcome.html',
1095 },
1096 },
1096 },
1097 },
1097 **extra)
1098 **extra)
General Comments 0
You need to be logged in to leave comments. Login now