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