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