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