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