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