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