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