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