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