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