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