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