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