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