##// END OF EJS Templates
setup: fix runcmd() usage on darwin...
Adam Simpkins -
r33119:176030f6 default
parent child Browse files
Show More
@@ -1,895 +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 return env
231
231
232 version = ''
232 version = ''
233
233
234 if os.path.isdir('.hg'):
234 if os.path.isdir('.hg'):
235 hg = findhg()
235 hg = findhg()
236 cmd = ['log', '-r', '.', '--template', '{tags}\n']
236 cmd = ['log', '-r', '.', '--template', '{tags}\n']
237 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()]
238 hgid = hg.run(['id', '-i']).strip()
238 hgid = hg.run(['id', '-i']).strip()
239 if not hgid:
239 if not hgid:
240 # Bail out if hg is having problems interacting with this repository,
240 # Bail out if hg is having problems interacting with this repository,
241 # rather than falling through and producing a bogus version number.
241 # rather than falling through and producing a bogus version number.
242 # Continuing with an invalid version number will break extensions
242 # Continuing with an invalid version number will break extensions
243 # that define minimumhgversion.
243 # that define minimumhgversion.
244 raise SystemExit('Unable to determine hg version from local repository')
244 raise SystemExit('Unable to determine hg version from local repository')
245 if numerictags: # tag(s) found
245 if numerictags: # tag(s) found
246 version = numerictags[-1]
246 version = numerictags[-1]
247 if hgid.endswith('+'): # propagate the dirty status to the tag
247 if hgid.endswith('+'): # propagate the dirty status to the tag
248 version += '+'
248 version += '+'
249 else: # no tag found
249 else: # no tag found
250 ltagcmd = ['parents', '--template', '{latesttag}']
250 ltagcmd = ['parents', '--template', '{latesttag}']
251 ltag = hg.run(ltagcmd)
251 ltag = hg.run(ltagcmd)
252 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
252 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
253 changessince = len(hg.run(changessincecmd).splitlines())
253 changessince = len(hg.run(changessincecmd).splitlines())
254 version = '%s+%s-%s' % (ltag, changessince, hgid)
254 version = '%s+%s-%s' % (ltag, changessince, hgid)
255 if version.endswith('+'):
255 if version.endswith('+'):
256 version += time.strftime('%Y%m%d')
256 version += time.strftime('%Y%m%d')
257 elif os.path.exists('.hg_archival.txt'):
257 elif os.path.exists('.hg_archival.txt'):
258 kw = dict([[t.strip() for t in l.split(':', 1)]
258 kw = dict([[t.strip() for t in l.split(':', 1)]
259 for l in open('.hg_archival.txt')])
259 for l in open('.hg_archival.txt')])
260 if 'tag' in kw:
260 if 'tag' in kw:
261 version = kw['tag']
261 version = kw['tag']
262 elif 'latesttag' in kw:
262 elif 'latesttag' in kw:
263 if 'changessincelatesttag' in kw:
263 if 'changessincelatesttag' in kw:
264 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
264 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
265 else:
265 else:
266 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
266 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
267 else:
267 else:
268 version = kw.get('node', '')[:12]
268 version = kw.get('node', '')[:12]
269
269
270 if version:
270 if version:
271 with open("mercurial/__version__.py", "w") as f:
271 with open("mercurial/__version__.py", "w") as f:
272 f.write('# this file is autogenerated by setup.py\n')
272 f.write('# this file is autogenerated by setup.py\n')
273 f.write('version = "%s"\n' % version)
273 f.write('version = "%s"\n' % version)
274
274
275 try:
275 try:
276 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
276 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
277 os.environ['HGMODULEPOLICY'] = 'py'
277 os.environ['HGMODULEPOLICY'] = 'py'
278 from mercurial import __version__
278 from mercurial import __version__
279 version = __version__.version
279 version = __version__.version
280 except ImportError:
280 except ImportError:
281 version = 'unknown'
281 version = 'unknown'
282 finally:
282 finally:
283 if oldpolicy is None:
283 if oldpolicy is None:
284 del os.environ['HGMODULEPOLICY']
284 del os.environ['HGMODULEPOLICY']
285 else:
285 else:
286 os.environ['HGMODULEPOLICY'] = oldpolicy
286 os.environ['HGMODULEPOLICY'] = oldpolicy
287
287
288 class hgbuild(build):
288 class hgbuild(build):
289 # Insert hgbuildmo first so that files in mercurial/locale/ are found
289 # Insert hgbuildmo first so that files in mercurial/locale/ are found
290 # when build_py is run next.
290 # when build_py is run next.
291 sub_commands = [('build_mo', None)] + build.sub_commands
291 sub_commands = [('build_mo', None)] + build.sub_commands
292
292
293 class hgbuildmo(build):
293 class hgbuildmo(build):
294
294
295 description = "build translations (.mo files)"
295 description = "build translations (.mo files)"
296
296
297 def run(self):
297 def run(self):
298 if not find_executable('msgfmt'):
298 if not find_executable('msgfmt'):
299 self.warn("could not find msgfmt executable, no translations "
299 self.warn("could not find msgfmt executable, no translations "
300 "will be built")
300 "will be built")
301 return
301 return
302
302
303 podir = 'i18n'
303 podir = 'i18n'
304 if not os.path.isdir(podir):
304 if not os.path.isdir(podir):
305 self.warn("could not find %s/ directory" % podir)
305 self.warn("could not find %s/ directory" % podir)
306 return
306 return
307
307
308 join = os.path.join
308 join = os.path.join
309 for po in os.listdir(podir):
309 for po in os.listdir(podir):
310 if not po.endswith('.po'):
310 if not po.endswith('.po'):
311 continue
311 continue
312 pofile = join(podir, po)
312 pofile = join(podir, po)
313 modir = join('locale', po[:-3], 'LC_MESSAGES')
313 modir = join('locale', po[:-3], 'LC_MESSAGES')
314 mofile = join(modir, 'hg.mo')
314 mofile = join(modir, 'hg.mo')
315 mobuildfile = join('mercurial', mofile)
315 mobuildfile = join('mercurial', mofile)
316 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
316 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
317 if sys.platform != 'sunos5':
317 if sys.platform != 'sunos5':
318 # msgfmt on Solaris does not know about -c
318 # msgfmt on Solaris does not know about -c
319 cmd.append('-c')
319 cmd.append('-c')
320 self.mkpath(join('mercurial', modir))
320 self.mkpath(join('mercurial', modir))
321 self.make_file([pofile], mobuildfile, spawn, (cmd,))
321 self.make_file([pofile], mobuildfile, spawn, (cmd,))
322
322
323
323
324 class hgdist(Distribution):
324 class hgdist(Distribution):
325 pure = False
325 pure = False
326 cffi = ispypy
326 cffi = ispypy
327
327
328 global_options = Distribution.global_options + \
328 global_options = Distribution.global_options + \
329 [('pure', None, "use pure (slow) Python "
329 [('pure', None, "use pure (slow) Python "
330 "code instead of C extensions"),
330 "code instead of C extensions"),
331 ]
331 ]
332
332
333 def has_ext_modules(self):
333 def has_ext_modules(self):
334 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
334 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
335 # too late for some cases
335 # too late for some cases
336 return not self.pure and Distribution.has_ext_modules(self)
336 return not self.pure and Distribution.has_ext_modules(self)
337
337
338 # This is ugly as a one-liner. So use a variable.
338 # This is ugly as a one-liner. So use a variable.
339 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
339 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
340 buildextnegops['no-zstd'] = 'zstd'
340 buildextnegops['no-zstd'] = 'zstd'
341
341
342 class hgbuildext(build_ext):
342 class hgbuildext(build_ext):
343 user_options = build_ext.user_options + [
343 user_options = build_ext.user_options + [
344 ('zstd', None, 'compile zstd bindings [default]'),
344 ('zstd', None, 'compile zstd bindings [default]'),
345 ('no-zstd', None, 'do not compile zstd bindings'),
345 ('no-zstd', None, 'do not compile zstd bindings'),
346 ]
346 ]
347
347
348 boolean_options = build_ext.boolean_options + ['zstd']
348 boolean_options = build_ext.boolean_options + ['zstd']
349 negative_opt = buildextnegops
349 negative_opt = buildextnegops
350
350
351 def initialize_options(self):
351 def initialize_options(self):
352 self.zstd = True
352 self.zstd = True
353 return build_ext.initialize_options(self)
353 return build_ext.initialize_options(self)
354
354
355 def build_extensions(self):
355 def build_extensions(self):
356 # Filter out zstd if disabled via argument.
356 # Filter out zstd if disabled via argument.
357 if not self.zstd:
357 if not self.zstd:
358 self.extensions = [e for e in self.extensions
358 self.extensions = [e for e in self.extensions
359 if e.name != 'mercurial.zstd']
359 if e.name != 'mercurial.zstd']
360
360
361 return build_ext.build_extensions(self)
361 return build_ext.build_extensions(self)
362
362
363 def build_extension(self, ext):
363 def build_extension(self, ext):
364 try:
364 try:
365 build_ext.build_extension(self, ext)
365 build_ext.build_extension(self, ext)
366 except CCompilerError:
366 except CCompilerError:
367 if not getattr(ext, 'optional', False):
367 if not getattr(ext, 'optional', False):
368 raise
368 raise
369 log.warn("Failed to build optional extension '%s' (skipping)",
369 log.warn("Failed to build optional extension '%s' (skipping)",
370 ext.name)
370 ext.name)
371
371
372 class hgbuildscripts(build_scripts):
372 class hgbuildscripts(build_scripts):
373 def run(self):
373 def run(self):
374 if os.name != 'nt' or self.distribution.pure:
374 if os.name != 'nt' or self.distribution.pure:
375 return build_scripts.run(self)
375 return build_scripts.run(self)
376
376
377 exebuilt = False
377 exebuilt = False
378 try:
378 try:
379 self.run_command('build_hgexe')
379 self.run_command('build_hgexe')
380 exebuilt = True
380 exebuilt = True
381 except (DistutilsError, CCompilerError):
381 except (DistutilsError, CCompilerError):
382 log.warn('failed to build optional hg.exe')
382 log.warn('failed to build optional hg.exe')
383
383
384 if exebuilt:
384 if exebuilt:
385 # Copying hg.exe to the scripts build directory ensures it is
385 # Copying hg.exe to the scripts build directory ensures it is
386 # installed by the install_scripts command.
386 # installed by the install_scripts command.
387 hgexecommand = self.get_finalized_command('build_hgexe')
387 hgexecommand = self.get_finalized_command('build_hgexe')
388 dest = os.path.join(self.build_dir, 'hg.exe')
388 dest = os.path.join(self.build_dir, 'hg.exe')
389 self.mkpath(self.build_dir)
389 self.mkpath(self.build_dir)
390 self.copy_file(hgexecommand.hgexepath, dest)
390 self.copy_file(hgexecommand.hgexepath, dest)
391
391
392 # Remove hg.bat because it is redundant with hg.exe.
392 # Remove hg.bat because it is redundant with hg.exe.
393 self.scripts.remove('contrib/win32/hg.bat')
393 self.scripts.remove('contrib/win32/hg.bat')
394
394
395 return build_scripts.run(self)
395 return build_scripts.run(self)
396
396
397 class hgbuildpy(build_py):
397 class hgbuildpy(build_py):
398 def finalize_options(self):
398 def finalize_options(self):
399 build_py.finalize_options(self)
399 build_py.finalize_options(self)
400
400
401 if self.distribution.pure:
401 if self.distribution.pure:
402 self.distribution.ext_modules = []
402 self.distribution.ext_modules = []
403 elif self.distribution.cffi:
403 elif self.distribution.cffi:
404 from mercurial.cffi import (
404 from mercurial.cffi import (
405 bdiffbuild,
405 bdiffbuild,
406 mpatchbuild,
406 mpatchbuild,
407 )
407 )
408 exts = [mpatchbuild.ffi.distutils_extension(),
408 exts = [mpatchbuild.ffi.distutils_extension(),
409 bdiffbuild.ffi.distutils_extension()]
409 bdiffbuild.ffi.distutils_extension()]
410 # cffi modules go here
410 # cffi modules go here
411 if sys.platform == 'darwin':
411 if sys.platform == 'darwin':
412 from mercurial.cffi import osutilbuild
412 from mercurial.cffi import osutilbuild
413 exts.append(osutilbuild.ffi.distutils_extension())
413 exts.append(osutilbuild.ffi.distutils_extension())
414 self.distribution.ext_modules = exts
414 self.distribution.ext_modules = exts
415 else:
415 else:
416 h = os.path.join(get_python_inc(), 'Python.h')
416 h = os.path.join(get_python_inc(), 'Python.h')
417 if not os.path.exists(h):
417 if not os.path.exists(h):
418 raise SystemExit('Python headers are required to build '
418 raise SystemExit('Python headers are required to build '
419 'Mercurial but weren\'t found in %s' % h)
419 'Mercurial but weren\'t found in %s' % h)
420
420
421 def run(self):
421 def run(self):
422 basepath = os.path.join(self.build_lib, 'mercurial')
422 basepath = os.path.join(self.build_lib, 'mercurial')
423 self.mkpath(basepath)
423 self.mkpath(basepath)
424
424
425 if self.distribution.pure:
425 if self.distribution.pure:
426 modulepolicy = 'py'
426 modulepolicy = 'py'
427 elif self.build_lib == '.':
427 elif self.build_lib == '.':
428 # in-place build should run without rebuilding C extensions
428 # in-place build should run without rebuilding C extensions
429 modulepolicy = 'allow'
429 modulepolicy = 'allow'
430 else:
430 else:
431 modulepolicy = 'c'
431 modulepolicy = 'c'
432 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
432 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
433 f.write('# this file is autogenerated by setup.py\n')
433 f.write('# this file is autogenerated by setup.py\n')
434 f.write('modulepolicy = b"%s"\n' % modulepolicy)
434 f.write('modulepolicy = b"%s"\n' % modulepolicy)
435
435
436 build_py.run(self)
436 build_py.run(self)
437
437
438 class buildhgextindex(Command):
438 class buildhgextindex(Command):
439 description = 'generate prebuilt index of hgext (for frozen package)'
439 description = 'generate prebuilt index of hgext (for frozen package)'
440 user_options = []
440 user_options = []
441 _indexfilename = 'hgext/__index__.py'
441 _indexfilename = 'hgext/__index__.py'
442
442
443 def initialize_options(self):
443 def initialize_options(self):
444 pass
444 pass
445
445
446 def finalize_options(self):
446 def finalize_options(self):
447 pass
447 pass
448
448
449 def run(self):
449 def run(self):
450 if os.path.exists(self._indexfilename):
450 if os.path.exists(self._indexfilename):
451 with open(self._indexfilename, 'w') as f:
451 with open(self._indexfilename, 'w') as f:
452 f.write('# empty\n')
452 f.write('# empty\n')
453
453
454 # here no extension enabled, disabled() lists up everything
454 # here no extension enabled, disabled() lists up everything
455 code = ('import pprint; from mercurial import extensions; '
455 code = ('import pprint; from mercurial import extensions; '
456 'pprint.pprint(extensions.disabled())')
456 'pprint.pprint(extensions.disabled())')
457 returncode, out, err = runcmd([sys.executable, '-c', code],
457 returncode, out, err = runcmd([sys.executable, '-c', code],
458 localhgenv())
458 localhgenv())
459 if err or returncode != 0:
459 if err or returncode != 0:
460 raise DistutilsExecError(err)
460 raise DistutilsExecError(err)
461
461
462 with open(self._indexfilename, 'w') as f:
462 with open(self._indexfilename, 'w') as f:
463 f.write('# this file is autogenerated by setup.py\n')
463 f.write('# this file is autogenerated by setup.py\n')
464 f.write('docs = ')
464 f.write('docs = ')
465 f.write(out)
465 f.write(out)
466
466
467 class buildhgexe(build_ext):
467 class buildhgexe(build_ext):
468 description = 'compile hg.exe from mercurial/exewrapper.c'
468 description = 'compile hg.exe from mercurial/exewrapper.c'
469
469
470 def build_extensions(self):
470 def build_extensions(self):
471 if os.name != 'nt':
471 if os.name != 'nt':
472 return
472 return
473 if isinstance(self.compiler, HackedMingw32CCompiler):
473 if isinstance(self.compiler, HackedMingw32CCompiler):
474 self.compiler.compiler_so = self.compiler.compiler # no -mdll
474 self.compiler.compiler_so = self.compiler.compiler # no -mdll
475 self.compiler.dll_libraries = [] # no -lmsrvc90
475 self.compiler.dll_libraries = [] # no -lmsrvc90
476
476
477 # Different Python installs can have different Python library
477 # Different Python installs can have different Python library
478 # names. e.g. the official CPython distribution uses pythonXY.dll
478 # names. e.g. the official CPython distribution uses pythonXY.dll
479 # and MinGW uses libpythonX.Y.dll.
479 # and MinGW uses libpythonX.Y.dll.
480 _kernel32 = ctypes.windll.kernel32
480 _kernel32 = ctypes.windll.kernel32
481 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
481 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
482 ctypes.c_void_p,
482 ctypes.c_void_p,
483 ctypes.c_ulong]
483 ctypes.c_ulong]
484 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
484 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
485 size = 1000
485 size = 1000
486 buf = ctypes.create_string_buffer(size + 1)
486 buf = ctypes.create_string_buffer(size + 1)
487 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
487 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
488 size)
488 size)
489
489
490 if filelen > 0 and filelen != size:
490 if filelen > 0 and filelen != size:
491 dllbasename = os.path.basename(buf.value)
491 dllbasename = os.path.basename(buf.value)
492 if not dllbasename.lower().endswith('.dll'):
492 if not dllbasename.lower().endswith('.dll'):
493 raise SystemExit('Python DLL does not end with .dll: %s' %
493 raise SystemExit('Python DLL does not end with .dll: %s' %
494 dllbasename)
494 dllbasename)
495 pythonlib = dllbasename[:-4]
495 pythonlib = dllbasename[:-4]
496 else:
496 else:
497 log.warn('could not determine Python DLL filename; '
497 log.warn('could not determine Python DLL filename; '
498 'assuming pythonXY')
498 'assuming pythonXY')
499
499
500 hv = sys.hexversion
500 hv = sys.hexversion
501 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
501 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
502
502
503 log.info('using %s as Python library name' % pythonlib)
503 log.info('using %s as Python library name' % pythonlib)
504 with open('mercurial/hgpythonlib.h', 'wb') as f:
504 with open('mercurial/hgpythonlib.h', 'wb') as f:
505 f.write('/* this file is autogenerated by setup.py */\n')
505 f.write('/* this file is autogenerated by setup.py */\n')
506 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
506 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
507 objects = self.compiler.compile(['mercurial/exewrapper.c'],
507 objects = self.compiler.compile(['mercurial/exewrapper.c'],
508 output_dir=self.build_temp)
508 output_dir=self.build_temp)
509 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
509 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
510 target = os.path.join(dir, 'hg')
510 target = os.path.join(dir, 'hg')
511 self.compiler.link_executable(objects, target,
511 self.compiler.link_executable(objects, target,
512 libraries=[],
512 libraries=[],
513 output_dir=self.build_temp)
513 output_dir=self.build_temp)
514
514
515 @property
515 @property
516 def hgexepath(self):
516 def hgexepath(self):
517 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
517 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
518 return os.path.join(self.build_temp, dir, 'hg.exe')
518 return os.path.join(self.build_temp, dir, 'hg.exe')
519
519
520 class hginstall(install):
520 class hginstall(install):
521
521
522 user_options = install.user_options + [
522 user_options = install.user_options + [
523 ('old-and-unmanageable', None,
523 ('old-and-unmanageable', None,
524 'noop, present for eggless setuptools compat'),
524 'noop, present for eggless setuptools compat'),
525 ('single-version-externally-managed', None,
525 ('single-version-externally-managed', None,
526 'noop, present for eggless setuptools compat'),
526 'noop, present for eggless setuptools compat'),
527 ]
527 ]
528
528
529 # 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.
530 single_version_externally_managed = True
530 single_version_externally_managed = True
531
531
532 def get_sub_commands(self):
532 def get_sub_commands(self):
533 # Screen out egg related commands to prevent egg generation. But allow
533 # Screen out egg related commands to prevent egg generation. But allow
534 # mercurial.egg-info generation, since that is part of modern
534 # mercurial.egg-info generation, since that is part of modern
535 # packaging.
535 # packaging.
536 excl = {'bdist_egg'}
536 excl = {'bdist_egg'}
537 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))
538
538
539 class hginstalllib(install_lib):
539 class hginstalllib(install_lib):
540 '''
540 '''
541 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
542 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,
543 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
544 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
545 copying will lead to a broken install.
545 copying will lead to a broken install.
546
546
547 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
548 insufficient, as it might still be applying a umask.
548 insufficient, as it might still be applying a umask.
549 '''
549 '''
550
550
551 def run(self):
551 def run(self):
552 realcopyfile = file_util.copy_file
552 realcopyfile = file_util.copy_file
553 def copyfileandsetmode(*args, **kwargs):
553 def copyfileandsetmode(*args, **kwargs):
554 src, dst = args[0], args[1]
554 src, dst = args[0], args[1]
555 dst, copied = realcopyfile(*args, **kwargs)
555 dst, copied = realcopyfile(*args, **kwargs)
556 if copied:
556 if copied:
557 st = os.stat(src)
557 st = os.stat(src)
558 # Persist executable bit (apply it to group and other if user
558 # Persist executable bit (apply it to group and other if user
559 # has it)
559 # has it)
560 if st[stat.ST_MODE] & stat.S_IXUSR:
560 if st[stat.ST_MODE] & stat.S_IXUSR:
561 setmode = int('0755', 8)
561 setmode = int('0755', 8)
562 else:
562 else:
563 setmode = int('0644', 8)
563 setmode = int('0644', 8)
564 m = stat.S_IMODE(st[stat.ST_MODE])
564 m = stat.S_IMODE(st[stat.ST_MODE])
565 m = (m & ~int('0777', 8)) | setmode
565 m = (m & ~int('0777', 8)) | setmode
566 os.chmod(dst, m)
566 os.chmod(dst, m)
567 file_util.copy_file = copyfileandsetmode
567 file_util.copy_file = copyfileandsetmode
568 try:
568 try:
569 install_lib.run(self)
569 install_lib.run(self)
570 finally:
570 finally:
571 file_util.copy_file = realcopyfile
571 file_util.copy_file = realcopyfile
572
572
573 class hginstallscripts(install_scripts):
573 class hginstallscripts(install_scripts):
574 '''
574 '''
575 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
576 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
577 to the directory for scripts.
577 to the directory for scripts.
578 '''
578 '''
579
579
580 def initialize_options(self):
580 def initialize_options(self):
581 install_scripts.initialize_options(self)
581 install_scripts.initialize_options(self)
582
582
583 self.install_lib = None
583 self.install_lib = None
584
584
585 def finalize_options(self):
585 def finalize_options(self):
586 install_scripts.finalize_options(self)
586 install_scripts.finalize_options(self)
587 self.set_undefined_options('install',
587 self.set_undefined_options('install',
588 ('install_lib', 'install_lib'))
588 ('install_lib', 'install_lib'))
589
589
590 def run(self):
590 def run(self):
591 install_scripts.run(self)
591 install_scripts.run(self)
592
592
593 # 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
594 # the install path is known. For wheels, the logic below calculates
594 # the install path is known. For wheels, the logic below calculates
595 # 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
596 # wheel archive looks like:
596 # wheel archive looks like:
597 #
597 #
598 # mercurial-3.6.1.data/scripts/hg
598 # mercurial-3.6.1.data/scripts/hg
599 # mercurial/__init__.py
599 # mercurial/__init__.py
600 #
600 #
601 # When installing wheels, the subdirectories of the "<pkg>.data"
601 # When installing wheels, the subdirectories of the "<pkg>.data"
602 # directory are translated to system local paths and files therein
602 # directory are translated to system local paths and files therein
603 # are copied in place. The mercurial/* files are installed into the
603 # are copied in place. The mercurial/* files are installed into the
604 # site-packages directory. However, the site-packages directory
604 # site-packages directory. However, the site-packages directory
605 # 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
606 # at wheel generation time what the installed site-packages directory
606 # at wheel generation time what the installed site-packages directory
607 # 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
608 # custom code to run during wheel installation. This all means that
608 # custom code to run during wheel installation. This all means that
609 # 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
610 # of looking in sys.path must do.
610 # of looking in sys.path must do.
611
611
612 if (os.path.splitdrive(self.install_dir)[0] !=
612 if (os.path.splitdrive(self.install_dir)[0] !=
613 os.path.splitdrive(self.install_lib)[0]):
613 os.path.splitdrive(self.install_lib)[0]):
614 # 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
615 # absolute path instead
615 # absolute path instead
616 libdir = self.install_lib
616 libdir = self.install_lib
617 else:
617 else:
618 common = os.path.commonprefix((self.install_dir, self.install_lib))
618 common = os.path.commonprefix((self.install_dir, self.install_lib))
619 rest = self.install_dir[len(common):]
619 rest = self.install_dir[len(common):]
620 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])
621
621
622 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
622 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
623
623
624 for outfile in self.outfiles:
624 for outfile in self.outfiles:
625 with open(outfile, 'rb') as fp:
625 with open(outfile, 'rb') as fp:
626 data = fp.read()
626 data = fp.read()
627
627
628 # skip binary files
628 # skip binary files
629 if b'\0' in data:
629 if b'\0' in data:
630 continue
630 continue
631
631
632 # During local installs, the shebang will be rewritten to the final
632 # During local installs, the shebang will be rewritten to the final
633 # install path. During wheel packaging, the shebang has a special
633 # install path. During wheel packaging, the shebang has a special
634 # value.
634 # value.
635 if data.startswith(b'#!python'):
635 if data.startswith(b'#!python'):
636 log.info('not rewriting @LIBDIR@ in %s because install path '
636 log.info('not rewriting @LIBDIR@ in %s because install path '
637 'not known' % outfile)
637 'not known' % outfile)
638 continue
638 continue
639
639
640 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
640 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
641 with open(outfile, 'wb') as fp:
641 with open(outfile, 'wb') as fp:
642 fp.write(data)
642 fp.write(data)
643
643
644 cmdclass = {'build': hgbuild,
644 cmdclass = {'build': hgbuild,
645 'build_mo': hgbuildmo,
645 'build_mo': hgbuildmo,
646 'build_ext': hgbuildext,
646 'build_ext': hgbuildext,
647 'build_py': hgbuildpy,
647 'build_py': hgbuildpy,
648 'build_scripts': hgbuildscripts,
648 'build_scripts': hgbuildscripts,
649 'build_hgextindex': buildhgextindex,
649 'build_hgextindex': buildhgextindex,
650 'install': hginstall,
650 'install': hginstall,
651 'install_lib': hginstalllib,
651 'install_lib': hginstalllib,
652 'install_scripts': hginstallscripts,
652 'install_scripts': hginstallscripts,
653 'build_hgexe': buildhgexe,
653 'build_hgexe': buildhgexe,
654 }
654 }
655
655
656 packages = ['mercurial',
656 packages = ['mercurial',
657 'mercurial.cext',
657 'mercurial.cext',
658 'mercurial.cffi',
658 'mercurial.cffi',
659 'mercurial.hgweb',
659 'mercurial.hgweb',
660 'mercurial.httpclient',
660 'mercurial.httpclient',
661 'mercurial.pure',
661 'mercurial.pure',
662 'hgext', 'hgext.convert', 'hgext.fsmonitor',
662 'hgext', 'hgext.convert', 'hgext.fsmonitor',
663 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
663 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
664 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
664 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
665 'hgdemandimport']
665 'hgdemandimport']
666
666
667 common_depends = ['mercurial/bitmanipulation.h',
667 common_depends = ['mercurial/bitmanipulation.h',
668 'mercurial/compat.h',
668 'mercurial/compat.h',
669 'mercurial/cext/util.h']
669 'mercurial/cext/util.h']
670 common_include_dirs = ['mercurial']
670 common_include_dirs = ['mercurial']
671
671
672 osutil_cflags = []
672 osutil_cflags = []
673 osutil_ldflags = []
673 osutil_ldflags = []
674
674
675 # platform specific macros
675 # platform specific macros
676 for plat, func in [('bsd', 'setproctitle')]:
676 for plat, func in [('bsd', 'setproctitle')]:
677 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
677 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
678 osutil_cflags.append('-DHAVE_%s' % func.upper())
678 osutil_cflags.append('-DHAVE_%s' % func.upper())
679
679
680 for plat, macro, code in [
680 for plat, macro, code in [
681 ('bsd|darwin', 'BSD_STATFS', '''
681 ('bsd|darwin', 'BSD_STATFS', '''
682 #include <sys/param.h>
682 #include <sys/param.h>
683 #include <sys/mount.h>
683 #include <sys/mount.h>
684 int main() { struct statfs s; return sizeof(s.f_fstypename); }
684 int main() { struct statfs s; return sizeof(s.f_fstypename); }
685 '''),
685 '''),
686 ('linux', 'LINUX_STATFS', '''
686 ('linux', 'LINUX_STATFS', '''
687 #include <linux/magic.h>
687 #include <linux/magic.h>
688 #include <sys/vfs.h>
688 #include <sys/vfs.h>
689 int main() { struct statfs s; return sizeof(s.f_type); }
689 int main() { struct statfs s; return sizeof(s.f_type); }
690 '''),
690 '''),
691 ]:
691 ]:
692 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
692 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
693 osutil_cflags.append('-DHAVE_%s' % macro)
693 osutil_cflags.append('-DHAVE_%s' % macro)
694
694
695 if sys.platform == 'darwin':
695 if sys.platform == 'darwin':
696 osutil_ldflags += ['-framework', 'ApplicationServices']
696 osutil_ldflags += ['-framework', 'ApplicationServices']
697
697
698 extmodules = [
698 extmodules = [
699 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
699 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
700 include_dirs=common_include_dirs,
700 include_dirs=common_include_dirs,
701 depends=common_depends),
701 depends=common_depends),
702 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
702 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
703 'mercurial/cext/bdiff.c'],
703 'mercurial/cext/bdiff.c'],
704 include_dirs=common_include_dirs,
704 include_dirs=common_include_dirs,
705 depends=common_depends + ['mercurial/bdiff.h']),
705 depends=common_depends + ['mercurial/bdiff.h']),
706 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
706 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
707 include_dirs=common_include_dirs,
707 include_dirs=common_include_dirs,
708 depends=common_depends),
708 depends=common_depends),
709 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
709 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
710 'mercurial/cext/mpatch.c'],
710 'mercurial/cext/mpatch.c'],
711 include_dirs=common_include_dirs,
711 include_dirs=common_include_dirs,
712 depends=common_depends),
712 depends=common_depends),
713 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
713 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
714 'mercurial/cext/manifest.c',
714 'mercurial/cext/manifest.c',
715 'mercurial/cext/parsers.c',
715 'mercurial/cext/parsers.c',
716 'mercurial/cext/pathencode.c',
716 'mercurial/cext/pathencode.c',
717 'mercurial/cext/revlog.c'],
717 'mercurial/cext/revlog.c'],
718 include_dirs=common_include_dirs,
718 include_dirs=common_include_dirs,
719 depends=common_depends),
719 depends=common_depends),
720 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
720 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
721 include_dirs=common_include_dirs,
721 include_dirs=common_include_dirs,
722 extra_compile_args=osutil_cflags,
722 extra_compile_args=osutil_cflags,
723 extra_link_args=osutil_ldflags,
723 extra_link_args=osutil_ldflags,
724 depends=common_depends),
724 depends=common_depends),
725 Extension('hgext.fsmonitor.pywatchman.bser',
725 Extension('hgext.fsmonitor.pywatchman.bser',
726 ['hgext/fsmonitor/pywatchman/bser.c']),
726 ['hgext/fsmonitor/pywatchman/bser.c']),
727 ]
727 ]
728
728
729 sys.path.insert(0, 'contrib/python-zstandard')
729 sys.path.insert(0, 'contrib/python-zstandard')
730 import setup_zstd
730 import setup_zstd
731 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
731 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
732
732
733 try:
733 try:
734 from distutils import cygwinccompiler
734 from distutils import cygwinccompiler
735
735
736 # the -mno-cygwin option has been deprecated for years
736 # the -mno-cygwin option has been deprecated for years
737 compiler = cygwinccompiler.Mingw32CCompiler
737 compiler = cygwinccompiler.Mingw32CCompiler
738
738
739 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
739 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
740 def __init__(self, *args, **kwargs):
740 def __init__(self, *args, **kwargs):
741 compiler.__init__(self, *args, **kwargs)
741 compiler.__init__(self, *args, **kwargs)
742 for i in 'compiler compiler_so linker_exe linker_so'.split():
742 for i in 'compiler compiler_so linker_exe linker_so'.split():
743 try:
743 try:
744 getattr(self, i).remove('-mno-cygwin')
744 getattr(self, i).remove('-mno-cygwin')
745 except ValueError:
745 except ValueError:
746 pass
746 pass
747
747
748 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
748 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
749 except ImportError:
749 except ImportError:
750 # the cygwinccompiler package is not available on some Python
750 # the cygwinccompiler package is not available on some Python
751 # distributions like the ones from the optware project for Synology
751 # distributions like the ones from the optware project for Synology
752 # DiskStation boxes
752 # DiskStation boxes
753 class HackedMingw32CCompiler(object):
753 class HackedMingw32CCompiler(object):
754 pass
754 pass
755
755
756 if os.name == 'nt':
756 if os.name == 'nt':
757 # 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
758 # extra_link_args to distutils.extensions.Extension() doesn't have any
758 # extra_link_args to distutils.extensions.Extension() doesn't have any
759 # effect.
759 # effect.
760 from distutils import msvccompiler
760 from distutils import msvccompiler
761
761
762 compiler = msvccompiler.MSVCCompiler
762 compiler = msvccompiler.MSVCCompiler
763
763
764 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
764 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
765 def initialize(self):
765 def initialize(self):
766 compiler.initialize(self)
766 compiler.initialize(self)
767 # "warning LNK4197: export 'func' specified multiple times"
767 # "warning LNK4197: export 'func' specified multiple times"
768 self.ldflags_shared.append('/ignore:4197')
768 self.ldflags_shared.append('/ignore:4197')
769 self.ldflags_shared_debug.append('/ignore:4197')
769 self.ldflags_shared_debug.append('/ignore:4197')
770
770
771 msvccompiler.MSVCCompiler = HackedMSVCCompiler
771 msvccompiler.MSVCCompiler = HackedMSVCCompiler
772
772
773 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
773 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
774 'help/*.txt',
774 'help/*.txt',
775 'help/internals/*.txt',
775 'help/internals/*.txt',
776 'default.d/*.rc',
776 'default.d/*.rc',
777 'dummycert.pem']}
777 'dummycert.pem']}
778
778
779 def ordinarypath(p):
779 def ordinarypath(p):
780 return p and p[0] != '.' and p[-1] != '~'
780 return p and p[0] != '.' and p[-1] != '~'
781
781
782 for root in ('templates',):
782 for root in ('templates',):
783 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)):
784 curdir = curdir.split(os.sep, 1)[1]
784 curdir = curdir.split(os.sep, 1)[1]
785 dirs[:] = filter(ordinarypath, dirs)
785 dirs[:] = filter(ordinarypath, dirs)
786 for f in filter(ordinarypath, files):
786 for f in filter(ordinarypath, files):
787 f = os.path.join(curdir, f)
787 f = os.path.join(curdir, f)
788 packagedata['mercurial'].append(f)
788 packagedata['mercurial'].append(f)
789
789
790 datafiles = []
790 datafiles = []
791
791
792 # distutils expects version to be str/unicode. Converting it to
792 # distutils expects version to be str/unicode. Converting it to
793 # 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
794 # non-ascii bytes and will be implicitly converted back to bytes
794 # non-ascii bytes and will be implicitly converted back to bytes
795 # when operated on.
795 # when operated on.
796 assert isinstance(version, bytes)
796 assert isinstance(version, bytes)
797 setupversion = version.decode('ascii')
797 setupversion = version.decode('ascii')
798
798
799 extra = {}
799 extra = {}
800
800
801 if py2exeloaded:
801 if py2exeloaded:
802 extra['console'] = [
802 extra['console'] = [
803 {'script':'hg',
803 {'script':'hg',
804 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
804 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
805 'product_version':version}]
805 'product_version':version}]
806 # sub command of 'build' because 'py2exe' does not handle sub_commands
806 # sub command of 'build' because 'py2exe' does not handle sub_commands
807 build.sub_commands.insert(0, ('build_hgextindex', None))
807 build.sub_commands.insert(0, ('build_hgextindex', None))
808 # 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
809 extra['zipfile'] = 'lib/library.zip'
809 extra['zipfile'] = 'lib/library.zip'
810
810
811 if os.name == 'nt':
811 if os.name == 'nt':
812 # Windows binary file versions for exe/dll files must have the
812 # Windows binary file versions for exe/dll files must have the
813 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
813 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
814 setupversion = version.split('+', 1)[0]
814 setupversion = version.split('+', 1)[0]
815
815
816 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
816 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
817 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
817 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
818 if version:
818 if version:
819 version = version[0]
819 version = version[0]
820 if sys.version_info[0] == 3:
820 if sys.version_info[0] == 3:
821 version = version.decode('utf-8')
821 version = version.decode('utf-8')
822 xcode4 = (version.startswith('Xcode') and
822 xcode4 = (version.startswith('Xcode') and
823 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
823 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
824 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
825 else:
825 else:
826 # 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
827 # installed, but instead with only command-line tools. Assume
827 # installed, but instead with only command-line tools. Assume
828 # that only happens on >= Lion, thus no PPC support.
828 # that only happens on >= Lion, thus no PPC support.
829 xcode4 = True
829 xcode4 = True
830 xcode51 = False
830 xcode51 = False
831
831
832 # 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
833 # distutils.sysconfig
833 # distutils.sysconfig
834 if xcode4:
834 if xcode4:
835 os.environ['ARCHFLAGS'] = ''
835 os.environ['ARCHFLAGS'] = ''
836
836
837 # 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
838 # -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
839 # 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
840 # C extension modules, and a bug has been filed upstream at
840 # C extension modules, and a bug has been filed upstream at
841 # 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
842 # so Mercurial can continue to compile in the meantime.
842 # so Mercurial can continue to compile in the meantime.
843 if xcode51:
843 if xcode51:
844 cflags = get_config_var('CFLAGS')
844 cflags = get_config_var('CFLAGS')
845 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:
846 os.environ['CFLAGS'] = (
846 os.environ['CFLAGS'] = (
847 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
847 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
848
848
849 setup(name='mercurial',
849 setup(name='mercurial',
850 version=setupversion,
850 version=setupversion,
851 author='Matt Mackall and many others',
851 author='Matt Mackall and many others',
852 author_email='mercurial@mercurial-scm.org',
852 author_email='mercurial@mercurial-scm.org',
853 url='https://mercurial-scm.org/',
853 url='https://mercurial-scm.org/',
854 download_url='https://mercurial-scm.org/release/',
854 download_url='https://mercurial-scm.org/release/',
855 description=('Fast scalable distributed SCM (revision control, version '
855 description=('Fast scalable distributed SCM (revision control, version '
856 'control) system'),
856 'control) system'),
857 long_description=('Mercurial is a distributed SCM tool written in Python.'
857 long_description=('Mercurial is a distributed SCM tool written in Python.'
858 ' It is used by a number of large projects that require'
858 ' It is used by a number of large projects that require'
859 ' fast, reliable distributed revision control, such as '
859 ' fast, reliable distributed revision control, such as '
860 'Mozilla.'),
860 'Mozilla.'),
861 license='GNU GPLv2 or any later version',
861 license='GNU GPLv2 or any later version',
862 classifiers=[
862 classifiers=[
863 'Development Status :: 6 - Mature',
863 'Development Status :: 6 - Mature',
864 'Environment :: Console',
864 'Environment :: Console',
865 'Intended Audience :: Developers',
865 'Intended Audience :: Developers',
866 'Intended Audience :: System Administrators',
866 'Intended Audience :: System Administrators',
867 'License :: OSI Approved :: GNU General Public License (GPL)',
867 'License :: OSI Approved :: GNU General Public License (GPL)',
868 'Natural Language :: Danish',
868 'Natural Language :: Danish',
869 'Natural Language :: English',
869 'Natural Language :: English',
870 'Natural Language :: German',
870 'Natural Language :: German',
871 'Natural Language :: Italian',
871 'Natural Language :: Italian',
872 'Natural Language :: Japanese',
872 'Natural Language :: Japanese',
873 'Natural Language :: Portuguese (Brazilian)',
873 'Natural Language :: Portuguese (Brazilian)',
874 'Operating System :: Microsoft :: Windows',
874 'Operating System :: Microsoft :: Windows',
875 'Operating System :: OS Independent',
875 'Operating System :: OS Independent',
876 'Operating System :: POSIX',
876 'Operating System :: POSIX',
877 'Programming Language :: C',
877 'Programming Language :: C',
878 'Programming Language :: Python',
878 'Programming Language :: Python',
879 'Topic :: Software Development :: Version Control',
879 'Topic :: Software Development :: Version Control',
880 ],
880 ],
881 scripts=scripts,
881 scripts=scripts,
882 packages=packages,
882 packages=packages,
883 ext_modules=extmodules,
883 ext_modules=extmodules,
884 data_files=datafiles,
884 data_files=datafiles,
885 package_data=packagedata,
885 package_data=packagedata,
886 cmdclass=cmdclass,
886 cmdclass=cmdclass,
887 distclass=hgdist,
887 distclass=hgdist,
888 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email']},
888 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email']},
889 'bdist_mpkg': {'zipdist': False,
889 'bdist_mpkg': {'zipdist': False,
890 'license': 'COPYING',
890 'license': 'COPYING',
891 'readme': 'contrib/macosx/Readme.html',
891 'readme': 'contrib/macosx/Readme.html',
892 'welcome': 'contrib/macosx/Welcome.html',
892 'welcome': 'contrib/macosx/Welcome.html',
893 },
893 },
894 },
894 },
895 **extra)
895 **extra)
General Comments 0
You need to be logged in to leave comments. Login now