##// END OF EJS Templates
windows: add an experimental option for long paths support...
Kostia Balytskyi -
r34531:ed5acd3f default
parent child Browse files
Show More
@@ -1,955 +1,1010 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
6
7 import os
7 import os
8
8
9 supportedpy = '~= 2.7'
9 supportedpy = '~= 2.7'
10 if os.environ.get('HGALLOWPYTHON3', ''):
10 if os.environ.get('HGALLOWPYTHON3', ''):
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 # due to a bug in % formatting in bytestrings.
13 # due to a bug in % formatting in bytestrings.
14 #
14 #
15 # TODO: when we actually work on Python 3, use this string as the
15 # TODO: when we actually work on Python 3, use this string as the
16 # actual supportedpy string.
16 # actual supportedpy string.
17 supportedpy = ','.join([
17 supportedpy = ','.join([
18 '>=2.7',
18 '>=2.7',
19 '!=3.0.*',
19 '!=3.0.*',
20 '!=3.1.*',
20 '!=3.1.*',
21 '!=3.2.*',
21 '!=3.2.*',
22 '!=3.3.*',
22 '!=3.3.*',
23 '!=3.4.*',
23 '!=3.4.*',
24 '!=3.6.0',
24 '!=3.6.0',
25 '!=3.6.1',
25 '!=3.6.1',
26 ])
26 ])
27
27
28 import sys, platform
28 import sys, platform
29 if sys.version_info[0] >= 3:
29 if sys.version_info[0] >= 3:
30 printf = eval('print')
30 printf = eval('print')
31 libdir_escape = 'unicode_escape'
31 libdir_escape = 'unicode_escape'
32 else:
32 else:
33 libdir_escape = 'string_escape'
33 libdir_escape = 'string_escape'
34 def printf(*args, **kwargs):
34 def printf(*args, **kwargs):
35 f = kwargs.get('file', sys.stdout)
35 f = kwargs.get('file', sys.stdout)
36 end = kwargs.get('end', '\n')
36 end = kwargs.get('end', '\n')
37 f.write(b' '.join(args) + end)
37 f.write(b' '.join(args) + end)
38
38
39 # Attempt to guide users to a modern pip - this means that 2.6 users
39 # Attempt to guide users to a modern pip - this means that 2.6 users
40 # should have a chance of getting a 4.2 release, and when we ratchet
40 # should have a chance of getting a 4.2 release, and when we ratchet
41 # the version requirement forward again hopefully everyone will get
41 # the version requirement forward again hopefully everyone will get
42 # something that works for them.
42 # something that works for them.
43 if sys.version_info < (2, 7, 0, 'final'):
43 if sys.version_info < (2, 7, 0, 'final'):
44 pip_message = ('This may be due to an out of date pip. '
44 pip_message = ('This may be due to an out of date pip. '
45 'Make sure you have pip >= 9.0.1.')
45 'Make sure you have pip >= 9.0.1.')
46 try:
46 try:
47 import pip
47 import pip
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
49 if pip_version < (9, 0, 1) :
49 if pip_version < (9, 0, 1) :
50 pip_message = (
50 pip_message = (
51 'Your pip version is out of date, please install '
51 'Your pip version is out of date, please install '
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
53 else:
53 else:
54 # pip is new enough - it must be something else
54 # pip is new enough - it must be something else
55 pip_message = ''
55 pip_message = ''
56 except Exception:
56 except Exception:
57 pass
57 pass
58 error = """
58 error = """
59 Mercurial does not support Python older than 2.7.
59 Mercurial does not support Python older than 2.7.
60 Python {py} detected.
60 Python {py} detected.
61 {pip}
61 {pip}
62 """.format(py=sys.version_info, pip=pip_message)
62 """.format(py=sys.version_info, pip=pip_message)
63 printf(error, file=sys.stderr)
63 printf(error, file=sys.stderr)
64 sys.exit(1)
64 sys.exit(1)
65
65
66 # Solaris Python packaging brain damage
66 # Solaris Python packaging brain damage
67 try:
67 try:
68 import hashlib
68 import hashlib
69 sha = hashlib.sha1()
69 sha = hashlib.sha1()
70 except ImportError:
70 except ImportError:
71 try:
71 try:
72 import sha
72 import sha
73 sha.sha # silence unused import warning
73 sha.sha # silence unused import warning
74 except ImportError:
74 except ImportError:
75 raise SystemExit(
75 raise SystemExit(
76 "Couldn't import standard hashlib (incomplete Python install).")
76 "Couldn't import standard hashlib (incomplete Python install).")
77
77
78 try:
78 try:
79 import zlib
79 import zlib
80 zlib.compressobj # silence unused import warning
80 zlib.compressobj # silence unused import warning
81 except ImportError:
81 except ImportError:
82 raise SystemExit(
82 raise SystemExit(
83 "Couldn't import standard zlib (incomplete Python install).")
83 "Couldn't import standard zlib (incomplete Python install).")
84
84
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
86 isironpython = False
86 isironpython = False
87 try:
87 try:
88 isironpython = (platform.python_implementation()
88 isironpython = (platform.python_implementation()
89 .lower().find("ironpython") != -1)
89 .lower().find("ironpython") != -1)
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 bz2.BZ2Compressor # silence unused import warning
98 bz2.BZ2Compressor # silence unused import warning
99 except ImportError:
99 except ImportError:
100 raise SystemExit(
100 raise SystemExit(
101 "Couldn't import standard bz2 (incomplete Python install).")
101 "Couldn't import standard bz2 (incomplete Python install).")
102
102
103 ispypy = "PyPy" in sys.version
103 ispypy = "PyPy" in sys.version
104
104
105 import ctypes
105 import ctypes
106 import stat, subprocess, time
106 import stat, subprocess, time
107 import re
107 import re
108 import shutil
108 import shutil
109 import tempfile
109 import tempfile
110 from distutils import log
110 from distutils import log
111 # We have issues with setuptools on some platforms and builders. Until
111 # We have issues with setuptools on some platforms and builders. Until
112 # those are resolved, setuptools is opt-in except for platforms where
112 # those are resolved, setuptools is opt-in except for platforms where
113 # we don't have issues.
113 # we don't have issues.
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
115 if issetuptools:
115 if issetuptools:
116 from setuptools import setup
116 from setuptools import setup
117 else:
117 else:
118 from distutils.core import setup
118 from distutils.core import setup
119 from distutils.ccompiler import new_compiler
119 from distutils.ccompiler import new_compiler
120 from distutils.core import Command, Extension
120 from distutils.core import Command, Extension
121 from distutils.dist import Distribution
121 from distutils.dist import Distribution
122 from distutils.command.build import build
122 from distutils.command.build import build
123 from distutils.command.build_ext import build_ext
123 from distutils.command.build_ext import build_ext
124 from distutils.command.build_py import build_py
124 from distutils.command.build_py import build_py
125 from distutils.command.build_scripts import build_scripts
125 from distutils.command.build_scripts import build_scripts
126 from distutils.command.install import install
126 from distutils.command.install import install
127 from distutils.command.install_lib import install_lib
127 from distutils.command.install_lib import install_lib
128 from distutils.command.install_scripts import install_scripts
128 from distutils.command.install_scripts import install_scripts
129 from distutils.spawn import spawn, find_executable
129 from distutils.spawn import spawn, find_executable
130 from distutils import file_util
130 from distutils import file_util
131 from distutils.errors import (
131 from distutils.errors import (
132 CCompilerError,
132 CCompilerError,
133 DistutilsError,
133 DistutilsError,
134 DistutilsExecError,
134 DistutilsExecError,
135 )
135 )
136 from distutils.sysconfig import get_python_inc, get_config_var
136 from distutils.sysconfig import get_python_inc, get_config_var
137 from distutils.version import StrictVersion
137 from distutils.version import StrictVersion
138
138
139 scripts = ['hg']
139 scripts = ['hg']
140 if os.name == 'nt':
140 if os.name == 'nt':
141 # We remove hg.bat if we are able to build hg.exe.
141 # We remove hg.bat if we are able to build hg.exe.
142 scripts.append('contrib/win32/hg.bat')
142 scripts.append('contrib/win32/hg.bat')
143
143
144 def cancompile(cc, code):
144 def cancompile(cc, code):
145 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
145 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
146 devnull = oldstderr = None
146 devnull = oldstderr = None
147 try:
147 try:
148 fname = os.path.join(tmpdir, 'testcomp.c')
148 fname = os.path.join(tmpdir, 'testcomp.c')
149 f = open(fname, 'w')
149 f = open(fname, 'w')
150 f.write(code)
150 f.write(code)
151 f.close()
151 f.close()
152 # Redirect stderr to /dev/null to hide any error messages
152 # Redirect stderr to /dev/null to hide any error messages
153 # from the compiler.
153 # from the compiler.
154 # This will have to be changed if we ever have to check
154 # This will have to be changed if we ever have to check
155 # for a function on Windows.
155 # for a function on Windows.
156 devnull = open('/dev/null', 'w')
156 devnull = open('/dev/null', 'w')
157 oldstderr = os.dup(sys.stderr.fileno())
157 oldstderr = os.dup(sys.stderr.fileno())
158 os.dup2(devnull.fileno(), sys.stderr.fileno())
158 os.dup2(devnull.fileno(), sys.stderr.fileno())
159 objects = cc.compile([fname], output_dir=tmpdir)
159 objects = cc.compile([fname], output_dir=tmpdir)
160 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
160 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
161 return True
161 return True
162 except Exception:
162 except Exception:
163 return False
163 return False
164 finally:
164 finally:
165 if oldstderr is not None:
165 if oldstderr is not None:
166 os.dup2(oldstderr, sys.stderr.fileno())
166 os.dup2(oldstderr, sys.stderr.fileno())
167 if devnull is not None:
167 if devnull is not None:
168 devnull.close()
168 devnull.close()
169 shutil.rmtree(tmpdir)
169 shutil.rmtree(tmpdir)
170
170
171 # simplified version of distutils.ccompiler.CCompiler.has_function
171 # simplified version of distutils.ccompiler.CCompiler.has_function
172 # that actually removes its temporary files.
172 # that actually removes its temporary files.
173 def hasfunction(cc, funcname):
173 def hasfunction(cc, funcname):
174 code = 'int main(void) { %s(); }\n' % funcname
174 code = 'int main(void) { %s(); }\n' % funcname
175 return cancompile(cc, code)
175 return cancompile(cc, code)
176
176
177 def hasheader(cc, headername):
177 def hasheader(cc, headername):
178 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
178 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
179 return cancompile(cc, code)
179 return cancompile(cc, code)
180
180
181 # py2exe needs to be installed to work
181 # py2exe needs to be installed to work
182 try:
182 try:
183 import py2exe
183 import py2exe
184 py2exe.Distribution # silence unused import warning
184 py2exe.Distribution # silence unused import warning
185 py2exeloaded = True
185 py2exeloaded = True
186 # import py2exe's patched Distribution class
186 # import py2exe's patched Distribution class
187 from distutils.core import Distribution
187 from distutils.core import Distribution
188 except ImportError:
188 except ImportError:
189 py2exeloaded = False
189 py2exeloaded = False
190
190
191 def runcmd(cmd, env):
191 def runcmd(cmd, env):
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
193 stderr=subprocess.PIPE, env=env)
193 stderr=subprocess.PIPE, env=env)
194 out, err = p.communicate()
194 out, err = p.communicate()
195 return p.returncode, out, err
195 return p.returncode, out, err
196
196
197 class hgcommand(object):
197 class hgcommand(object):
198 def __init__(self, cmd, env):
198 def __init__(self, cmd, env):
199 self.cmd = cmd
199 self.cmd = cmd
200 self.env = env
200 self.env = env
201
201
202 def run(self, args):
202 def run(self, args):
203 cmd = self.cmd + args
203 cmd = self.cmd + args
204 returncode, out, err = runcmd(cmd, self.env)
204 returncode, out, err = runcmd(cmd, self.env)
205 err = filterhgerr(err)
205 err = filterhgerr(err)
206 if err or returncode != 0:
206 if err or returncode != 0:
207 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
207 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
208 printf(err, file=sys.stderr)
208 printf(err, file=sys.stderr)
209 return ''
209 return ''
210 return out
210 return out
211
211
212 def filterhgerr(err):
212 def filterhgerr(err):
213 # If root is executing setup.py, but the repository is owned by
213 # If root is executing setup.py, but the repository is owned by
214 # another user (as in "sudo python setup.py install") we will get
214 # another user (as in "sudo python setup.py install") we will get
215 # trust warnings since the .hg/hgrc file is untrusted. That is
215 # trust warnings since the .hg/hgrc file is untrusted. That is
216 # fine, we don't want to load it anyway. Python may warn about
216 # fine, we don't want to load it anyway. Python may warn about
217 # a missing __init__.py in mercurial/locale, we also ignore that.
217 # a missing __init__.py in mercurial/locale, we also ignore that.
218 err = [e for e in err.splitlines()
218 err = [e for e in err.splitlines()
219 if (not e.startswith(b'not trusting file')
219 if (not e.startswith(b'not trusting file')
220 and not e.startswith(b'warning: Not importing')
220 and not e.startswith(b'warning: Not importing')
221 and not e.startswith(b'obsolete feature not enabled'))]
221 and not e.startswith(b'obsolete feature not enabled'))]
222 return b'\n'.join(b' ' + e for e in err)
222 return b'\n'.join(b' ' + e for e in err)
223
223
224 def findhg():
224 def findhg():
225 """Try to figure out how we should invoke hg for examining the local
225 """Try to figure out how we should invoke hg for examining the local
226 repository contents.
226 repository contents.
227
227
228 Returns an hgcommand object."""
228 Returns an hgcommand object."""
229 # By default, prefer the "hg" command in the user's path. This was
229 # By default, prefer the "hg" command in the user's path. This was
230 # presumably the hg command that the user used to create this repository.
230 # presumably the hg command that the user used to create this repository.
231 #
231 #
232 # This repository may require extensions or other settings that would not
232 # This repository may require extensions or other settings that would not
233 # be enabled by running the hg script directly from this local repository.
233 # be enabled by running the hg script directly from this local repository.
234 hgenv = os.environ.copy()
234 hgenv = os.environ.copy()
235 # Use HGPLAIN to disable hgrc settings that would change output formatting,
235 # Use HGPLAIN to disable hgrc settings that would change output formatting,
236 # and disable localization for the same reasons.
236 # and disable localization for the same reasons.
237 hgenv['HGPLAIN'] = '1'
237 hgenv['HGPLAIN'] = '1'
238 hgenv['LANGUAGE'] = 'C'
238 hgenv['LANGUAGE'] = 'C'
239 hgcmd = ['hg']
239 hgcmd = ['hg']
240 # Run a simple "hg log" command just to see if using hg from the user's
240 # Run a simple "hg log" command just to see if using hg from the user's
241 # path works and can successfully interact with this repository.
241 # path works and can successfully interact with this repository.
242 check_cmd = ['log', '-r.', '-Ttest']
242 check_cmd = ['log', '-r.', '-Ttest']
243 try:
243 try:
244 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
244 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
245 except EnvironmentError:
245 except EnvironmentError:
246 retcode = -1
246 retcode = -1
247 if retcode == 0 and not filterhgerr(err):
247 if retcode == 0 and not filterhgerr(err):
248 return hgcommand(hgcmd, hgenv)
248 return hgcommand(hgcmd, hgenv)
249
249
250 # Fall back to trying the local hg installation.
250 # Fall back to trying the local hg installation.
251 hgenv = localhgenv()
251 hgenv = localhgenv()
252 # Don't source any system hgrc files when using the local hg.
252 # Don't source any system hgrc files when using the local hg.
253 hgenv['HGRCPATH'] = ''
253 hgenv['HGRCPATH'] = ''
254 hgcmd = [sys.executable, 'hg']
254 hgcmd = [sys.executable, 'hg']
255 try:
255 try:
256 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
256 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
257 except EnvironmentError:
257 except EnvironmentError:
258 retcode = -1
258 retcode = -1
259 if retcode == 0 and not filterhgerr(err):
259 if retcode == 0 and not filterhgerr(err):
260 return hgcommand(hgcmd, hgenv)
260 return hgcommand(hgcmd, hgenv)
261
261
262 raise SystemExit('Unable to find a working hg binary to extract the '
262 raise SystemExit('Unable to find a working hg binary to extract the '
263 'version from the repository tags')
263 'version from the repository tags')
264
264
265 def localhgenv():
265 def localhgenv():
266 """Get an environment dictionary to use for invoking or importing
266 """Get an environment dictionary to use for invoking or importing
267 mercurial from the local repository."""
267 mercurial from the local repository."""
268 # Execute hg out of this directory with a custom environment which takes
268 # Execute hg out of this directory with a custom environment which takes
269 # care to not use any hgrc files and do no localization.
269 # care to not use any hgrc files and do no localization.
270 env = {'HGMODULEPOLICY': 'py',
270 env = {'HGMODULEPOLICY': 'py',
271 'HGRCPATH': '',
271 'HGRCPATH': '',
272 'LANGUAGE': 'C',
272 'LANGUAGE': 'C',
273 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
273 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
274 if 'LD_LIBRARY_PATH' in os.environ:
274 if 'LD_LIBRARY_PATH' in os.environ:
275 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
275 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
276 if 'SystemRoot' in os.environ:
276 if 'SystemRoot' in os.environ:
277 # SystemRoot is required by Windows to load various DLLs. See:
277 # SystemRoot is required by Windows to load various DLLs. See:
278 # https://bugs.python.org/issue13524#msg148850
278 # https://bugs.python.org/issue13524#msg148850
279 env['SystemRoot'] = os.environ['SystemRoot']
279 env['SystemRoot'] = os.environ['SystemRoot']
280 return env
280 return env
281
281
282 version = ''
282 version = ''
283
283
284 if os.path.isdir('.hg'):
284 if os.path.isdir('.hg'):
285 hg = findhg()
285 hg = findhg()
286 cmd = ['log', '-r', '.', '--template', '{tags}\n']
286 cmd = ['log', '-r', '.', '--template', '{tags}\n']
287 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
287 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
288 hgid = hg.run(['id', '-i']).strip()
288 hgid = hg.run(['id', '-i']).strip()
289 if not hgid:
289 if not hgid:
290 # Bail out if hg is having problems interacting with this repository,
290 # Bail out if hg is having problems interacting with this repository,
291 # rather than falling through and producing a bogus version number.
291 # rather than falling through and producing a bogus version number.
292 # Continuing with an invalid version number will break extensions
292 # Continuing with an invalid version number will break extensions
293 # that define minimumhgversion.
293 # that define minimumhgversion.
294 raise SystemExit('Unable to determine hg version from local repository')
294 raise SystemExit('Unable to determine hg version from local repository')
295 if numerictags: # tag(s) found
295 if numerictags: # tag(s) found
296 version = numerictags[-1]
296 version = numerictags[-1]
297 if hgid.endswith('+'): # propagate the dirty status to the tag
297 if hgid.endswith('+'): # propagate the dirty status to the tag
298 version += '+'
298 version += '+'
299 else: # no tag found
299 else: # no tag found
300 ltagcmd = ['parents', '--template', '{latesttag}']
300 ltagcmd = ['parents', '--template', '{latesttag}']
301 ltag = hg.run(ltagcmd)
301 ltag = hg.run(ltagcmd)
302 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
302 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
303 changessince = len(hg.run(changessincecmd).splitlines())
303 changessince = len(hg.run(changessincecmd).splitlines())
304 version = '%s+%s-%s' % (ltag, changessince, hgid)
304 version = '%s+%s-%s' % (ltag, changessince, hgid)
305 if version.endswith('+'):
305 if version.endswith('+'):
306 version += time.strftime('%Y%m%d')
306 version += time.strftime('%Y%m%d')
307 elif os.path.exists('.hg_archival.txt'):
307 elif os.path.exists('.hg_archival.txt'):
308 kw = dict([[t.strip() for t in l.split(':', 1)]
308 kw = dict([[t.strip() for t in l.split(':', 1)]
309 for l in open('.hg_archival.txt')])
309 for l in open('.hg_archival.txt')])
310 if 'tag' in kw:
310 if 'tag' in kw:
311 version = kw['tag']
311 version = kw['tag']
312 elif 'latesttag' in kw:
312 elif 'latesttag' in kw:
313 if 'changessincelatesttag' in kw:
313 if 'changessincelatesttag' in kw:
314 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
314 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
315 else:
315 else:
316 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
316 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
317 else:
317 else:
318 version = kw.get('node', '')[:12]
318 version = kw.get('node', '')[:12]
319
319
320 if version:
320 if version:
321 with open("mercurial/__version__.py", "w") as f:
321 with open("mercurial/__version__.py", "w") as f:
322 f.write('# this file is autogenerated by setup.py\n')
322 f.write('# this file is autogenerated by setup.py\n')
323 f.write('version = "%s"\n' % version)
323 f.write('version = "%s"\n' % version)
324
324
325 try:
325 try:
326 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
326 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
327 os.environ['HGMODULEPOLICY'] = 'py'
327 os.environ['HGMODULEPOLICY'] = 'py'
328 from mercurial import __version__
328 from mercurial import __version__
329 version = __version__.version
329 version = __version__.version
330 except ImportError:
330 except ImportError:
331 version = 'unknown'
331 version = 'unknown'
332 finally:
332 finally:
333 if oldpolicy is None:
333 if oldpolicy is None:
334 del os.environ['HGMODULEPOLICY']
334 del os.environ['HGMODULEPOLICY']
335 else:
335 else:
336 os.environ['HGMODULEPOLICY'] = oldpolicy
336 os.environ['HGMODULEPOLICY'] = oldpolicy
337
337
338 class hgbuild(build):
338 class hgbuild(build):
339 # Insert hgbuildmo first so that files in mercurial/locale/ are found
339 # Insert hgbuildmo first so that files in mercurial/locale/ are found
340 # when build_py is run next.
340 # when build_py is run next.
341 sub_commands = [('build_mo', None)] + build.sub_commands
341 sub_commands = [('build_mo', None)] + build.sub_commands
342
342
343 class hgbuildmo(build):
343 class hgbuildmo(build):
344
344
345 description = "build translations (.mo files)"
345 description = "build translations (.mo files)"
346
346
347 def run(self):
347 def run(self):
348 if not find_executable('msgfmt'):
348 if not find_executable('msgfmt'):
349 self.warn("could not find msgfmt executable, no translations "
349 self.warn("could not find msgfmt executable, no translations "
350 "will be built")
350 "will be built")
351 return
351 return
352
352
353 podir = 'i18n'
353 podir = 'i18n'
354 if not os.path.isdir(podir):
354 if not os.path.isdir(podir):
355 self.warn("could not find %s/ directory" % podir)
355 self.warn("could not find %s/ directory" % podir)
356 return
356 return
357
357
358 join = os.path.join
358 join = os.path.join
359 for po in os.listdir(podir):
359 for po in os.listdir(podir):
360 if not po.endswith('.po'):
360 if not po.endswith('.po'):
361 continue
361 continue
362 pofile = join(podir, po)
362 pofile = join(podir, po)
363 modir = join('locale', po[:-3], 'LC_MESSAGES')
363 modir = join('locale', po[:-3], 'LC_MESSAGES')
364 mofile = join(modir, 'hg.mo')
364 mofile = join(modir, 'hg.mo')
365 mobuildfile = join('mercurial', mofile)
365 mobuildfile = join('mercurial', mofile)
366 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
366 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
367 if sys.platform != 'sunos5':
367 if sys.platform != 'sunos5':
368 # msgfmt on Solaris does not know about -c
368 # msgfmt on Solaris does not know about -c
369 cmd.append('-c')
369 cmd.append('-c')
370 self.mkpath(join('mercurial', modir))
370 self.mkpath(join('mercurial', modir))
371 self.make_file([pofile], mobuildfile, spawn, (cmd,))
371 self.make_file([pofile], mobuildfile, spawn, (cmd,))
372
372
373
373
374 class hgdist(Distribution):
374 class hgdist(Distribution):
375 pure = False
375 pure = False
376 cffi = ispypy
376 cffi = ispypy
377
377
378 global_options = Distribution.global_options + \
378 global_options = Distribution.global_options + \
379 [('pure', None, "use pure (slow) Python "
379 [('pure', None, "use pure (slow) Python "
380 "code instead of C extensions"),
380 "code instead of C extensions"),
381 ]
381 ]
382
382
383 def has_ext_modules(self):
383 def has_ext_modules(self):
384 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
384 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
385 # too late for some cases
385 # too late for some cases
386 return not self.pure and Distribution.has_ext_modules(self)
386 return not self.pure and Distribution.has_ext_modules(self)
387
387
388 # This is ugly as a one-liner. So use a variable.
388 # This is ugly as a one-liner. So use a variable.
389 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
389 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
390 buildextnegops['no-zstd'] = 'zstd'
390 buildextnegops['no-zstd'] = 'zstd'
391
391
392 class hgbuildext(build_ext):
392 class hgbuildext(build_ext):
393 user_options = build_ext.user_options + [
393 user_options = build_ext.user_options + [
394 ('zstd', None, 'compile zstd bindings [default]'),
394 ('zstd', None, 'compile zstd bindings [default]'),
395 ('no-zstd', None, 'do not compile zstd bindings'),
395 ('no-zstd', None, 'do not compile zstd bindings'),
396 ]
396 ]
397
397
398 boolean_options = build_ext.boolean_options + ['zstd']
398 boolean_options = build_ext.boolean_options + ['zstd']
399 negative_opt = buildextnegops
399 negative_opt = buildextnegops
400
400
401 def initialize_options(self):
401 def initialize_options(self):
402 self.zstd = True
402 self.zstd = True
403 return build_ext.initialize_options(self)
403 return build_ext.initialize_options(self)
404
404
405 def build_extensions(self):
405 def build_extensions(self):
406 # Filter out zstd if disabled via argument.
406 # Filter out zstd if disabled via argument.
407 if not self.zstd:
407 if not self.zstd:
408 self.extensions = [e for e in self.extensions
408 self.extensions = [e for e in self.extensions
409 if e.name != 'mercurial.zstd']
409 if e.name != 'mercurial.zstd']
410
410
411 return build_ext.build_extensions(self)
411 return build_ext.build_extensions(self)
412
412
413 def build_extension(self, ext):
413 def build_extension(self, ext):
414 try:
414 try:
415 build_ext.build_extension(self, ext)
415 build_ext.build_extension(self, ext)
416 except CCompilerError:
416 except CCompilerError:
417 if not getattr(ext, 'optional', False):
417 if not getattr(ext, 'optional', False):
418 raise
418 raise
419 log.warn("Failed to build optional extension '%s' (skipping)",
419 log.warn("Failed to build optional extension '%s' (skipping)",
420 ext.name)
420 ext.name)
421
421
422 class hgbuildscripts(build_scripts):
422 class hgbuildscripts(build_scripts):
423 def run(self):
423 def run(self):
424 if os.name != 'nt' or self.distribution.pure:
424 if os.name != 'nt' or self.distribution.pure:
425 return build_scripts.run(self)
425 return build_scripts.run(self)
426
426
427 exebuilt = False
427 exebuilt = False
428 try:
428 try:
429 self.run_command('build_hgexe')
429 self.run_command('build_hgexe')
430 exebuilt = True
430 exebuilt = True
431 except (DistutilsError, CCompilerError):
431 except (DistutilsError, CCompilerError):
432 log.warn('failed to build optional hg.exe')
432 log.warn('failed to build optional hg.exe')
433
433
434 if exebuilt:
434 if exebuilt:
435 # Copying hg.exe to the scripts build directory ensures it is
435 # Copying hg.exe to the scripts build directory ensures it is
436 # installed by the install_scripts command.
436 # installed by the install_scripts command.
437 hgexecommand = self.get_finalized_command('build_hgexe')
437 hgexecommand = self.get_finalized_command('build_hgexe')
438 dest = os.path.join(self.build_dir, 'hg.exe')
438 dest = os.path.join(self.build_dir, 'hg.exe')
439 self.mkpath(self.build_dir)
439 self.mkpath(self.build_dir)
440 self.copy_file(hgexecommand.hgexepath, dest)
440 self.copy_file(hgexecommand.hgexepath, dest)
441
441
442 # Remove hg.bat because it is redundant with hg.exe.
442 # Remove hg.bat because it is redundant with hg.exe.
443 self.scripts.remove('contrib/win32/hg.bat')
443 self.scripts.remove('contrib/win32/hg.bat')
444
444
445 return build_scripts.run(self)
445 return build_scripts.run(self)
446
446
447 class hgbuildpy(build_py):
447 class hgbuildpy(build_py):
448 def finalize_options(self):
448 def finalize_options(self):
449 build_py.finalize_options(self)
449 build_py.finalize_options(self)
450
450
451 if self.distribution.pure:
451 if self.distribution.pure:
452 self.distribution.ext_modules = []
452 self.distribution.ext_modules = []
453 elif self.distribution.cffi:
453 elif self.distribution.cffi:
454 from mercurial.cffi import (
454 from mercurial.cffi import (
455 bdiffbuild,
455 bdiffbuild,
456 mpatchbuild,
456 mpatchbuild,
457 )
457 )
458 exts = [mpatchbuild.ffi.distutils_extension(),
458 exts = [mpatchbuild.ffi.distutils_extension(),
459 bdiffbuild.ffi.distutils_extension()]
459 bdiffbuild.ffi.distutils_extension()]
460 # cffi modules go here
460 # cffi modules go here
461 if sys.platform == 'darwin':
461 if sys.platform == 'darwin':
462 from mercurial.cffi import osutilbuild
462 from mercurial.cffi import osutilbuild
463 exts.append(osutilbuild.ffi.distutils_extension())
463 exts.append(osutilbuild.ffi.distutils_extension())
464 self.distribution.ext_modules = exts
464 self.distribution.ext_modules = exts
465 else:
465 else:
466 h = os.path.join(get_python_inc(), 'Python.h')
466 h = os.path.join(get_python_inc(), 'Python.h')
467 if not os.path.exists(h):
467 if not os.path.exists(h):
468 raise SystemExit('Python headers are required to build '
468 raise SystemExit('Python headers are required to build '
469 'Mercurial but weren\'t found in %s' % h)
469 'Mercurial but weren\'t found in %s' % h)
470
470
471 def run(self):
471 def run(self):
472 basepath = os.path.join(self.build_lib, 'mercurial')
472 basepath = os.path.join(self.build_lib, 'mercurial')
473 self.mkpath(basepath)
473 self.mkpath(basepath)
474
474
475 if self.distribution.pure:
475 if self.distribution.pure:
476 modulepolicy = 'py'
476 modulepolicy = 'py'
477 elif self.build_lib == '.':
477 elif self.build_lib == '.':
478 # in-place build should run without rebuilding C extensions
478 # in-place build should run without rebuilding C extensions
479 modulepolicy = 'allow'
479 modulepolicy = 'allow'
480 else:
480 else:
481 modulepolicy = 'c'
481 modulepolicy = 'c'
482 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
482 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
483 f.write('# this file is autogenerated by setup.py\n')
483 f.write('# this file is autogenerated by setup.py\n')
484 f.write('modulepolicy = b"%s"\n' % modulepolicy)
484 f.write('modulepolicy = b"%s"\n' % modulepolicy)
485
485
486 build_py.run(self)
486 build_py.run(self)
487
487
488 class buildhgextindex(Command):
488 class buildhgextindex(Command):
489 description = 'generate prebuilt index of hgext (for frozen package)'
489 description = 'generate prebuilt index of hgext (for frozen package)'
490 user_options = []
490 user_options = []
491 _indexfilename = 'hgext/__index__.py'
491 _indexfilename = 'hgext/__index__.py'
492
492
493 def initialize_options(self):
493 def initialize_options(self):
494 pass
494 pass
495
495
496 def finalize_options(self):
496 def finalize_options(self):
497 pass
497 pass
498
498
499 def run(self):
499 def run(self):
500 if os.path.exists(self._indexfilename):
500 if os.path.exists(self._indexfilename):
501 with open(self._indexfilename, 'w') as f:
501 with open(self._indexfilename, 'w') as f:
502 f.write('# empty\n')
502 f.write('# empty\n')
503
503
504 # here no extension enabled, disabled() lists up everything
504 # here no extension enabled, disabled() lists up everything
505 code = ('import pprint; from mercurial import extensions; '
505 code = ('import pprint; from mercurial import extensions; '
506 'pprint.pprint(extensions.disabled())')
506 'pprint.pprint(extensions.disabled())')
507 returncode, out, err = runcmd([sys.executable, '-c', code],
507 returncode, out, err = runcmd([sys.executable, '-c', code],
508 localhgenv())
508 localhgenv())
509 if err or returncode != 0:
509 if err or returncode != 0:
510 raise DistutilsExecError(err)
510 raise DistutilsExecError(err)
511
511
512 with open(self._indexfilename, 'w') as f:
512 with open(self._indexfilename, 'w') as f:
513 f.write('# this file is autogenerated by setup.py\n')
513 f.write('# this file is autogenerated by setup.py\n')
514 f.write('docs = ')
514 f.write('docs = ')
515 f.write(out)
515 f.write(out)
516
516
517 class buildhgexe(build_ext):
517 class buildhgexe(build_ext):
518 description = 'compile hg.exe from mercurial/exewrapper.c'
518 description = 'compile hg.exe from mercurial/exewrapper.c'
519 user_options = build_ext.user_options + [
520 ('long-paths-support', None, 'enable support for long paths on '
521 'Windows (off by default and '
522 'experimental)'),
523 ]
524
525 LONG_PATHS_MANIFEST = """
526 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
527 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
528 <application>
529 <windowsSettings
530 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
531 <ws2:longPathAware>true</ws2:longPathAware>
532 </windowsSettings>
533 </application>
534 </assembly>"""
535
536 def initialize_options(self):
537 build_ext.initialize_options(self)
538 self.long_paths_support = False
519
539
520 def build_extensions(self):
540 def build_extensions(self):
521 if os.name != 'nt':
541 if os.name != 'nt':
522 return
542 return
523 if isinstance(self.compiler, HackedMingw32CCompiler):
543 if isinstance(self.compiler, HackedMingw32CCompiler):
524 self.compiler.compiler_so = self.compiler.compiler # no -mdll
544 self.compiler.compiler_so = self.compiler.compiler # no -mdll
525 self.compiler.dll_libraries = [] # no -lmsrvc90
545 self.compiler.dll_libraries = [] # no -lmsrvc90
526
546
527 # Different Python installs can have different Python library
547 # Different Python installs can have different Python library
528 # names. e.g. the official CPython distribution uses pythonXY.dll
548 # names. e.g. the official CPython distribution uses pythonXY.dll
529 # and MinGW uses libpythonX.Y.dll.
549 # and MinGW uses libpythonX.Y.dll.
530 _kernel32 = ctypes.windll.kernel32
550 _kernel32 = ctypes.windll.kernel32
531 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
551 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
532 ctypes.c_void_p,
552 ctypes.c_void_p,
533 ctypes.c_ulong]
553 ctypes.c_ulong]
534 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
554 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
535 size = 1000
555 size = 1000
536 buf = ctypes.create_string_buffer(size + 1)
556 buf = ctypes.create_string_buffer(size + 1)
537 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
557 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
538 size)
558 size)
539
559
540 if filelen > 0 and filelen != size:
560 if filelen > 0 and filelen != size:
541 dllbasename = os.path.basename(buf.value)
561 dllbasename = os.path.basename(buf.value)
542 if not dllbasename.lower().endswith('.dll'):
562 if not dllbasename.lower().endswith('.dll'):
543 raise SystemExit('Python DLL does not end with .dll: %s' %
563 raise SystemExit('Python DLL does not end with .dll: %s' %
544 dllbasename)
564 dllbasename)
545 pythonlib = dllbasename[:-4]
565 pythonlib = dllbasename[:-4]
546 else:
566 else:
547 log.warn('could not determine Python DLL filename; '
567 log.warn('could not determine Python DLL filename; '
548 'assuming pythonXY')
568 'assuming pythonXY')
549
569
550 hv = sys.hexversion
570 hv = sys.hexversion
551 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
571 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
552
572
553 log.info('using %s as Python library name' % pythonlib)
573 log.info('using %s as Python library name' % pythonlib)
554 with open('mercurial/hgpythonlib.h', 'wb') as f:
574 with open('mercurial/hgpythonlib.h', 'wb') as f:
555 f.write('/* this file is autogenerated by setup.py */\n')
575 f.write('/* this file is autogenerated by setup.py */\n')
556 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
576 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
557 objects = self.compiler.compile(['mercurial/exewrapper.c'],
577 objects = self.compiler.compile(['mercurial/exewrapper.c'],
558 output_dir=self.build_temp)
578 output_dir=self.build_temp)
559 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
579 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
560 target = os.path.join(dir, 'hg')
580 self.hgtarget = os.path.join(dir, 'hg')
561 self.compiler.link_executable(objects, target,
581 self.compiler.link_executable(objects, self.hgtarget,
562 libraries=[],
582 libraries=[],
563 output_dir=self.build_temp)
583 output_dir=self.build_temp)
584 if self.long_paths_support:
585 self.addlongpathsmanifest()
586
587 def addlongpathsmanifest(self):
588 """Add manifest pieces so that hg.exe understands long paths
589
590 This is an EXPERIMENTAL feature, use with care.
591 To enable long paths support, one needs to do two things:
592 - build Mercurial with --long-paths-support option
593 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
594 LongPathsEnabled to have value 1.
595
596 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
597 it happens because Mercurial uses mt.exe circa 2008, which is not
598 yet aware of long paths support in the manifest (I think so at least).
599 This does not stop mt.exe from embedding/merging the XML properly.
600
601 Why resource #1 should be used for .exe manifests? I don't know and
602 wasn't able to find an explanation for mortals. But it seems to work.
603 """
604 exefname = self.compiler.executable_filename(self.hgtarget)
605 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
606 os.close(fdauto)
607 with open(manfname, 'w') as f:
608 f.write(self.LONG_PATHS_MANIFEST)
609 log.info("long paths manifest is written to '%s'" % manfname)
610 inputresource = '-inputresource:%s;#1' % exefname
611 outputresource = '-outputresource:%s;#1' % exefname
612 log.info("running mt.exe to update hg.exe's manifest in-place")
613 # supplying both -manifest and -inputresource to mt.exe makes
614 # it merge the embedded and supplied manifests in the -outputresource
615 self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
616 inputresource, outputresource])
617 log.info("done updating hg.exe's manifest")
618 os.remove(manfname)
564
619
565 @property
620 @property
566 def hgexepath(self):
621 def hgexepath(self):
567 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
622 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
568 return os.path.join(self.build_temp, dir, 'hg.exe')
623 return os.path.join(self.build_temp, dir, 'hg.exe')
569
624
570 class hginstall(install):
625 class hginstall(install):
571
626
572 user_options = install.user_options + [
627 user_options = install.user_options + [
573 ('old-and-unmanageable', None,
628 ('old-and-unmanageable', None,
574 'noop, present for eggless setuptools compat'),
629 'noop, present for eggless setuptools compat'),
575 ('single-version-externally-managed', None,
630 ('single-version-externally-managed', None,
576 'noop, present for eggless setuptools compat'),
631 'noop, present for eggless setuptools compat'),
577 ]
632 ]
578
633
579 # Also helps setuptools not be sad while we refuse to create eggs.
634 # Also helps setuptools not be sad while we refuse to create eggs.
580 single_version_externally_managed = True
635 single_version_externally_managed = True
581
636
582 def get_sub_commands(self):
637 def get_sub_commands(self):
583 # Screen out egg related commands to prevent egg generation. But allow
638 # Screen out egg related commands to prevent egg generation. But allow
584 # mercurial.egg-info generation, since that is part of modern
639 # mercurial.egg-info generation, since that is part of modern
585 # packaging.
640 # packaging.
586 excl = set(['bdist_egg'])
641 excl = set(['bdist_egg'])
587 return filter(lambda x: x not in excl, install.get_sub_commands(self))
642 return filter(lambda x: x not in excl, install.get_sub_commands(self))
588
643
589 class hginstalllib(install_lib):
644 class hginstalllib(install_lib):
590 '''
645 '''
591 This is a specialization of install_lib that replaces the copy_file used
646 This is a specialization of install_lib that replaces the copy_file used
592 there so that it supports setting the mode of files after copying them,
647 there so that it supports setting the mode of files after copying them,
593 instead of just preserving the mode that the files originally had. If your
648 instead of just preserving the mode that the files originally had. If your
594 system has a umask of something like 027, preserving the permissions when
649 system has a umask of something like 027, preserving the permissions when
595 copying will lead to a broken install.
650 copying will lead to a broken install.
596
651
597 Note that just passing keep_permissions=False to copy_file would be
652 Note that just passing keep_permissions=False to copy_file would be
598 insufficient, as it might still be applying a umask.
653 insufficient, as it might still be applying a umask.
599 '''
654 '''
600
655
601 def run(self):
656 def run(self):
602 realcopyfile = file_util.copy_file
657 realcopyfile = file_util.copy_file
603 def copyfileandsetmode(*args, **kwargs):
658 def copyfileandsetmode(*args, **kwargs):
604 src, dst = args[0], args[1]
659 src, dst = args[0], args[1]
605 dst, copied = realcopyfile(*args, **kwargs)
660 dst, copied = realcopyfile(*args, **kwargs)
606 if copied:
661 if copied:
607 st = os.stat(src)
662 st = os.stat(src)
608 # Persist executable bit (apply it to group and other if user
663 # Persist executable bit (apply it to group and other if user
609 # has it)
664 # has it)
610 if st[stat.ST_MODE] & stat.S_IXUSR:
665 if st[stat.ST_MODE] & stat.S_IXUSR:
611 setmode = int('0755', 8)
666 setmode = int('0755', 8)
612 else:
667 else:
613 setmode = int('0644', 8)
668 setmode = int('0644', 8)
614 m = stat.S_IMODE(st[stat.ST_MODE])
669 m = stat.S_IMODE(st[stat.ST_MODE])
615 m = (m & ~int('0777', 8)) | setmode
670 m = (m & ~int('0777', 8)) | setmode
616 os.chmod(dst, m)
671 os.chmod(dst, m)
617 file_util.copy_file = copyfileandsetmode
672 file_util.copy_file = copyfileandsetmode
618 try:
673 try:
619 install_lib.run(self)
674 install_lib.run(self)
620 finally:
675 finally:
621 file_util.copy_file = realcopyfile
676 file_util.copy_file = realcopyfile
622
677
623 class hginstallscripts(install_scripts):
678 class hginstallscripts(install_scripts):
624 '''
679 '''
625 This is a specialization of install_scripts that replaces the @LIBDIR@ with
680 This is a specialization of install_scripts that replaces the @LIBDIR@ with
626 the configured directory for modules. If possible, the path is made relative
681 the configured directory for modules. If possible, the path is made relative
627 to the directory for scripts.
682 to the directory for scripts.
628 '''
683 '''
629
684
630 def initialize_options(self):
685 def initialize_options(self):
631 install_scripts.initialize_options(self)
686 install_scripts.initialize_options(self)
632
687
633 self.install_lib = None
688 self.install_lib = None
634
689
635 def finalize_options(self):
690 def finalize_options(self):
636 install_scripts.finalize_options(self)
691 install_scripts.finalize_options(self)
637 self.set_undefined_options('install',
692 self.set_undefined_options('install',
638 ('install_lib', 'install_lib'))
693 ('install_lib', 'install_lib'))
639
694
640 def run(self):
695 def run(self):
641 install_scripts.run(self)
696 install_scripts.run(self)
642
697
643 # It only makes sense to replace @LIBDIR@ with the install path if
698 # It only makes sense to replace @LIBDIR@ with the install path if
644 # the install path is known. For wheels, the logic below calculates
699 # the install path is known. For wheels, the logic below calculates
645 # the libdir to be "../..". This is because the internal layout of a
700 # the libdir to be "../..". This is because the internal layout of a
646 # wheel archive looks like:
701 # wheel archive looks like:
647 #
702 #
648 # mercurial-3.6.1.data/scripts/hg
703 # mercurial-3.6.1.data/scripts/hg
649 # mercurial/__init__.py
704 # mercurial/__init__.py
650 #
705 #
651 # When installing wheels, the subdirectories of the "<pkg>.data"
706 # When installing wheels, the subdirectories of the "<pkg>.data"
652 # directory are translated to system local paths and files therein
707 # directory are translated to system local paths and files therein
653 # are copied in place. The mercurial/* files are installed into the
708 # are copied in place. The mercurial/* files are installed into the
654 # site-packages directory. However, the site-packages directory
709 # site-packages directory. However, the site-packages directory
655 # isn't known until wheel install time. This means we have no clue
710 # isn't known until wheel install time. This means we have no clue
656 # at wheel generation time what the installed site-packages directory
711 # at wheel generation time what the installed site-packages directory
657 # will be. And, wheels don't appear to provide the ability to register
712 # will be. And, wheels don't appear to provide the ability to register
658 # custom code to run during wheel installation. This all means that
713 # custom code to run during wheel installation. This all means that
659 # we can't reliably set the libdir in wheels: the default behavior
714 # we can't reliably set the libdir in wheels: the default behavior
660 # of looking in sys.path must do.
715 # of looking in sys.path must do.
661
716
662 if (os.path.splitdrive(self.install_dir)[0] !=
717 if (os.path.splitdrive(self.install_dir)[0] !=
663 os.path.splitdrive(self.install_lib)[0]):
718 os.path.splitdrive(self.install_lib)[0]):
664 # can't make relative paths from one drive to another, so use an
719 # can't make relative paths from one drive to another, so use an
665 # absolute path instead
720 # absolute path instead
666 libdir = self.install_lib
721 libdir = self.install_lib
667 else:
722 else:
668 common = os.path.commonprefix((self.install_dir, self.install_lib))
723 common = os.path.commonprefix((self.install_dir, self.install_lib))
669 rest = self.install_dir[len(common):]
724 rest = self.install_dir[len(common):]
670 uplevel = len([n for n in os.path.split(rest) if n])
725 uplevel = len([n for n in os.path.split(rest) if n])
671
726
672 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
727 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
673
728
674 for outfile in self.outfiles:
729 for outfile in self.outfiles:
675 with open(outfile, 'rb') as fp:
730 with open(outfile, 'rb') as fp:
676 data = fp.read()
731 data = fp.read()
677
732
678 # skip binary files
733 # skip binary files
679 if b'\0' in data:
734 if b'\0' in data:
680 continue
735 continue
681
736
682 # During local installs, the shebang will be rewritten to the final
737 # During local installs, the shebang will be rewritten to the final
683 # install path. During wheel packaging, the shebang has a special
738 # install path. During wheel packaging, the shebang has a special
684 # value.
739 # value.
685 if data.startswith(b'#!python'):
740 if data.startswith(b'#!python'):
686 log.info('not rewriting @LIBDIR@ in %s because install path '
741 log.info('not rewriting @LIBDIR@ in %s because install path '
687 'not known' % outfile)
742 'not known' % outfile)
688 continue
743 continue
689
744
690 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
745 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
691 with open(outfile, 'wb') as fp:
746 with open(outfile, 'wb') as fp:
692 fp.write(data)
747 fp.write(data)
693
748
694 cmdclass = {'build': hgbuild,
749 cmdclass = {'build': hgbuild,
695 'build_mo': hgbuildmo,
750 'build_mo': hgbuildmo,
696 'build_ext': hgbuildext,
751 'build_ext': hgbuildext,
697 'build_py': hgbuildpy,
752 'build_py': hgbuildpy,
698 'build_scripts': hgbuildscripts,
753 'build_scripts': hgbuildscripts,
699 'build_hgextindex': buildhgextindex,
754 'build_hgextindex': buildhgextindex,
700 'install': hginstall,
755 'install': hginstall,
701 'install_lib': hginstalllib,
756 'install_lib': hginstalllib,
702 'install_scripts': hginstallscripts,
757 'install_scripts': hginstallscripts,
703 'build_hgexe': buildhgexe,
758 'build_hgexe': buildhgexe,
704 }
759 }
705
760
706 packages = ['mercurial',
761 packages = ['mercurial',
707 'mercurial.cext',
762 'mercurial.cext',
708 'mercurial.cffi',
763 'mercurial.cffi',
709 'mercurial.hgweb',
764 'mercurial.hgweb',
710 'mercurial.httpclient',
765 'mercurial.httpclient',
711 'mercurial.pure',
766 'mercurial.pure',
712 'mercurial.thirdparty',
767 'mercurial.thirdparty',
713 'mercurial.thirdparty.attr',
768 'mercurial.thirdparty.attr',
714 'hgext', 'hgext.convert', 'hgext.fsmonitor',
769 'hgext', 'hgext.convert', 'hgext.fsmonitor',
715 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
770 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
716 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
771 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
717 'hgdemandimport']
772 'hgdemandimport']
718
773
719 common_depends = ['mercurial/bitmanipulation.h',
774 common_depends = ['mercurial/bitmanipulation.h',
720 'mercurial/compat.h',
775 'mercurial/compat.h',
721 'mercurial/cext/util.h']
776 'mercurial/cext/util.h']
722 common_include_dirs = ['mercurial']
777 common_include_dirs = ['mercurial']
723
778
724 osutil_cflags = []
779 osutil_cflags = []
725 osutil_ldflags = []
780 osutil_ldflags = []
726
781
727 # platform specific macros
782 # platform specific macros
728 for plat, func in [('bsd', 'setproctitle')]:
783 for plat, func in [('bsd', 'setproctitle')]:
729 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
784 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
730 osutil_cflags.append('-DHAVE_%s' % func.upper())
785 osutil_cflags.append('-DHAVE_%s' % func.upper())
731
786
732 for plat, macro, code in [
787 for plat, macro, code in [
733 ('bsd|darwin', 'BSD_STATFS', '''
788 ('bsd|darwin', 'BSD_STATFS', '''
734 #include <sys/param.h>
789 #include <sys/param.h>
735 #include <sys/mount.h>
790 #include <sys/mount.h>
736 int main() { struct statfs s; return sizeof(s.f_fstypename); }
791 int main() { struct statfs s; return sizeof(s.f_fstypename); }
737 '''),
792 '''),
738 ('linux', 'LINUX_STATFS', '''
793 ('linux', 'LINUX_STATFS', '''
739 #include <linux/magic.h>
794 #include <linux/magic.h>
740 #include <sys/vfs.h>
795 #include <sys/vfs.h>
741 int main() { struct statfs s; return sizeof(s.f_type); }
796 int main() { struct statfs s; return sizeof(s.f_type); }
742 '''),
797 '''),
743 ]:
798 ]:
744 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
799 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
745 osutil_cflags.append('-DHAVE_%s' % macro)
800 osutil_cflags.append('-DHAVE_%s' % macro)
746
801
747 if sys.platform == 'darwin':
802 if sys.platform == 'darwin':
748 osutil_ldflags += ['-framework', 'ApplicationServices']
803 osutil_ldflags += ['-framework', 'ApplicationServices']
749
804
750 extmodules = [
805 extmodules = [
751 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
806 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
752 include_dirs=common_include_dirs,
807 include_dirs=common_include_dirs,
753 depends=common_depends),
808 depends=common_depends),
754 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
809 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
755 'mercurial/cext/bdiff.c'],
810 'mercurial/cext/bdiff.c'],
756 include_dirs=common_include_dirs,
811 include_dirs=common_include_dirs,
757 depends=common_depends + ['mercurial/bdiff.h']),
812 depends=common_depends + ['mercurial/bdiff.h']),
758 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
813 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
759 include_dirs=common_include_dirs,
814 include_dirs=common_include_dirs,
760 depends=common_depends),
815 depends=common_depends),
761 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
816 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
762 'mercurial/cext/mpatch.c'],
817 'mercurial/cext/mpatch.c'],
763 include_dirs=common_include_dirs,
818 include_dirs=common_include_dirs,
764 depends=common_depends),
819 depends=common_depends),
765 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
820 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
766 'mercurial/cext/dirs.c',
821 'mercurial/cext/dirs.c',
767 'mercurial/cext/manifest.c',
822 'mercurial/cext/manifest.c',
768 'mercurial/cext/parsers.c',
823 'mercurial/cext/parsers.c',
769 'mercurial/cext/pathencode.c',
824 'mercurial/cext/pathencode.c',
770 'mercurial/cext/revlog.c'],
825 'mercurial/cext/revlog.c'],
771 include_dirs=common_include_dirs,
826 include_dirs=common_include_dirs,
772 depends=common_depends + ['mercurial/cext/charencode.h']),
827 depends=common_depends + ['mercurial/cext/charencode.h']),
773 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
828 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
774 include_dirs=common_include_dirs,
829 include_dirs=common_include_dirs,
775 extra_compile_args=osutil_cflags,
830 extra_compile_args=osutil_cflags,
776 extra_link_args=osutil_ldflags,
831 extra_link_args=osutil_ldflags,
777 depends=common_depends),
832 depends=common_depends),
778 Extension('hgext.fsmonitor.pywatchman.bser',
833 Extension('hgext.fsmonitor.pywatchman.bser',
779 ['hgext/fsmonitor/pywatchman/bser.c']),
834 ['hgext/fsmonitor/pywatchman/bser.c']),
780 ]
835 ]
781
836
782 sys.path.insert(0, 'contrib/python-zstandard')
837 sys.path.insert(0, 'contrib/python-zstandard')
783 import setup_zstd
838 import setup_zstd
784 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
839 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
785
840
786 try:
841 try:
787 from distutils import cygwinccompiler
842 from distutils import cygwinccompiler
788
843
789 # the -mno-cygwin option has been deprecated for years
844 # the -mno-cygwin option has been deprecated for years
790 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
845 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
791
846
792 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
847 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
793 def __init__(self, *args, **kwargs):
848 def __init__(self, *args, **kwargs):
794 mingw32compilerclass.__init__(self, *args, **kwargs)
849 mingw32compilerclass.__init__(self, *args, **kwargs)
795 for i in 'compiler compiler_so linker_exe linker_so'.split():
850 for i in 'compiler compiler_so linker_exe linker_so'.split():
796 try:
851 try:
797 getattr(self, i).remove('-mno-cygwin')
852 getattr(self, i).remove('-mno-cygwin')
798 except ValueError:
853 except ValueError:
799 pass
854 pass
800
855
801 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
856 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
802 except ImportError:
857 except ImportError:
803 # the cygwinccompiler package is not available on some Python
858 # the cygwinccompiler package is not available on some Python
804 # distributions like the ones from the optware project for Synology
859 # distributions like the ones from the optware project for Synology
805 # DiskStation boxes
860 # DiskStation boxes
806 class HackedMingw32CCompiler(object):
861 class HackedMingw32CCompiler(object):
807 pass
862 pass
808
863
809 if os.name == 'nt':
864 if os.name == 'nt':
810 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
865 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
811 # extra_link_args to distutils.extensions.Extension() doesn't have any
866 # extra_link_args to distutils.extensions.Extension() doesn't have any
812 # effect.
867 # effect.
813 from distutils import msvccompiler
868 from distutils import msvccompiler
814
869
815 msvccompilerclass = msvccompiler.MSVCCompiler
870 msvccompilerclass = msvccompiler.MSVCCompiler
816
871
817 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
872 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
818 def initialize(self):
873 def initialize(self):
819 msvccompilerclass.initialize(self)
874 msvccompilerclass.initialize(self)
820 # "warning LNK4197: export 'func' specified multiple times"
875 # "warning LNK4197: export 'func' specified multiple times"
821 self.ldflags_shared.append('/ignore:4197')
876 self.ldflags_shared.append('/ignore:4197')
822 self.ldflags_shared_debug.append('/ignore:4197')
877 self.ldflags_shared_debug.append('/ignore:4197')
823
878
824 msvccompiler.MSVCCompiler = HackedMSVCCompiler
879 msvccompiler.MSVCCompiler = HackedMSVCCompiler
825
880
826 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
881 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
827 'help/*.txt',
882 'help/*.txt',
828 'help/internals/*.txt',
883 'help/internals/*.txt',
829 'default.d/*.rc',
884 'default.d/*.rc',
830 'dummycert.pem']}
885 'dummycert.pem']}
831
886
832 def ordinarypath(p):
887 def ordinarypath(p):
833 return p and p[0] != '.' and p[-1] != '~'
888 return p and p[0] != '.' and p[-1] != '~'
834
889
835 for root in ('templates',):
890 for root in ('templates',):
836 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
891 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
837 curdir = curdir.split(os.sep, 1)[1]
892 curdir = curdir.split(os.sep, 1)[1]
838 dirs[:] = filter(ordinarypath, dirs)
893 dirs[:] = filter(ordinarypath, dirs)
839 for f in filter(ordinarypath, files):
894 for f in filter(ordinarypath, files):
840 f = os.path.join(curdir, f)
895 f = os.path.join(curdir, f)
841 packagedata['mercurial'].append(f)
896 packagedata['mercurial'].append(f)
842
897
843 datafiles = []
898 datafiles = []
844
899
845 # distutils expects version to be str/unicode. Converting it to
900 # distutils expects version to be str/unicode. Converting it to
846 # unicode on Python 2 still works because it won't contain any
901 # unicode on Python 2 still works because it won't contain any
847 # non-ascii bytes and will be implicitly converted back to bytes
902 # non-ascii bytes and will be implicitly converted back to bytes
848 # when operated on.
903 # when operated on.
849 assert isinstance(version, bytes)
904 assert isinstance(version, bytes)
850 setupversion = version.decode('ascii')
905 setupversion = version.decode('ascii')
851
906
852 extra = {}
907 extra = {}
853
908
854 if issetuptools:
909 if issetuptools:
855 extra['python_requires'] = supportedpy
910 extra['python_requires'] = supportedpy
856 if py2exeloaded:
911 if py2exeloaded:
857 extra['console'] = [
912 extra['console'] = [
858 {'script':'hg',
913 {'script':'hg',
859 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
914 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
860 'product_version':version}]
915 'product_version':version}]
861 # sub command of 'build' because 'py2exe' does not handle sub_commands
916 # sub command of 'build' because 'py2exe' does not handle sub_commands
862 build.sub_commands.insert(0, ('build_hgextindex', None))
917 build.sub_commands.insert(0, ('build_hgextindex', None))
863 # put dlls in sub directory so that they won't pollute PATH
918 # put dlls in sub directory so that they won't pollute PATH
864 extra['zipfile'] = 'lib/library.zip'
919 extra['zipfile'] = 'lib/library.zip'
865
920
866 if os.name == 'nt':
921 if os.name == 'nt':
867 # Windows binary file versions for exe/dll files must have the
922 # Windows binary file versions for exe/dll files must have the
868 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
923 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
869 setupversion = version.split('+', 1)[0]
924 setupversion = version.split('+', 1)[0]
870
925
871 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
926 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
872 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
927 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
873 if version:
928 if version:
874 version = version[0]
929 version = version[0]
875 if sys.version_info[0] == 3:
930 if sys.version_info[0] == 3:
876 version = version.decode('utf-8')
931 version = version.decode('utf-8')
877 xcode4 = (version.startswith('Xcode') and
932 xcode4 = (version.startswith('Xcode') and
878 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
933 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
879 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
934 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
880 else:
935 else:
881 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
936 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
882 # installed, but instead with only command-line tools. Assume
937 # installed, but instead with only command-line tools. Assume
883 # that only happens on >= Lion, thus no PPC support.
938 # that only happens on >= Lion, thus no PPC support.
884 xcode4 = True
939 xcode4 = True
885 xcode51 = False
940 xcode51 = False
886
941
887 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
942 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
888 # distutils.sysconfig
943 # distutils.sysconfig
889 if xcode4:
944 if xcode4:
890 os.environ['ARCHFLAGS'] = ''
945 os.environ['ARCHFLAGS'] = ''
891
946
892 # XCode 5.1 changes clang such that it now fails to compile if the
947 # XCode 5.1 changes clang such that it now fails to compile if the
893 # -mno-fused-madd flag is passed, but the version of Python shipped with
948 # -mno-fused-madd flag is passed, but the version of Python shipped with
894 # OS X 10.9 Mavericks includes this flag. This causes problems in all
949 # OS X 10.9 Mavericks includes this flag. This causes problems in all
895 # C extension modules, and a bug has been filed upstream at
950 # C extension modules, and a bug has been filed upstream at
896 # http://bugs.python.org/issue21244. We also need to patch this here
951 # http://bugs.python.org/issue21244. We also need to patch this here
897 # so Mercurial can continue to compile in the meantime.
952 # so Mercurial can continue to compile in the meantime.
898 if xcode51:
953 if xcode51:
899 cflags = get_config_var('CFLAGS')
954 cflags = get_config_var('CFLAGS')
900 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
955 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
901 os.environ['CFLAGS'] = (
956 os.environ['CFLAGS'] = (
902 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
957 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
903
958
904 setup(name='mercurial',
959 setup(name='mercurial',
905 version=setupversion,
960 version=setupversion,
906 author='Matt Mackall and many others',
961 author='Matt Mackall and many others',
907 author_email='mercurial@mercurial-scm.org',
962 author_email='mercurial@mercurial-scm.org',
908 url='https://mercurial-scm.org/',
963 url='https://mercurial-scm.org/',
909 download_url='https://mercurial-scm.org/release/',
964 download_url='https://mercurial-scm.org/release/',
910 description=('Fast scalable distributed SCM (revision control, version '
965 description=('Fast scalable distributed SCM (revision control, version '
911 'control) system'),
966 'control) system'),
912 long_description=('Mercurial is a distributed SCM tool written in Python.'
967 long_description=('Mercurial is a distributed SCM tool written in Python.'
913 ' It is used by a number of large projects that require'
968 ' It is used by a number of large projects that require'
914 ' fast, reliable distributed revision control, such as '
969 ' fast, reliable distributed revision control, such as '
915 'Mozilla.'),
970 'Mozilla.'),
916 license='GNU GPLv2 or any later version',
971 license='GNU GPLv2 or any later version',
917 classifiers=[
972 classifiers=[
918 'Development Status :: 6 - Mature',
973 'Development Status :: 6 - Mature',
919 'Environment :: Console',
974 'Environment :: Console',
920 'Intended Audience :: Developers',
975 'Intended Audience :: Developers',
921 'Intended Audience :: System Administrators',
976 'Intended Audience :: System Administrators',
922 'License :: OSI Approved :: GNU General Public License (GPL)',
977 'License :: OSI Approved :: GNU General Public License (GPL)',
923 'Natural Language :: Danish',
978 'Natural Language :: Danish',
924 'Natural Language :: English',
979 'Natural Language :: English',
925 'Natural Language :: German',
980 'Natural Language :: German',
926 'Natural Language :: Italian',
981 'Natural Language :: Italian',
927 'Natural Language :: Japanese',
982 'Natural Language :: Japanese',
928 'Natural Language :: Portuguese (Brazilian)',
983 'Natural Language :: Portuguese (Brazilian)',
929 'Operating System :: Microsoft :: Windows',
984 'Operating System :: Microsoft :: Windows',
930 'Operating System :: OS Independent',
985 'Operating System :: OS Independent',
931 'Operating System :: POSIX',
986 'Operating System :: POSIX',
932 'Programming Language :: C',
987 'Programming Language :: C',
933 'Programming Language :: Python',
988 'Programming Language :: Python',
934 'Topic :: Software Development :: Version Control',
989 'Topic :: Software Development :: Version Control',
935 ],
990 ],
936 scripts=scripts,
991 scripts=scripts,
937 packages=packages,
992 packages=packages,
938 ext_modules=extmodules,
993 ext_modules=extmodules,
939 data_files=datafiles,
994 data_files=datafiles,
940 package_data=packagedata,
995 package_data=packagedata,
941 cmdclass=cmdclass,
996 cmdclass=cmdclass,
942 distclass=hgdist,
997 distclass=hgdist,
943 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
998 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
944 # implicitly imported per module policy
999 # implicitly imported per module policy
945 # (cffi wouldn't be used as a frozen exe)
1000 # (cffi wouldn't be used as a frozen exe)
946 'mercurial.cext',
1001 'mercurial.cext',
947 #'mercurial.cffi',
1002 #'mercurial.cffi',
948 'mercurial.pure']},
1003 'mercurial.pure']},
949 'bdist_mpkg': {'zipdist': False,
1004 'bdist_mpkg': {'zipdist': False,
950 'license': 'COPYING',
1005 'license': 'COPYING',
951 'readme': 'contrib/macosx/Readme.html',
1006 'readme': 'contrib/macosx/Readme.html',
952 'welcome': 'contrib/macosx/Welcome.html',
1007 'welcome': 'contrib/macosx/Welcome.html',
953 },
1008 },
954 },
1009 },
955 **extra)
1010 **extra)
General Comments 0
You need to be logged in to leave comments. Login now