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