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