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