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