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