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