##// END OF EJS Templates
cffi: put compiled modules into mercurial.cffi package...
Yuya Nishihara -
r32506:2dcb3d52 default
parent child Browse files
Show More
@@ -1,31 +1,31
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import cffi
3 import cffi
4 import os
4 import os
5
5
6 ffi = cffi.FFI()
6 ffi = cffi.FFI()
7 ffi.set_source("_bdiff_cffi",
7 ffi.set_source("mercurial.cffi._bdiff",
8 open(os.path.join(os.path.join(os.path.dirname(__file__), '..'),
8 open(os.path.join(os.path.join(os.path.dirname(__file__), '..'),
9 'bdiff.c')).read(), include_dirs=['mercurial'])
9 'bdiff.c')).read(), include_dirs=['mercurial'])
10 ffi.cdef("""
10 ffi.cdef("""
11 struct bdiff_line {
11 struct bdiff_line {
12 int hash, n, e;
12 int hash, n, e;
13 ssize_t len;
13 ssize_t len;
14 const char *l;
14 const char *l;
15 };
15 };
16
16
17 struct bdiff_hunk;
17 struct bdiff_hunk;
18 struct bdiff_hunk {
18 struct bdiff_hunk {
19 int a1, a2, b1, b2;
19 int a1, a2, b1, b2;
20 struct bdiff_hunk *next;
20 struct bdiff_hunk *next;
21 };
21 };
22
22
23 int bdiff_splitlines(const char *a, ssize_t len, struct bdiff_line **lr);
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,
24 int bdiff_diff(struct bdiff_line *a, int an, struct bdiff_line *b, int bn,
25 struct bdiff_hunk *base);
25 struct bdiff_hunk *base);
26 void bdiff_freehunks(struct bdiff_hunk *l);
26 void bdiff_freehunks(struct bdiff_hunk *l);
27 void free(void*);
27 void free(void*);
28 """)
28 """)
29
29
30 if __name__ == '__main__':
30 if __name__ == '__main__':
31 ffi.compile()
31 ffi.compile()
@@ -1,35 +1,35
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import cffi
3 import cffi
4 import os
4 import os
5
5
6 ffi = cffi.FFI()
6 ffi = cffi.FFI()
7 mpatch_c = os.path.join(os.path.join(os.path.dirname(__file__), '..',
7 mpatch_c = os.path.join(os.path.join(os.path.dirname(__file__), '..',
8 'mpatch.c'))
8 'mpatch.c'))
9 ffi.set_source("_mpatch_cffi", open(mpatch_c).read(),
9 ffi.set_source("mercurial.cffi._mpatch", open(mpatch_c).read(),
10 include_dirs=["mercurial"])
10 include_dirs=["mercurial"])
11 ffi.cdef("""
11 ffi.cdef("""
12
12
13 struct mpatch_frag {
13 struct mpatch_frag {
14 int start, end, len;
14 int start, end, len;
15 const char *data;
15 const char *data;
16 };
16 };
17
17
18 struct mpatch_flist {
18 struct mpatch_flist {
19 struct mpatch_frag *base, *head, *tail;
19 struct mpatch_frag *base, *head, *tail;
20 };
20 };
21
21
22 extern "Python" struct mpatch_flist* cffi_get_next_item(void*, ssize_t);
22 extern "Python" struct mpatch_flist* cffi_get_next_item(void*, ssize_t);
23
23
24 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist** res);
24 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist** res);
25 ssize_t mpatch_calcsize(size_t len, struct mpatch_flist *l);
25 ssize_t mpatch_calcsize(size_t len, struct mpatch_flist *l);
26 void mpatch_lfree(struct mpatch_flist *a);
26 void mpatch_lfree(struct mpatch_flist *a);
27 static int mpatch_apply(char *buf, const char *orig, size_t len,
27 static int mpatch_apply(char *buf, const char *orig, size_t len,
28 struct mpatch_flist *l);
28 struct mpatch_flist *l);
29 struct mpatch_flist *mpatch_fold(void *bins,
29 struct mpatch_flist *mpatch_fold(void *bins,
30 struct mpatch_flist* (*get_next_item)(void*, ssize_t),
30 struct mpatch_flist* (*get_next_item)(void*, ssize_t),
31 ssize_t start, ssize_t end);
31 ssize_t start, ssize_t end);
32 """)
32 """)
33
33
34 if __name__ == '__main__':
34 if __name__ == '__main__':
35 ffi.compile()
35 ffi.compile()
@@ -1,102 +1,102
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import cffi
3 import cffi
4
4
5 ffi = cffi.FFI()
5 ffi = cffi.FFI()
6 ffi.set_source("_osutil_cffi", """
6 ffi.set_source("mercurial.cffi._osutil", """
7 #include <sys/attr.h>
7 #include <sys/attr.h>
8 #include <sys/vnode.h>
8 #include <sys/vnode.h>
9 #include <unistd.h>
9 #include <unistd.h>
10 #include <fcntl.h>
10 #include <fcntl.h>
11 #include <time.h>
11 #include <time.h>
12
12
13 typedef struct val_attrs {
13 typedef struct val_attrs {
14 uint32_t length;
14 uint32_t length;
15 attribute_set_t returned;
15 attribute_set_t returned;
16 attrreference_t name_info;
16 attrreference_t name_info;
17 fsobj_type_t obj_type;
17 fsobj_type_t obj_type;
18 struct timespec mtime;
18 struct timespec mtime;
19 uint32_t accessmask;
19 uint32_t accessmask;
20 off_t datalength;
20 off_t datalength;
21 } __attribute__((aligned(4), packed)) val_attrs_t;
21 } __attribute__((aligned(4), packed)) val_attrs_t;
22 """, include_dirs=['mercurial'])
22 """, include_dirs=['mercurial'])
23 ffi.cdef('''
23 ffi.cdef('''
24
24
25 typedef uint32_t attrgroup_t;
25 typedef uint32_t attrgroup_t;
26
26
27 typedef struct attrlist {
27 typedef struct attrlist {
28 uint16_t bitmapcount; /* number of attr. bit sets in list */
28 uint16_t bitmapcount; /* number of attr. bit sets in list */
29 uint16_t reserved; /* (to maintain 4-byte alignment) */
29 uint16_t reserved; /* (to maintain 4-byte alignment) */
30 attrgroup_t commonattr; /* common attribute group */
30 attrgroup_t commonattr; /* common attribute group */
31 attrgroup_t volattr; /* volume attribute group */
31 attrgroup_t volattr; /* volume attribute group */
32 attrgroup_t dirattr; /* directory attribute group */
32 attrgroup_t dirattr; /* directory attribute group */
33 attrgroup_t fileattr; /* file attribute group */
33 attrgroup_t fileattr; /* file attribute group */
34 attrgroup_t forkattr; /* fork attribute group */
34 attrgroup_t forkattr; /* fork attribute group */
35 ...;
35 ...;
36 };
36 };
37
37
38 typedef struct attribute_set {
38 typedef struct attribute_set {
39 ...;
39 ...;
40 } attribute_set_t;
40 } attribute_set_t;
41
41
42 typedef struct attrreference {
42 typedef struct attrreference {
43 int attr_dataoffset;
43 int attr_dataoffset;
44 int attr_length;
44 int attr_length;
45 ...;
45 ...;
46 } attrreference_t;
46 } attrreference_t;
47
47
48 typedef int ... off_t;
48 typedef int ... off_t;
49
49
50 typedef struct val_attrs {
50 typedef struct val_attrs {
51 uint32_t length;
51 uint32_t length;
52 attribute_set_t returned;
52 attribute_set_t returned;
53 attrreference_t name_info;
53 attrreference_t name_info;
54 uint32_t obj_type;
54 uint32_t obj_type;
55 struct timespec mtime;
55 struct timespec mtime;
56 uint32_t accessmask;
56 uint32_t accessmask;
57 off_t datalength;
57 off_t datalength;
58 ...;
58 ...;
59 } val_attrs_t;
59 } val_attrs_t;
60
60
61 /* the exact layout of the above struct will be figured out during build time */
61 /* the exact layout of the above struct will be figured out during build time */
62
62
63 typedef int ... time_t;
63 typedef int ... time_t;
64
64
65 typedef struct timespec {
65 typedef struct timespec {
66 time_t tv_sec;
66 time_t tv_sec;
67 ...;
67 ...;
68 };
68 };
69
69
70 int getattrlist(const char* path, struct attrlist * attrList, void * attrBuf,
70 int getattrlist(const char* path, struct attrlist * attrList, void * attrBuf,
71 size_t attrBufSize, unsigned int options);
71 size_t attrBufSize, unsigned int options);
72
72
73 int getattrlistbulk(int dirfd, struct attrlist * attrList, void * attrBuf,
73 int getattrlistbulk(int dirfd, struct attrlist * attrList, void * attrBuf,
74 size_t attrBufSize, uint64_t options);
74 size_t attrBufSize, uint64_t options);
75
75
76 #define ATTR_BIT_MAP_COUNT ...
76 #define ATTR_BIT_MAP_COUNT ...
77 #define ATTR_CMN_NAME ...
77 #define ATTR_CMN_NAME ...
78 #define ATTR_CMN_OBJTYPE ...
78 #define ATTR_CMN_OBJTYPE ...
79 #define ATTR_CMN_MODTIME ...
79 #define ATTR_CMN_MODTIME ...
80 #define ATTR_CMN_ACCESSMASK ...
80 #define ATTR_CMN_ACCESSMASK ...
81 #define ATTR_CMN_ERROR ...
81 #define ATTR_CMN_ERROR ...
82 #define ATTR_CMN_RETURNED_ATTRS ...
82 #define ATTR_CMN_RETURNED_ATTRS ...
83 #define ATTR_FILE_DATALENGTH ...
83 #define ATTR_FILE_DATALENGTH ...
84
84
85 #define VREG ...
85 #define VREG ...
86 #define VDIR ...
86 #define VDIR ...
87 #define VLNK ...
87 #define VLNK ...
88 #define VBLK ...
88 #define VBLK ...
89 #define VCHR ...
89 #define VCHR ...
90 #define VFIFO ...
90 #define VFIFO ...
91 #define VSOCK ...
91 #define VSOCK ...
92
92
93 #define S_IFMT ...
93 #define S_IFMT ...
94
94
95 int open(const char *path, int oflag, int perm);
95 int open(const char *path, int oflag, int perm);
96 int close(int);
96 int close(int);
97
97
98 #define O_RDONLY ...
98 #define O_RDONLY ...
99 ''')
99 ''')
100
100
101 if __name__ == '__main__':
101 if __name__ == '__main__':
102 ffi.compile()
102 ffi.compile()
@@ -1,162 +1,162
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 difflib
10 import difflib
11 import re
11 import re
12 import struct
12 import struct
13
13
14 from .. import policy
14 from .. import policy
15 policynocffi = policy.policynocffi
15 policynocffi = policy.policynocffi
16 modulepolicy = policy.policy
16 modulepolicy = policy.policy
17
17
18 def splitnewlines(text):
18 def splitnewlines(text):
19 '''like str.splitlines, but only split on newlines.'''
19 '''like str.splitlines, but only split on newlines.'''
20 lines = [l + '\n' for l in text.split('\n')]
20 lines = [l + '\n' for l in text.split('\n')]
21 if lines:
21 if lines:
22 if lines[-1] == '\n':
22 if lines[-1] == '\n':
23 lines.pop()
23 lines.pop()
24 else:
24 else:
25 lines[-1] = lines[-1][:-1]
25 lines[-1] = lines[-1][:-1]
26 return lines
26 return lines
27
27
28 def _normalizeblocks(a, b, blocks):
28 def _normalizeblocks(a, b, blocks):
29 prev = None
29 prev = None
30 r = []
30 r = []
31 for curr in blocks:
31 for curr in blocks:
32 if prev is None:
32 if prev is None:
33 prev = curr
33 prev = curr
34 continue
34 continue
35 shift = 0
35 shift = 0
36
36
37 a1, b1, l1 = prev
37 a1, b1, l1 = prev
38 a1end = a1 + l1
38 a1end = a1 + l1
39 b1end = b1 + l1
39 b1end = b1 + l1
40
40
41 a2, b2, l2 = curr
41 a2, b2, l2 = curr
42 a2end = a2 + l2
42 a2end = a2 + l2
43 b2end = b2 + l2
43 b2end = b2 + l2
44 if a1end == a2:
44 if a1end == a2:
45 while (a1end + shift < a2end and
45 while (a1end + shift < a2end and
46 a[a1end + shift] == b[b1end + shift]):
46 a[a1end + shift] == b[b1end + shift]):
47 shift += 1
47 shift += 1
48 elif b1end == b2:
48 elif b1end == b2:
49 while (b1end + shift < b2end and
49 while (b1end + shift < b2end and
50 a[a1end + shift] == b[b1end + shift]):
50 a[a1end + shift] == b[b1end + shift]):
51 shift += 1
51 shift += 1
52 r.append((a1, b1, l1 + shift))
52 r.append((a1, b1, l1 + shift))
53 prev = a2 + shift, b2 + shift, l2 - shift
53 prev = a2 + shift, b2 + shift, l2 - shift
54 r.append(prev)
54 r.append(prev)
55 return r
55 return r
56
56
57 def bdiff(a, b):
57 def bdiff(a, b):
58 a = bytes(a).splitlines(True)
58 a = bytes(a).splitlines(True)
59 b = bytes(b).splitlines(True)
59 b = bytes(b).splitlines(True)
60
60
61 if not a:
61 if not a:
62 s = "".join(b)
62 s = "".join(b)
63 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
63 return s and (struct.pack(">lll", 0, 0, len(s)) + s)
64
64
65 bin = []
65 bin = []
66 p = [0]
66 p = [0]
67 for i in a: p.append(p[-1] + len(i))
67 for i in a: p.append(p[-1] + len(i))
68
68
69 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
69 d = difflib.SequenceMatcher(None, a, b).get_matching_blocks()
70 d = _normalizeblocks(a, b, d)
70 d = _normalizeblocks(a, b, d)
71 la = 0
71 la = 0
72 lb = 0
72 lb = 0
73 for am, bm, size in d:
73 for am, bm, size in d:
74 s = "".join(b[lb:bm])
74 s = "".join(b[lb:bm])
75 if am > la or s:
75 if am > la or s:
76 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
76 bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s)
77 la = am + size
77 la = am + size
78 lb = bm + size
78 lb = bm + size
79
79
80 return "".join(bin)
80 return "".join(bin)
81
81
82 def blocks(a, b):
82 def blocks(a, b):
83 an = splitnewlines(a)
83 an = splitnewlines(a)
84 bn = splitnewlines(b)
84 bn = splitnewlines(b)
85 d = difflib.SequenceMatcher(None, an, bn).get_matching_blocks()
85 d = difflib.SequenceMatcher(None, an, bn).get_matching_blocks()
86 d = _normalizeblocks(an, bn, d)
86 d = _normalizeblocks(an, bn, d)
87 return [(i, i + n, j, j + n) for (i, j, n) in d]
87 return [(i, i + n, j, j + n) for (i, j, n) in d]
88
88
89 def fixws(text, allws):
89 def fixws(text, allws):
90 if allws:
90 if allws:
91 text = re.sub('[ \t\r]+', '', text)
91 text = re.sub('[ \t\r]+', '', text)
92 else:
92 else:
93 text = re.sub('[ \t\r]+', ' ', text)
93 text = re.sub('[ \t\r]+', ' ', text)
94 text = text.replace(' \n', '\n')
94 text = text.replace(' \n', '\n')
95 return text
95 return text
96
96
97 if modulepolicy not in policynocffi:
97 if modulepolicy not in policynocffi:
98 try:
98 try:
99 from _bdiff_cffi import ffi, lib
99 from ..cffi._bdiff import ffi, lib
100 except ImportError:
100 except ImportError:
101 if modulepolicy == 'cffi': # strict cffi import
101 if modulepolicy == 'cffi': # strict cffi import
102 raise
102 raise
103 else:
103 else:
104 def blocks(sa, sb):
104 def blocks(sa, sb):
105 a = ffi.new("struct bdiff_line**")
105 a = ffi.new("struct bdiff_line**")
106 b = ffi.new("struct bdiff_line**")
106 b = ffi.new("struct bdiff_line**")
107 ac = ffi.new("char[]", str(sa))
107 ac = ffi.new("char[]", str(sa))
108 bc = ffi.new("char[]", str(sb))
108 bc = ffi.new("char[]", str(sb))
109 l = ffi.new("struct bdiff_hunk*")
109 l = ffi.new("struct bdiff_hunk*")
110 try:
110 try:
111 an = lib.bdiff_splitlines(ac, len(sa), a)
111 an = lib.bdiff_splitlines(ac, len(sa), a)
112 bn = lib.bdiff_splitlines(bc, len(sb), b)
112 bn = lib.bdiff_splitlines(bc, len(sb), b)
113 if not a[0] or not b[0]:
113 if not a[0] or not b[0]:
114 raise MemoryError
114 raise MemoryError
115 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
115 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
116 if count < 0:
116 if count < 0:
117 raise MemoryError
117 raise MemoryError
118 rl = [None] * count
118 rl = [None] * count
119 h = l.next
119 h = l.next
120 i = 0
120 i = 0
121 while h:
121 while h:
122 rl[i] = (h.a1, h.a2, h.b1, h.b2)
122 rl[i] = (h.a1, h.a2, h.b1, h.b2)
123 h = h.next
123 h = h.next
124 i += 1
124 i += 1
125 finally:
125 finally:
126 lib.free(a[0])
126 lib.free(a[0])
127 lib.free(b[0])
127 lib.free(b[0])
128 lib.bdiff_freehunks(l.next)
128 lib.bdiff_freehunks(l.next)
129 return rl
129 return rl
130
130
131 def bdiff(sa, sb):
131 def bdiff(sa, sb):
132 a = ffi.new("struct bdiff_line**")
132 a = ffi.new("struct bdiff_line**")
133 b = ffi.new("struct bdiff_line**")
133 b = ffi.new("struct bdiff_line**")
134 ac = ffi.new("char[]", str(sa))
134 ac = ffi.new("char[]", str(sa))
135 bc = ffi.new("char[]", str(sb))
135 bc = ffi.new("char[]", str(sb))
136 l = ffi.new("struct bdiff_hunk*")
136 l = ffi.new("struct bdiff_hunk*")
137 try:
137 try:
138 an = lib.bdiff_splitlines(ac, len(sa), a)
138 an = lib.bdiff_splitlines(ac, len(sa), a)
139 bn = lib.bdiff_splitlines(bc, len(sb), b)
139 bn = lib.bdiff_splitlines(bc, len(sb), b)
140 if not a[0] or not b[0]:
140 if not a[0] or not b[0]:
141 raise MemoryError
141 raise MemoryError
142 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
142 count = lib.bdiff_diff(a[0], an, b[0], bn, l)
143 if count < 0:
143 if count < 0:
144 raise MemoryError
144 raise MemoryError
145 rl = []
145 rl = []
146 h = l.next
146 h = l.next
147 la = lb = 0
147 la = lb = 0
148 while h:
148 while h:
149 if h.a1 != la or h.b1 != lb:
149 if h.a1 != la or h.b1 != lb:
150 lgt = (b[0] + h.b1).l - (b[0] + lb).l
150 lgt = (b[0] + h.b1).l - (b[0] + lb).l
151 rl.append(struct.pack(">lll", (a[0] + la).l - a[0].l,
151 rl.append(struct.pack(">lll", (a[0] + la).l - a[0].l,
152 (a[0] + h.a1).l - a[0].l, lgt))
152 (a[0] + h.a1).l - a[0].l, lgt))
153 rl.append(str(ffi.buffer((b[0] + lb).l, lgt)))
153 rl.append(str(ffi.buffer((b[0] + lb).l, lgt)))
154 la = h.a2
154 la = h.a2
155 lb = h.b2
155 lb = h.b2
156 h = h.next
156 h = h.next
157
157
158 finally:
158 finally:
159 lib.free(a[0])
159 lib.free(a[0])
160 lib.free(b[0])
160 lib.free(b[0])
161 lib.bdiff_freehunks(l.next)
161 lib.bdiff_freehunks(l.next)
162 return "".join(rl)
162 return "".join(rl)
@@ -1,169 +1,169
1 # mpatch.py - Python implementation of mpatch.c
1 # mpatch.py - Python implementation of mpatch.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 struct
10 import struct
11
11
12 from .. import policy, pycompat
12 from .. import policy, pycompat
13 stringio = pycompat.stringio
13 stringio = pycompat.stringio
14 modulepolicy = policy.policy
14 modulepolicy = policy.policy
15 policynocffi = policy.policynocffi
15 policynocffi = policy.policynocffi
16
16
17 class mpatchError(Exception):
17 class mpatchError(Exception):
18 """error raised when a delta cannot be decoded
18 """error raised when a delta cannot be decoded
19 """
19 """
20
20
21 # This attempts to apply a series of patches in time proportional to
21 # This attempts to apply a series of patches in time proportional to
22 # the total size of the patches, rather than patches * len(text). This
22 # the total size of the patches, rather than patches * len(text). This
23 # means rather than shuffling strings around, we shuffle around
23 # means rather than shuffling strings around, we shuffle around
24 # pointers to fragments with fragment lists.
24 # pointers to fragments with fragment lists.
25 #
25 #
26 # When the fragment lists get too long, we collapse them. To do this
26 # When the fragment lists get too long, we collapse them. To do this
27 # efficiently, we do all our operations inside a buffer created by
27 # efficiently, we do all our operations inside a buffer created by
28 # mmap and simply use memmove. This avoids creating a bunch of large
28 # mmap and simply use memmove. This avoids creating a bunch of large
29 # temporary string buffers.
29 # temporary string buffers.
30
30
31 def _pull(dst, src, l): # pull l bytes from src
31 def _pull(dst, src, l): # pull l bytes from src
32 while l:
32 while l:
33 f = src.pop()
33 f = src.pop()
34 if f[0] > l: # do we need to split?
34 if f[0] > l: # do we need to split?
35 src.append((f[0] - l, f[1] + l))
35 src.append((f[0] - l, f[1] + l))
36 dst.append((l, f[1]))
36 dst.append((l, f[1]))
37 return
37 return
38 dst.append(f)
38 dst.append(f)
39 l -= f[0]
39 l -= f[0]
40
40
41 def _move(m, dest, src, count):
41 def _move(m, dest, src, count):
42 """move count bytes from src to dest
42 """move count bytes from src to dest
43
43
44 The file pointer is left at the end of dest.
44 The file pointer is left at the end of dest.
45 """
45 """
46 m.seek(src)
46 m.seek(src)
47 buf = m.read(count)
47 buf = m.read(count)
48 m.seek(dest)
48 m.seek(dest)
49 m.write(buf)
49 m.write(buf)
50
50
51 def _collect(m, buf, list):
51 def _collect(m, buf, list):
52 start = buf
52 start = buf
53 for l, p in reversed(list):
53 for l, p in reversed(list):
54 _move(m, buf, p, l)
54 _move(m, buf, p, l)
55 buf += l
55 buf += l
56 return (buf - start, start)
56 return (buf - start, start)
57
57
58 def patches(a, bins):
58 def patches(a, bins):
59 if not bins:
59 if not bins:
60 return a
60 return a
61
61
62 plens = [len(x) for x in bins]
62 plens = [len(x) for x in bins]
63 pl = sum(plens)
63 pl = sum(plens)
64 bl = len(a) + pl
64 bl = len(a) + pl
65 tl = bl + bl + pl # enough for the patches and two working texts
65 tl = bl + bl + pl # enough for the patches and two working texts
66 b1, b2 = 0, bl
66 b1, b2 = 0, bl
67
67
68 if not tl:
68 if not tl:
69 return a
69 return a
70
70
71 m = stringio()
71 m = stringio()
72
72
73 # load our original text
73 # load our original text
74 m.write(a)
74 m.write(a)
75 frags = [(len(a), b1)]
75 frags = [(len(a), b1)]
76
76
77 # copy all the patches into our segment so we can memmove from them
77 # copy all the patches into our segment so we can memmove from them
78 pos = b2 + bl
78 pos = b2 + bl
79 m.seek(pos)
79 m.seek(pos)
80 for p in bins: m.write(p)
80 for p in bins: m.write(p)
81
81
82 for plen in plens:
82 for plen in plens:
83 # if our list gets too long, execute it
83 # if our list gets too long, execute it
84 if len(frags) > 128:
84 if len(frags) > 128:
85 b2, b1 = b1, b2
85 b2, b1 = b1, b2
86 frags = [_collect(m, b1, frags)]
86 frags = [_collect(m, b1, frags)]
87
87
88 new = []
88 new = []
89 end = pos + plen
89 end = pos + plen
90 last = 0
90 last = 0
91 while pos < end:
91 while pos < end:
92 m.seek(pos)
92 m.seek(pos)
93 try:
93 try:
94 p1, p2, l = struct.unpack(">lll", m.read(12))
94 p1, p2, l = struct.unpack(">lll", m.read(12))
95 except struct.error:
95 except struct.error:
96 raise mpatchError("patch cannot be decoded")
96 raise mpatchError("patch cannot be decoded")
97 _pull(new, frags, p1 - last) # what didn't change
97 _pull(new, frags, p1 - last) # what didn't change
98 _pull([], frags, p2 - p1) # what got deleted
98 _pull([], frags, p2 - p1) # what got deleted
99 new.append((l, pos + 12)) # what got added
99 new.append((l, pos + 12)) # what got added
100 pos += l + 12
100 pos += l + 12
101 last = p2
101 last = p2
102 frags.extend(reversed(new)) # what was left at the end
102 frags.extend(reversed(new)) # what was left at the end
103
103
104 t = _collect(m, b2, frags)
104 t = _collect(m, b2, frags)
105
105
106 m.seek(t[1])
106 m.seek(t[1])
107 return m.read(t[0])
107 return m.read(t[0])
108
108
109 def patchedsize(orig, delta):
109 def patchedsize(orig, delta):
110 outlen, last, bin = 0, 0, 0
110 outlen, last, bin = 0, 0, 0
111 binend = len(delta)
111 binend = len(delta)
112 data = 12
112 data = 12
113
113
114 while data <= binend:
114 while data <= binend:
115 decode = delta[bin:bin + 12]
115 decode = delta[bin:bin + 12]
116 start, end, length = struct.unpack(">lll", decode)
116 start, end, length = struct.unpack(">lll", decode)
117 if start > end:
117 if start > end:
118 break
118 break
119 bin = data + length
119 bin = data + length
120 data = bin + 12
120 data = bin + 12
121 outlen += start - last
121 outlen += start - last
122 last = end
122 last = end
123 outlen += length
123 outlen += length
124
124
125 if bin != binend:
125 if bin != binend:
126 raise mpatchError("patch cannot be decoded")
126 raise mpatchError("patch cannot be decoded")
127
127
128 outlen += orig - last
128 outlen += orig - last
129 return outlen
129 return outlen
130
130
131 if modulepolicy not in policynocffi:
131 if modulepolicy not in policynocffi:
132 try:
132 try:
133 from _mpatch_cffi import ffi, lib
133 from ..cffi._mpatch import ffi, lib
134 except ImportError:
134 except ImportError:
135 if modulepolicy == 'cffi': # strict cffi import
135 if modulepolicy == 'cffi': # strict cffi import
136 raise
136 raise
137 else:
137 else:
138 @ffi.def_extern()
138 @ffi.def_extern()
139 def cffi_get_next_item(arg, pos):
139 def cffi_get_next_item(arg, pos):
140 all, bins = ffi.from_handle(arg)
140 all, bins = ffi.from_handle(arg)
141 container = ffi.new("struct mpatch_flist*[1]")
141 container = ffi.new("struct mpatch_flist*[1]")
142 to_pass = ffi.new("char[]", str(bins[pos]))
142 to_pass = ffi.new("char[]", str(bins[pos]))
143 all.append(to_pass)
143 all.append(to_pass)
144 r = lib.mpatch_decode(to_pass, len(to_pass) - 1, container)
144 r = lib.mpatch_decode(to_pass, len(to_pass) - 1, container)
145 if r < 0:
145 if r < 0:
146 return ffi.NULL
146 return ffi.NULL
147 return container[0]
147 return container[0]
148
148
149 def patches(text, bins):
149 def patches(text, bins):
150 lgt = len(bins)
150 lgt = len(bins)
151 all = []
151 all = []
152 if not lgt:
152 if not lgt:
153 return text
153 return text
154 arg = (all, bins)
154 arg = (all, bins)
155 patch = lib.mpatch_fold(ffi.new_handle(arg),
155 patch = lib.mpatch_fold(ffi.new_handle(arg),
156 lib.cffi_get_next_item, 0, lgt)
156 lib.cffi_get_next_item, 0, lgt)
157 if not patch:
157 if not patch:
158 raise mpatchError("cannot decode chunk")
158 raise mpatchError("cannot decode chunk")
159 outlen = lib.mpatch_calcsize(len(text), patch)
159 outlen = lib.mpatch_calcsize(len(text), patch)
160 if outlen < 0:
160 if outlen < 0:
161 lib.mpatch_lfree(patch)
161 lib.mpatch_lfree(patch)
162 raise mpatchError("inconsistency detected")
162 raise mpatchError("inconsistency detected")
163 buf = ffi.new("char[]", outlen)
163 buf = ffi.new("char[]", outlen)
164 if lib.mpatch_apply(buf, text, len(text), patch) < 0:
164 if lib.mpatch_apply(buf, text, len(text), patch) < 0:
165 lib.mpatch_lfree(patch)
165 lib.mpatch_lfree(patch)
166 raise mpatchError("error applying patches")
166 raise mpatchError("error applying patches")
167 res = ffi.buffer(buf, outlen)[:]
167 res = ffi.buffer(buf, outlen)[:]
168 lib.mpatch_lfree(patch)
168 lib.mpatch_lfree(patch)
169 return res
169 return res
@@ -1,365 +1,365
1 # osutil.py - pure Python version of osutil.c
1 # osutil.py - pure Python version of osutil.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 ctypes
10 import ctypes
11 import ctypes.util
11 import ctypes.util
12 import os
12 import os
13 import socket
13 import socket
14 import stat as statmod
14 import stat as statmod
15
15
16 from .. import (
16 from .. import (
17 policy,
17 policy,
18 pycompat,
18 pycompat,
19 )
19 )
20
20
21 modulepolicy = policy.policy
21 modulepolicy = policy.policy
22 policynocffi = policy.policynocffi
22 policynocffi = policy.policynocffi
23
23
24 def _mode_to_kind(mode):
24 def _mode_to_kind(mode):
25 if statmod.S_ISREG(mode):
25 if statmod.S_ISREG(mode):
26 return statmod.S_IFREG
26 return statmod.S_IFREG
27 if statmod.S_ISDIR(mode):
27 if statmod.S_ISDIR(mode):
28 return statmod.S_IFDIR
28 return statmod.S_IFDIR
29 if statmod.S_ISLNK(mode):
29 if statmod.S_ISLNK(mode):
30 return statmod.S_IFLNK
30 return statmod.S_IFLNK
31 if statmod.S_ISBLK(mode):
31 if statmod.S_ISBLK(mode):
32 return statmod.S_IFBLK
32 return statmod.S_IFBLK
33 if statmod.S_ISCHR(mode):
33 if statmod.S_ISCHR(mode):
34 return statmod.S_IFCHR
34 return statmod.S_IFCHR
35 if statmod.S_ISFIFO(mode):
35 if statmod.S_ISFIFO(mode):
36 return statmod.S_IFIFO
36 return statmod.S_IFIFO
37 if statmod.S_ISSOCK(mode):
37 if statmod.S_ISSOCK(mode):
38 return statmod.S_IFSOCK
38 return statmod.S_IFSOCK
39 return mode
39 return mode
40
40
41 def listdirpure(path, stat=False, skip=None):
41 def listdirpure(path, stat=False, skip=None):
42 '''listdir(path, stat=False) -> list_of_tuples
42 '''listdir(path, stat=False) -> list_of_tuples
43
43
44 Return a sorted list containing information about the entries
44 Return a sorted list containing information about the entries
45 in the directory.
45 in the directory.
46
46
47 If stat is True, each element is a 3-tuple:
47 If stat is True, each element is a 3-tuple:
48
48
49 (name, type, stat object)
49 (name, type, stat object)
50
50
51 Otherwise, each element is a 2-tuple:
51 Otherwise, each element is a 2-tuple:
52
52
53 (name, type)
53 (name, type)
54 '''
54 '''
55 result = []
55 result = []
56 prefix = path
56 prefix = path
57 if not prefix.endswith(pycompat.ossep):
57 if not prefix.endswith(pycompat.ossep):
58 prefix += pycompat.ossep
58 prefix += pycompat.ossep
59 names = os.listdir(path)
59 names = os.listdir(path)
60 names.sort()
60 names.sort()
61 for fn in names:
61 for fn in names:
62 st = os.lstat(prefix + fn)
62 st = os.lstat(prefix + fn)
63 if fn == skip and statmod.S_ISDIR(st.st_mode):
63 if fn == skip and statmod.S_ISDIR(st.st_mode):
64 return []
64 return []
65 if stat:
65 if stat:
66 result.append((fn, _mode_to_kind(st.st_mode), st))
66 result.append((fn, _mode_to_kind(st.st_mode), st))
67 else:
67 else:
68 result.append((fn, _mode_to_kind(st.st_mode)))
68 result.append((fn, _mode_to_kind(st.st_mode)))
69 return result
69 return result
70
70
71 ffi = None
71 ffi = None
72 if modulepolicy not in policynocffi and pycompat.sysplatform == 'darwin':
72 if modulepolicy not in policynocffi and pycompat.sysplatform == 'darwin':
73 try:
73 try:
74 from _osutil_cffi import ffi, lib
74 from ..cffi._osutil import ffi, lib
75 except ImportError:
75 except ImportError:
76 if modulepolicy == 'cffi': # strict cffi import
76 if modulepolicy == 'cffi': # strict cffi import
77 raise
77 raise
78
78
79 if pycompat.sysplatform == 'darwin' and ffi is not None:
79 if pycompat.sysplatform == 'darwin' and ffi is not None:
80 listdir_batch_size = 4096
80 listdir_batch_size = 4096
81 # tweakable number, only affects performance, which chunks
81 # tweakable number, only affects performance, which chunks
82 # of bytes do we get back from getattrlistbulk
82 # of bytes do we get back from getattrlistbulk
83
83
84 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty
84 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty
85
85
86 attrkinds[lib.VREG] = statmod.S_IFREG
86 attrkinds[lib.VREG] = statmod.S_IFREG
87 attrkinds[lib.VDIR] = statmod.S_IFDIR
87 attrkinds[lib.VDIR] = statmod.S_IFDIR
88 attrkinds[lib.VLNK] = statmod.S_IFLNK
88 attrkinds[lib.VLNK] = statmod.S_IFLNK
89 attrkinds[lib.VBLK] = statmod.S_IFBLK
89 attrkinds[lib.VBLK] = statmod.S_IFBLK
90 attrkinds[lib.VCHR] = statmod.S_IFCHR
90 attrkinds[lib.VCHR] = statmod.S_IFCHR
91 attrkinds[lib.VFIFO] = statmod.S_IFIFO
91 attrkinds[lib.VFIFO] = statmod.S_IFIFO
92 attrkinds[lib.VSOCK] = statmod.S_IFSOCK
92 attrkinds[lib.VSOCK] = statmod.S_IFSOCK
93
93
94 class stat_res(object):
94 class stat_res(object):
95 def __init__(self, st_mode, st_mtime, st_size):
95 def __init__(self, st_mode, st_mtime, st_size):
96 self.st_mode = st_mode
96 self.st_mode = st_mode
97 self.st_mtime = st_mtime
97 self.st_mtime = st_mtime
98 self.st_size = st_size
98 self.st_size = st_size
99
99
100 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
100 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
101 buf = ffi.new("char[]", listdir_batch_size)
101 buf = ffi.new("char[]", listdir_batch_size)
102
102
103 def listdirinternal(dfd, req, stat, skip):
103 def listdirinternal(dfd, req, stat, skip):
104 ret = []
104 ret = []
105 while True:
105 while True:
106 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
106 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
107 if r == 0:
107 if r == 0:
108 break
108 break
109 if r == -1:
109 if r == -1:
110 raise OSError(ffi.errno, os.strerror(ffi.errno))
110 raise OSError(ffi.errno, os.strerror(ffi.errno))
111 cur = ffi.cast("val_attrs_t*", buf)
111 cur = ffi.cast("val_attrs_t*", buf)
112 for i in range(r):
112 for i in range(r):
113 lgt = cur.length
113 lgt = cur.length
114 assert lgt == ffi.cast('uint32_t*', cur)[0]
114 assert lgt == ffi.cast('uint32_t*', cur)[0]
115 ofs = cur.name_info.attr_dataoffset
115 ofs = cur.name_info.attr_dataoffset
116 str_lgt = cur.name_info.attr_length
116 str_lgt = cur.name_info.attr_length
117 base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
117 base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
118 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
118 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
119 str_lgt - 1))
119 str_lgt - 1))
120 tp = attrkinds[cur.obj_type]
120 tp = attrkinds[cur.obj_type]
121 if name == "." or name == "..":
121 if name == "." or name == "..":
122 continue
122 continue
123 if skip == name and tp == statmod.S_ISDIR:
123 if skip == name and tp == statmod.S_ISDIR:
124 return []
124 return []
125 if stat:
125 if stat:
126 mtime = cur.mtime.tv_sec
126 mtime = cur.mtime.tv_sec
127 mode = (cur.accessmask & ~lib.S_IFMT)| tp
127 mode = (cur.accessmask & ~lib.S_IFMT)| tp
128 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
128 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
129 st_size=cur.datalength)))
129 st_size=cur.datalength)))
130 else:
130 else:
131 ret.append((name, tp))
131 ret.append((name, tp))
132 cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur))
132 cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur))
133 + lgt)
133 + lgt)
134 return ret
134 return ret
135
135
136 def listdir(path, stat=False, skip=None):
136 def listdir(path, stat=False, skip=None):
137 req = ffi.new("struct attrlist*")
137 req = ffi.new("struct attrlist*")
138 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
138 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
139 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
139 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
140 lib.ATTR_CMN_NAME |
140 lib.ATTR_CMN_NAME |
141 lib.ATTR_CMN_OBJTYPE |
141 lib.ATTR_CMN_OBJTYPE |
142 lib.ATTR_CMN_ACCESSMASK |
142 lib.ATTR_CMN_ACCESSMASK |
143 lib.ATTR_CMN_MODTIME)
143 lib.ATTR_CMN_MODTIME)
144 req.fileattr = lib.ATTR_FILE_DATALENGTH
144 req.fileattr = lib.ATTR_FILE_DATALENGTH
145 dfd = lib.open(path, lib.O_RDONLY, 0)
145 dfd = lib.open(path, lib.O_RDONLY, 0)
146 if dfd == -1:
146 if dfd == -1:
147 raise OSError(ffi.errno, os.strerror(ffi.errno))
147 raise OSError(ffi.errno, os.strerror(ffi.errno))
148
148
149 try:
149 try:
150 ret = listdirinternal(dfd, req, stat, skip)
150 ret = listdirinternal(dfd, req, stat, skip)
151 finally:
151 finally:
152 try:
152 try:
153 lib.close(dfd)
153 lib.close(dfd)
154 except BaseException:
154 except BaseException:
155 pass # we ignore all the errors from closing, not
155 pass # we ignore all the errors from closing, not
156 # much we can do about that
156 # much we can do about that
157 return ret
157 return ret
158 else:
158 else:
159 listdir = listdirpure
159 listdir = listdirpure
160
160
161 if pycompat.osname != 'nt':
161 if pycompat.osname != 'nt':
162 posixfile = open
162 posixfile = open
163
163
164 _SCM_RIGHTS = 0x01
164 _SCM_RIGHTS = 0x01
165 _socklen_t = ctypes.c_uint
165 _socklen_t = ctypes.c_uint
166
166
167 if pycompat.sysplatform.startswith('linux'):
167 if pycompat.sysplatform.startswith('linux'):
168 # socket.h says "the type should be socklen_t but the definition of
168 # socket.h says "the type should be socklen_t but the definition of
169 # the kernel is incompatible with this."
169 # the kernel is incompatible with this."
170 _cmsg_len_t = ctypes.c_size_t
170 _cmsg_len_t = ctypes.c_size_t
171 _msg_controllen_t = ctypes.c_size_t
171 _msg_controllen_t = ctypes.c_size_t
172 _msg_iovlen_t = ctypes.c_size_t
172 _msg_iovlen_t = ctypes.c_size_t
173 else:
173 else:
174 _cmsg_len_t = _socklen_t
174 _cmsg_len_t = _socklen_t
175 _msg_controllen_t = _socklen_t
175 _msg_controllen_t = _socklen_t
176 _msg_iovlen_t = ctypes.c_int
176 _msg_iovlen_t = ctypes.c_int
177
177
178 class _iovec(ctypes.Structure):
178 class _iovec(ctypes.Structure):
179 _fields_ = [
179 _fields_ = [
180 (u'iov_base', ctypes.c_void_p),
180 (u'iov_base', ctypes.c_void_p),
181 (u'iov_len', ctypes.c_size_t),
181 (u'iov_len', ctypes.c_size_t),
182 ]
182 ]
183
183
184 class _msghdr(ctypes.Structure):
184 class _msghdr(ctypes.Structure):
185 _fields_ = [
185 _fields_ = [
186 (u'msg_name', ctypes.c_void_p),
186 (u'msg_name', ctypes.c_void_p),
187 (u'msg_namelen', _socklen_t),
187 (u'msg_namelen', _socklen_t),
188 (u'msg_iov', ctypes.POINTER(_iovec)),
188 (u'msg_iov', ctypes.POINTER(_iovec)),
189 (u'msg_iovlen', _msg_iovlen_t),
189 (u'msg_iovlen', _msg_iovlen_t),
190 (u'msg_control', ctypes.c_void_p),
190 (u'msg_control', ctypes.c_void_p),
191 (u'msg_controllen', _msg_controllen_t),
191 (u'msg_controllen', _msg_controllen_t),
192 (u'msg_flags', ctypes.c_int),
192 (u'msg_flags', ctypes.c_int),
193 ]
193 ]
194
194
195 class _cmsghdr(ctypes.Structure):
195 class _cmsghdr(ctypes.Structure):
196 _fields_ = [
196 _fields_ = [
197 (u'cmsg_len', _cmsg_len_t),
197 (u'cmsg_len', _cmsg_len_t),
198 (u'cmsg_level', ctypes.c_int),
198 (u'cmsg_level', ctypes.c_int),
199 (u'cmsg_type', ctypes.c_int),
199 (u'cmsg_type', ctypes.c_int),
200 (u'cmsg_data', ctypes.c_ubyte * 0),
200 (u'cmsg_data', ctypes.c_ubyte * 0),
201 ]
201 ]
202
202
203 _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
203 _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
204 _recvmsg = getattr(_libc, 'recvmsg', None)
204 _recvmsg = getattr(_libc, 'recvmsg', None)
205 if _recvmsg:
205 if _recvmsg:
206 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
206 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
207 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
207 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
208 ctypes.c_int)
208 ctypes.c_int)
209 else:
209 else:
210 # recvmsg isn't always provided by libc; such systems are unsupported
210 # recvmsg isn't always provided by libc; such systems are unsupported
211 def _recvmsg(sockfd, msg, flags):
211 def _recvmsg(sockfd, msg, flags):
212 raise NotImplementedError('unsupported platform')
212 raise NotImplementedError('unsupported platform')
213
213
214 def _CMSG_FIRSTHDR(msgh):
214 def _CMSG_FIRSTHDR(msgh):
215 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
215 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
216 return
216 return
217 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
217 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
218 return cmsgptr.contents
218 return cmsgptr.contents
219
219
220 # The pure version is less portable than the native version because the
220 # The pure version is less portable than the native version because the
221 # handling of socket ancillary data heavily depends on C preprocessor.
221 # handling of socket ancillary data heavily depends on C preprocessor.
222 # Also, some length fields are wrongly typed in Linux kernel.
222 # Also, some length fields are wrongly typed in Linux kernel.
223 def recvfds(sockfd):
223 def recvfds(sockfd):
224 """receive list of file descriptors via socket"""
224 """receive list of file descriptors via socket"""
225 dummy = (ctypes.c_ubyte * 1)()
225 dummy = (ctypes.c_ubyte * 1)()
226 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
226 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
227 cbuf = ctypes.create_string_buffer(256)
227 cbuf = ctypes.create_string_buffer(256)
228 msgh = _msghdr(None, 0,
228 msgh = _msghdr(None, 0,
229 ctypes.pointer(iov), 1,
229 ctypes.pointer(iov), 1,
230 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
230 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
231 0)
231 0)
232 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
232 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
233 if r < 0:
233 if r < 0:
234 e = ctypes.get_errno()
234 e = ctypes.get_errno()
235 raise OSError(e, os.strerror(e))
235 raise OSError(e, os.strerror(e))
236 # assumes that the first cmsg has fds because it isn't easy to write
236 # assumes that the first cmsg has fds because it isn't easy to write
237 # portable CMSG_NXTHDR() with ctypes.
237 # portable CMSG_NXTHDR() with ctypes.
238 cmsg = _CMSG_FIRSTHDR(msgh)
238 cmsg = _CMSG_FIRSTHDR(msgh)
239 if not cmsg:
239 if not cmsg:
240 return []
240 return []
241 if (cmsg.cmsg_level != socket.SOL_SOCKET or
241 if (cmsg.cmsg_level != socket.SOL_SOCKET or
242 cmsg.cmsg_type != _SCM_RIGHTS):
242 cmsg.cmsg_type != _SCM_RIGHTS):
243 return []
243 return []
244 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
244 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
245 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
245 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
246 ctypes.sizeof(ctypes.c_int))
246 ctypes.sizeof(ctypes.c_int))
247 return [rfds[i] for i in xrange(rfdscount)]
247 return [rfds[i] for i in xrange(rfdscount)]
248
248
249 else:
249 else:
250 import msvcrt
250 import msvcrt
251
251
252 _kernel32 = ctypes.windll.kernel32
252 _kernel32 = ctypes.windll.kernel32
253
253
254 _DWORD = ctypes.c_ulong
254 _DWORD = ctypes.c_ulong
255 _LPCSTR = _LPSTR = ctypes.c_char_p
255 _LPCSTR = _LPSTR = ctypes.c_char_p
256 _HANDLE = ctypes.c_void_p
256 _HANDLE = ctypes.c_void_p
257
257
258 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
258 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
259
259
260 # CreateFile
260 # CreateFile
261 _FILE_SHARE_READ = 0x00000001
261 _FILE_SHARE_READ = 0x00000001
262 _FILE_SHARE_WRITE = 0x00000002
262 _FILE_SHARE_WRITE = 0x00000002
263 _FILE_SHARE_DELETE = 0x00000004
263 _FILE_SHARE_DELETE = 0x00000004
264
264
265 _CREATE_ALWAYS = 2
265 _CREATE_ALWAYS = 2
266 _OPEN_EXISTING = 3
266 _OPEN_EXISTING = 3
267 _OPEN_ALWAYS = 4
267 _OPEN_ALWAYS = 4
268
268
269 _GENERIC_READ = 0x80000000
269 _GENERIC_READ = 0x80000000
270 _GENERIC_WRITE = 0x40000000
270 _GENERIC_WRITE = 0x40000000
271
271
272 _FILE_ATTRIBUTE_NORMAL = 0x80
272 _FILE_ATTRIBUTE_NORMAL = 0x80
273
273
274 # open_osfhandle flags
274 # open_osfhandle flags
275 _O_RDONLY = 0x0000
275 _O_RDONLY = 0x0000
276 _O_RDWR = 0x0002
276 _O_RDWR = 0x0002
277 _O_APPEND = 0x0008
277 _O_APPEND = 0x0008
278
278
279 _O_TEXT = 0x4000
279 _O_TEXT = 0x4000
280 _O_BINARY = 0x8000
280 _O_BINARY = 0x8000
281
281
282 # types of parameters of C functions used (required by pypy)
282 # types of parameters of C functions used (required by pypy)
283
283
284 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
284 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
285 _DWORD, _DWORD, _HANDLE]
285 _DWORD, _DWORD, _HANDLE]
286 _kernel32.CreateFileA.restype = _HANDLE
286 _kernel32.CreateFileA.restype = _HANDLE
287
287
288 def _raiseioerror(name):
288 def _raiseioerror(name):
289 err = ctypes.WinError()
289 err = ctypes.WinError()
290 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
290 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
291
291
292 class posixfile(object):
292 class posixfile(object):
293 '''a file object aiming for POSIX-like semantics
293 '''a file object aiming for POSIX-like semantics
294
294
295 CPython's open() returns a file that was opened *without* setting the
295 CPython's open() returns a file that was opened *without* setting the
296 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
296 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
297 This even happens if any hardlinked copy of the file is in open state.
297 This even happens if any hardlinked copy of the file is in open state.
298 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
298 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
299 renamed and deleted while they are held open.
299 renamed and deleted while they are held open.
300 Note that if a file opened with posixfile is unlinked, the file
300 Note that if a file opened with posixfile is unlinked, the file
301 remains but cannot be opened again or be recreated under the same name,
301 remains but cannot be opened again or be recreated under the same name,
302 until all reading processes have closed the file.'''
302 until all reading processes have closed the file.'''
303
303
304 def __init__(self, name, mode='r', bufsize=-1):
304 def __init__(self, name, mode='r', bufsize=-1):
305 if 'b' in mode:
305 if 'b' in mode:
306 flags = _O_BINARY
306 flags = _O_BINARY
307 else:
307 else:
308 flags = _O_TEXT
308 flags = _O_TEXT
309
309
310 m0 = mode[0]
310 m0 = mode[0]
311 if m0 == 'r' and '+' not in mode:
311 if m0 == 'r' and '+' not in mode:
312 flags |= _O_RDONLY
312 flags |= _O_RDONLY
313 access = _GENERIC_READ
313 access = _GENERIC_READ
314 else:
314 else:
315 # work around http://support.microsoft.com/kb/899149 and
315 # work around http://support.microsoft.com/kb/899149 and
316 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
316 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
317 flags |= _O_RDWR
317 flags |= _O_RDWR
318 access = _GENERIC_READ | _GENERIC_WRITE
318 access = _GENERIC_READ | _GENERIC_WRITE
319
319
320 if m0 == 'r':
320 if m0 == 'r':
321 creation = _OPEN_EXISTING
321 creation = _OPEN_EXISTING
322 elif m0 == 'w':
322 elif m0 == 'w':
323 creation = _CREATE_ALWAYS
323 creation = _CREATE_ALWAYS
324 elif m0 == 'a':
324 elif m0 == 'a':
325 creation = _OPEN_ALWAYS
325 creation = _OPEN_ALWAYS
326 flags |= _O_APPEND
326 flags |= _O_APPEND
327 else:
327 else:
328 raise ValueError("invalid mode: %s" % mode)
328 raise ValueError("invalid mode: %s" % mode)
329
329
330 fh = _kernel32.CreateFileA(name, access,
330 fh = _kernel32.CreateFileA(name, access,
331 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
331 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
332 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
332 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
333 if fh == _INVALID_HANDLE_VALUE:
333 if fh == _INVALID_HANDLE_VALUE:
334 _raiseioerror(name)
334 _raiseioerror(name)
335
335
336 fd = msvcrt.open_osfhandle(fh, flags)
336 fd = msvcrt.open_osfhandle(fh, flags)
337 if fd == -1:
337 if fd == -1:
338 _kernel32.CloseHandle(fh)
338 _kernel32.CloseHandle(fh)
339 _raiseioerror(name)
339 _raiseioerror(name)
340
340
341 f = os.fdopen(fd, pycompat.sysstr(mode), bufsize)
341 f = os.fdopen(fd, pycompat.sysstr(mode), bufsize)
342 # unfortunately, f.name is '<fdopen>' at this point -- so we store
342 # unfortunately, f.name is '<fdopen>' at this point -- so we store
343 # the name on this wrapper. We cannot just assign to f.name,
343 # the name on this wrapper. We cannot just assign to f.name,
344 # because that attribute is read-only.
344 # because that attribute is read-only.
345 object.__setattr__(self, r'name', name)
345 object.__setattr__(self, r'name', name)
346 object.__setattr__(self, r'_file', f)
346 object.__setattr__(self, r'_file', f)
347
347
348 def __iter__(self):
348 def __iter__(self):
349 return self._file
349 return self._file
350
350
351 def __getattr__(self, name):
351 def __getattr__(self, name):
352 return getattr(self._file, name)
352 return getattr(self._file, name)
353
353
354 def __setattr__(self, name, value):
354 def __setattr__(self, name, value):
355 '''mimics the read-only attributes of Python file objects
355 '''mimics the read-only attributes of Python file objects
356 by raising 'TypeError: readonly attribute' if someone tries:
356 by raising 'TypeError: readonly attribute' if someone tries:
357 f = posixfile('foo.txt')
357 f = posixfile('foo.txt')
358 f.name = 'bla' '''
358 f.name = 'bla' '''
359 return self._file.__setattr__(name, value)
359 return self._file.__setattr__(name, value)
360
360
361 def __enter__(self):
361 def __enter__(self):
362 return self._file.__enter__()
362 return self._file.__enter__()
363
363
364 def __exit__(self, exc_type, exc_value, exc_tb):
364 def __exit__(self, exc_type, exc_value, exc_tb):
365 return self._file.__exit__(exc_type, exc_value, exc_tb)
365 return self._file.__exit__(exc_type, exc_value, exc_tb)
@@ -1,804 +1,805
1 #
1 #
2 # This is the mercurial setup script.
2 # This is the mercurial setup script.
3 #
3 #
4 # 'python setup.py install', or
4 # 'python setup.py install', or
5 # 'python setup.py --help' for more options
5 # 'python setup.py --help' for more options
6
6
7 import sys, platform
7 import sys, platform
8 if sys.version_info < (2, 7, 0, 'final'):
8 if sys.version_info < (2, 7, 0, 'final'):
9 raise SystemExit('Mercurial requires Python 2.7 or later.')
9 raise SystemExit('Mercurial requires Python 2.7 or later.')
10
10
11 if sys.version_info[0] >= 3:
11 if sys.version_info[0] >= 3:
12 printf = eval('print')
12 printf = eval('print')
13 libdir_escape = 'unicode_escape'
13 libdir_escape = 'unicode_escape'
14 else:
14 else:
15 libdir_escape = 'string_escape'
15 libdir_escape = 'string_escape'
16 def printf(*args, **kwargs):
16 def printf(*args, **kwargs):
17 f = kwargs.get('file', sys.stdout)
17 f = kwargs.get('file', sys.stdout)
18 end = kwargs.get('end', '\n')
18 end = kwargs.get('end', '\n')
19 f.write(b' '.join(args) + end)
19 f.write(b' '.join(args) + end)
20
20
21 # Solaris Python packaging brain damage
21 # Solaris Python packaging brain damage
22 try:
22 try:
23 import hashlib
23 import hashlib
24 sha = hashlib.sha1()
24 sha = hashlib.sha1()
25 except ImportError:
25 except ImportError:
26 try:
26 try:
27 import sha
27 import sha
28 sha.sha # silence unused import warning
28 sha.sha # silence unused import warning
29 except ImportError:
29 except ImportError:
30 raise SystemExit(
30 raise SystemExit(
31 "Couldn't import standard hashlib (incomplete Python install).")
31 "Couldn't import standard hashlib (incomplete Python install).")
32
32
33 try:
33 try:
34 import zlib
34 import zlib
35 zlib.compressobj # silence unused import warning
35 zlib.compressobj # silence unused import warning
36 except ImportError:
36 except ImportError:
37 raise SystemExit(
37 raise SystemExit(
38 "Couldn't import standard zlib (incomplete Python install).")
38 "Couldn't import standard zlib (incomplete Python install).")
39
39
40 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
40 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
41 isironpython = False
41 isironpython = False
42 try:
42 try:
43 isironpython = (platform.python_implementation()
43 isironpython = (platform.python_implementation()
44 .lower().find("ironpython") != -1)
44 .lower().find("ironpython") != -1)
45 except AttributeError:
45 except AttributeError:
46 pass
46 pass
47
47
48 if isironpython:
48 if isironpython:
49 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
49 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
50 else:
50 else:
51 try:
51 try:
52 import bz2
52 import bz2
53 bz2.BZ2Compressor # silence unused import warning
53 bz2.BZ2Compressor # silence unused import warning
54 except ImportError:
54 except ImportError:
55 raise SystemExit(
55 raise SystemExit(
56 "Couldn't import standard bz2 (incomplete Python install).")
56 "Couldn't import standard bz2 (incomplete Python install).")
57
57
58 ispypy = "PyPy" in sys.version
58 ispypy = "PyPy" in sys.version
59
59
60 import ctypes
60 import ctypes
61 import os, stat, subprocess, time
61 import os, stat, subprocess, time
62 import re
62 import re
63 import shutil
63 import shutil
64 import tempfile
64 import tempfile
65 from distutils import log
65 from distutils import log
66 # We have issues with setuptools on some platforms and builders. Until
66 # We have issues with setuptools on some platforms and builders. Until
67 # those are resolved, setuptools is opt-in except for platforms where
67 # those are resolved, setuptools is opt-in except for platforms where
68 # we don't have issues.
68 # we don't have issues.
69 if os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ:
69 if os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ:
70 from setuptools import setup
70 from setuptools import setup
71 else:
71 else:
72 from distutils.core import setup
72 from distutils.core import setup
73 from distutils.ccompiler import new_compiler
73 from distutils.ccompiler import new_compiler
74 from distutils.core import Command, Extension
74 from distutils.core import Command, Extension
75 from distutils.dist import Distribution
75 from distutils.dist import Distribution
76 from distutils.command.build import build
76 from distutils.command.build import build
77 from distutils.command.build_ext import build_ext
77 from distutils.command.build_ext import build_ext
78 from distutils.command.build_py import build_py
78 from distutils.command.build_py import build_py
79 from distutils.command.build_scripts import build_scripts
79 from distutils.command.build_scripts import build_scripts
80 from distutils.command.install_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 bdiffbuild,
356 bdiffbuild,
357 mpatchbuild,
357 mpatchbuild,
358 )
358 )
359 exts = [mpatchbuild.ffi.distutils_extension(),
359 exts = [mpatchbuild.ffi.distutils_extension(),
360 bdiffbuild.ffi.distutils_extension()]
360 bdiffbuild.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 osutilbuild
363 from mercurial.cffi import osutilbuild
364 exts.append(osutilbuild.ffi.distutils_extension())
364 exts.append(osutilbuild.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 elif self.build_lib == '.':
375 elif self.build_lib == '.':
376 # in-place build should run without rebuilding C extensions
376 # in-place build should run without rebuilding C extensions
377 modulepolicy = 'allow'
377 modulepolicy = 'allow'
378 else:
378 else:
379 modulepolicy = 'c'
379 modulepolicy = 'c'
380 with open("mercurial/__modulepolicy__.py", "w") as f:
380 with open("mercurial/__modulepolicy__.py", "w") as f:
381 f.write('# this file is autogenerated by setup.py\n')
381 f.write('# this file is autogenerated by setup.py\n')
382 f.write('modulepolicy = b"%s"\n' % modulepolicy)
382 f.write('modulepolicy = b"%s"\n' % modulepolicy)
383
383
384 build_py.run(self)
384 build_py.run(self)
385
385
386 class buildhgextindex(Command):
386 class buildhgextindex(Command):
387 description = 'generate prebuilt index of hgext (for frozen package)'
387 description = 'generate prebuilt index of hgext (for frozen package)'
388 user_options = []
388 user_options = []
389 _indexfilename = 'hgext/__index__.py'
389 _indexfilename = 'hgext/__index__.py'
390
390
391 def initialize_options(self):
391 def initialize_options(self):
392 pass
392 pass
393
393
394 def finalize_options(self):
394 def finalize_options(self):
395 pass
395 pass
396
396
397 def run(self):
397 def run(self):
398 if os.path.exists(self._indexfilename):
398 if os.path.exists(self._indexfilename):
399 with open(self._indexfilename, 'w') as f:
399 with open(self._indexfilename, 'w') as f:
400 f.write('# empty\n')
400 f.write('# empty\n')
401
401
402 # here no extension enabled, disabled() lists up everything
402 # here no extension enabled, disabled() lists up everything
403 code = ('import pprint; from mercurial import extensions; '
403 code = ('import pprint; from mercurial import extensions; '
404 'pprint.pprint(extensions.disabled())')
404 'pprint.pprint(extensions.disabled())')
405 out, err = runcmd([sys.executable, '-c', code], env)
405 out, err = runcmd([sys.executable, '-c', code], env)
406 if err:
406 if err:
407 raise DistutilsExecError(err)
407 raise DistutilsExecError(err)
408
408
409 with open(self._indexfilename, 'w') as f:
409 with open(self._indexfilename, 'w') as f:
410 f.write('# this file is autogenerated by setup.py\n')
410 f.write('# this file is autogenerated by setup.py\n')
411 f.write('docs = ')
411 f.write('docs = ')
412 f.write(out)
412 f.write(out)
413
413
414 class buildhgexe(build_ext):
414 class buildhgexe(build_ext):
415 description = 'compile hg.exe from mercurial/exewrapper.c'
415 description = 'compile hg.exe from mercurial/exewrapper.c'
416
416
417 def build_extensions(self):
417 def build_extensions(self):
418 if os.name != 'nt':
418 if os.name != 'nt':
419 return
419 return
420 if isinstance(self.compiler, HackedMingw32CCompiler):
420 if isinstance(self.compiler, HackedMingw32CCompiler):
421 self.compiler.compiler_so = self.compiler.compiler # no -mdll
421 self.compiler.compiler_so = self.compiler.compiler # no -mdll
422 self.compiler.dll_libraries = [] # no -lmsrvc90
422 self.compiler.dll_libraries = [] # no -lmsrvc90
423
423
424 # Different Python installs can have different Python library
424 # Different Python installs can have different Python library
425 # names. e.g. the official CPython distribution uses pythonXY.dll
425 # names. e.g. the official CPython distribution uses pythonXY.dll
426 # and MinGW uses libpythonX.Y.dll.
426 # and MinGW uses libpythonX.Y.dll.
427 _kernel32 = ctypes.windll.kernel32
427 _kernel32 = ctypes.windll.kernel32
428 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
428 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
429 ctypes.c_void_p,
429 ctypes.c_void_p,
430 ctypes.c_ulong]
430 ctypes.c_ulong]
431 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
431 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
432 size = 1000
432 size = 1000
433 buf = ctypes.create_string_buffer(size + 1)
433 buf = ctypes.create_string_buffer(size + 1)
434 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
434 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
435 size)
435 size)
436
436
437 if filelen > 0 and filelen != size:
437 if filelen > 0 and filelen != size:
438 dllbasename = os.path.basename(buf.value)
438 dllbasename = os.path.basename(buf.value)
439 if not dllbasename.lower().endswith('.dll'):
439 if not dllbasename.lower().endswith('.dll'):
440 raise SystemExit('Python DLL does not end with .dll: %s' %
440 raise SystemExit('Python DLL does not end with .dll: %s' %
441 dllbasename)
441 dllbasename)
442 pythonlib = dllbasename[:-4]
442 pythonlib = dllbasename[:-4]
443 else:
443 else:
444 log.warn('could not determine Python DLL filename; '
444 log.warn('could not determine Python DLL filename; '
445 'assuming pythonXY')
445 'assuming pythonXY')
446
446
447 hv = sys.hexversion
447 hv = sys.hexversion
448 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
448 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
449
449
450 log.info('using %s as Python library name' % pythonlib)
450 log.info('using %s as Python library name' % pythonlib)
451 with open('mercurial/hgpythonlib.h', 'wb') as f:
451 with open('mercurial/hgpythonlib.h', 'wb') as f:
452 f.write('/* this file is autogenerated by setup.py */\n')
452 f.write('/* this file is autogenerated by setup.py */\n')
453 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
453 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
454 objects = self.compiler.compile(['mercurial/exewrapper.c'],
454 objects = self.compiler.compile(['mercurial/exewrapper.c'],
455 output_dir=self.build_temp)
455 output_dir=self.build_temp)
456 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
456 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
457 target = os.path.join(dir, 'hg')
457 target = os.path.join(dir, 'hg')
458 self.compiler.link_executable(objects, target,
458 self.compiler.link_executable(objects, target,
459 libraries=[],
459 libraries=[],
460 output_dir=self.build_temp)
460 output_dir=self.build_temp)
461
461
462 @property
462 @property
463 def hgexepath(self):
463 def hgexepath(self):
464 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
464 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
465 return os.path.join(self.build_temp, dir, 'hg.exe')
465 return os.path.join(self.build_temp, dir, 'hg.exe')
466
466
467 class hginstalllib(install_lib):
467 class hginstalllib(install_lib):
468 '''
468 '''
469 This is a specialization of install_lib that replaces the copy_file used
469 This is a specialization of install_lib that replaces the copy_file used
470 there so that it supports setting the mode of files after copying them,
470 there so that it supports setting the mode of files after copying them,
471 instead of just preserving the mode that the files originally had. If your
471 instead of just preserving the mode that the files originally had. If your
472 system has a umask of something like 027, preserving the permissions when
472 system has a umask of something like 027, preserving the permissions when
473 copying will lead to a broken install.
473 copying will lead to a broken install.
474
474
475 Note that just passing keep_permissions=False to copy_file would be
475 Note that just passing keep_permissions=False to copy_file would be
476 insufficient, as it might still be applying a umask.
476 insufficient, as it might still be applying a umask.
477 '''
477 '''
478
478
479 def run(self):
479 def run(self):
480 realcopyfile = file_util.copy_file
480 realcopyfile = file_util.copy_file
481 def copyfileandsetmode(*args, **kwargs):
481 def copyfileandsetmode(*args, **kwargs):
482 src, dst = args[0], args[1]
482 src, dst = args[0], args[1]
483 dst, copied = realcopyfile(*args, **kwargs)
483 dst, copied = realcopyfile(*args, **kwargs)
484 if copied:
484 if copied:
485 st = os.stat(src)
485 st = os.stat(src)
486 # Persist executable bit (apply it to group and other if user
486 # Persist executable bit (apply it to group and other if user
487 # has it)
487 # has it)
488 if st[stat.ST_MODE] & stat.S_IXUSR:
488 if st[stat.ST_MODE] & stat.S_IXUSR:
489 setmode = int('0755', 8)
489 setmode = int('0755', 8)
490 else:
490 else:
491 setmode = int('0644', 8)
491 setmode = int('0644', 8)
492 m = stat.S_IMODE(st[stat.ST_MODE])
492 m = stat.S_IMODE(st[stat.ST_MODE])
493 m = (m & ~int('0777', 8)) | setmode
493 m = (m & ~int('0777', 8)) | setmode
494 os.chmod(dst, m)
494 os.chmod(dst, m)
495 file_util.copy_file = copyfileandsetmode
495 file_util.copy_file = copyfileandsetmode
496 try:
496 try:
497 install_lib.run(self)
497 install_lib.run(self)
498 finally:
498 finally:
499 file_util.copy_file = realcopyfile
499 file_util.copy_file = realcopyfile
500
500
501 class hginstallscripts(install_scripts):
501 class hginstallscripts(install_scripts):
502 '''
502 '''
503 This is a specialization of install_scripts that replaces the @LIBDIR@ with
503 This is a specialization of install_scripts that replaces the @LIBDIR@ with
504 the configured directory for modules. If possible, the path is made relative
504 the configured directory for modules. If possible, the path is made relative
505 to the directory for scripts.
505 to the directory for scripts.
506 '''
506 '''
507
507
508 def initialize_options(self):
508 def initialize_options(self):
509 install_scripts.initialize_options(self)
509 install_scripts.initialize_options(self)
510
510
511 self.install_lib = None
511 self.install_lib = None
512
512
513 def finalize_options(self):
513 def finalize_options(self):
514 install_scripts.finalize_options(self)
514 install_scripts.finalize_options(self)
515 self.set_undefined_options('install',
515 self.set_undefined_options('install',
516 ('install_lib', 'install_lib'))
516 ('install_lib', 'install_lib'))
517
517
518 def run(self):
518 def run(self):
519 install_scripts.run(self)
519 install_scripts.run(self)
520
520
521 # It only makes sense to replace @LIBDIR@ with the install path if
521 # It only makes sense to replace @LIBDIR@ with the install path if
522 # the install path is known. For wheels, the logic below calculates
522 # the install path is known. For wheels, the logic below calculates
523 # the libdir to be "../..". This is because the internal layout of a
523 # the libdir to be "../..". This is because the internal layout of a
524 # wheel archive looks like:
524 # wheel archive looks like:
525 #
525 #
526 # mercurial-3.6.1.data/scripts/hg
526 # mercurial-3.6.1.data/scripts/hg
527 # mercurial/__init__.py
527 # mercurial/__init__.py
528 #
528 #
529 # When installing wheels, the subdirectories of the "<pkg>.data"
529 # When installing wheels, the subdirectories of the "<pkg>.data"
530 # directory are translated to system local paths and files therein
530 # directory are translated to system local paths and files therein
531 # are copied in place. The mercurial/* files are installed into the
531 # are copied in place. The mercurial/* files are installed into the
532 # site-packages directory. However, the site-packages directory
532 # site-packages directory. However, the site-packages directory
533 # isn't known until wheel install time. This means we have no clue
533 # isn't known until wheel install time. This means we have no clue
534 # at wheel generation time what the installed site-packages directory
534 # at wheel generation time what the installed site-packages directory
535 # will be. And, wheels don't appear to provide the ability to register
535 # will be. And, wheels don't appear to provide the ability to register
536 # custom code to run during wheel installation. This all means that
536 # custom code to run during wheel installation. This all means that
537 # we can't reliably set the libdir in wheels: the default behavior
537 # we can't reliably set the libdir in wheels: the default behavior
538 # of looking in sys.path must do.
538 # of looking in sys.path must do.
539
539
540 if (os.path.splitdrive(self.install_dir)[0] !=
540 if (os.path.splitdrive(self.install_dir)[0] !=
541 os.path.splitdrive(self.install_lib)[0]):
541 os.path.splitdrive(self.install_lib)[0]):
542 # can't make relative paths from one drive to another, so use an
542 # can't make relative paths from one drive to another, so use an
543 # absolute path instead
543 # absolute path instead
544 libdir = self.install_lib
544 libdir = self.install_lib
545 else:
545 else:
546 common = os.path.commonprefix((self.install_dir, self.install_lib))
546 common = os.path.commonprefix((self.install_dir, self.install_lib))
547 rest = self.install_dir[len(common):]
547 rest = self.install_dir[len(common):]
548 uplevel = len([n for n in os.path.split(rest) if n])
548 uplevel = len([n for n in os.path.split(rest) if n])
549
549
550 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
550 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
551
551
552 for outfile in self.outfiles:
552 for outfile in self.outfiles:
553 with open(outfile, 'rb') as fp:
553 with open(outfile, 'rb') as fp:
554 data = fp.read()
554 data = fp.read()
555
555
556 # skip binary files
556 # skip binary files
557 if b'\0' in data:
557 if b'\0' in data:
558 continue
558 continue
559
559
560 # During local installs, the shebang will be rewritten to the final
560 # During local installs, the shebang will be rewritten to the final
561 # install path. During wheel packaging, the shebang has a special
561 # install path. During wheel packaging, the shebang has a special
562 # value.
562 # value.
563 if data.startswith(b'#!python'):
563 if data.startswith(b'#!python'):
564 log.info('not rewriting @LIBDIR@ in %s because install path '
564 log.info('not rewriting @LIBDIR@ in %s because install path '
565 'not known' % outfile)
565 'not known' % outfile)
566 continue
566 continue
567
567
568 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
568 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
569 with open(outfile, 'wb') as fp:
569 with open(outfile, 'wb') as fp:
570 fp.write(data)
570 fp.write(data)
571
571
572 cmdclass = {'build': hgbuild,
572 cmdclass = {'build': hgbuild,
573 'build_mo': hgbuildmo,
573 'build_mo': hgbuildmo,
574 'build_ext': hgbuildext,
574 'build_ext': hgbuildext,
575 'build_py': hgbuildpy,
575 'build_py': hgbuildpy,
576 'build_scripts': hgbuildscripts,
576 'build_scripts': hgbuildscripts,
577 'build_hgextindex': buildhgextindex,
577 'build_hgextindex': buildhgextindex,
578 'install_lib': hginstalllib,
578 'install_lib': hginstalllib,
579 'install_scripts': hginstallscripts,
579 'install_scripts': hginstallscripts,
580 'build_hgexe': buildhgexe,
580 'build_hgexe': buildhgexe,
581 }
581 }
582
582
583 packages = ['mercurial',
583 packages = ['mercurial',
584 'mercurial.cext',
584 'mercurial.cext',
585 'mercurial.cffi',
585 'mercurial.hgweb',
586 'mercurial.hgweb',
586 'mercurial.httpclient',
587 'mercurial.httpclient',
587 'mercurial.pure',
588 'mercurial.pure',
588 'hgext', 'hgext.convert', 'hgext.fsmonitor',
589 'hgext', 'hgext.convert', 'hgext.fsmonitor',
589 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
590 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
590 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
591 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
591 'hgdemandimport']
592 'hgdemandimport']
592
593
593 common_depends = ['mercurial/bitmanipulation.h',
594 common_depends = ['mercurial/bitmanipulation.h',
594 'mercurial/compat.h',
595 'mercurial/compat.h',
595 'mercurial/cext/util.h']
596 'mercurial/cext/util.h']
596 common_include_dirs = ['mercurial']
597 common_include_dirs = ['mercurial']
597
598
598 osutil_cflags = []
599 osutil_cflags = []
599 osutil_ldflags = []
600 osutil_ldflags = []
600
601
601 # platform specific macros
602 # platform specific macros
602 for plat, func in [('bsd', 'setproctitle')]:
603 for plat, func in [('bsd', 'setproctitle')]:
603 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
604 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
604 osutil_cflags.append('-DHAVE_%s' % func.upper())
605 osutil_cflags.append('-DHAVE_%s' % func.upper())
605
606
606 for plat, macro, code in [
607 for plat, macro, code in [
607 ('bsd|darwin', 'BSD_STATFS', '''
608 ('bsd|darwin', 'BSD_STATFS', '''
608 #include <sys/param.h>
609 #include <sys/param.h>
609 #include <sys/mount.h>
610 #include <sys/mount.h>
610 int main() { struct statfs s; return sizeof(s.f_fstypename); }
611 int main() { struct statfs s; return sizeof(s.f_fstypename); }
611 '''),
612 '''),
612 ('linux', 'LINUX_STATFS', '''
613 ('linux', 'LINUX_STATFS', '''
613 #include <linux/magic.h>
614 #include <linux/magic.h>
614 #include <sys/vfs.h>
615 #include <sys/vfs.h>
615 int main() { struct statfs s; return sizeof(s.f_type); }
616 int main() { struct statfs s; return sizeof(s.f_type); }
616 '''),
617 '''),
617 ]:
618 ]:
618 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
619 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
619 osutil_cflags.append('-DHAVE_%s' % macro)
620 osutil_cflags.append('-DHAVE_%s' % macro)
620
621
621 if sys.platform == 'darwin':
622 if sys.platform == 'darwin':
622 osutil_ldflags += ['-framework', 'ApplicationServices']
623 osutil_ldflags += ['-framework', 'ApplicationServices']
623
624
624 extmodules = [
625 extmodules = [
625 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
626 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
626 include_dirs=common_include_dirs,
627 include_dirs=common_include_dirs,
627 depends=common_depends),
628 depends=common_depends),
628 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
629 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
629 'mercurial/cext/bdiff.c'],
630 'mercurial/cext/bdiff.c'],
630 include_dirs=common_include_dirs,
631 include_dirs=common_include_dirs,
631 depends=common_depends + ['mercurial/bdiff.h']),
632 depends=common_depends + ['mercurial/bdiff.h']),
632 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
633 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
633 include_dirs=common_include_dirs,
634 include_dirs=common_include_dirs,
634 depends=common_depends),
635 depends=common_depends),
635 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
636 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
636 'mercurial/cext/mpatch.c'],
637 'mercurial/cext/mpatch.c'],
637 include_dirs=common_include_dirs,
638 include_dirs=common_include_dirs,
638 depends=common_depends),
639 depends=common_depends),
639 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
640 Extension('mercurial.cext.parsers', ['mercurial/cext/dirs.c',
640 'mercurial/cext/manifest.c',
641 'mercurial/cext/manifest.c',
641 'mercurial/cext/parsers.c',
642 'mercurial/cext/parsers.c',
642 'mercurial/cext/pathencode.c',
643 'mercurial/cext/pathencode.c',
643 'mercurial/cext/revlog.c'],
644 'mercurial/cext/revlog.c'],
644 include_dirs=common_include_dirs,
645 include_dirs=common_include_dirs,
645 depends=common_depends),
646 depends=common_depends),
646 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
647 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
647 include_dirs=common_include_dirs,
648 include_dirs=common_include_dirs,
648 extra_compile_args=osutil_cflags,
649 extra_compile_args=osutil_cflags,
649 extra_link_args=osutil_ldflags,
650 extra_link_args=osutil_ldflags,
650 depends=common_depends),
651 depends=common_depends),
651 Extension('hgext.fsmonitor.pywatchman.bser',
652 Extension('hgext.fsmonitor.pywatchman.bser',
652 ['hgext/fsmonitor/pywatchman/bser.c']),
653 ['hgext/fsmonitor/pywatchman/bser.c']),
653 ]
654 ]
654
655
655 sys.path.insert(0, 'contrib/python-zstandard')
656 sys.path.insert(0, 'contrib/python-zstandard')
656 import setup_zstd
657 import setup_zstd
657 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
658 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
658
659
659 try:
660 try:
660 from distutils import cygwinccompiler
661 from distutils import cygwinccompiler
661
662
662 # the -mno-cygwin option has been deprecated for years
663 # the -mno-cygwin option has been deprecated for years
663 compiler = cygwinccompiler.Mingw32CCompiler
664 compiler = cygwinccompiler.Mingw32CCompiler
664
665
665 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
666 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
666 def __init__(self, *args, **kwargs):
667 def __init__(self, *args, **kwargs):
667 compiler.__init__(self, *args, **kwargs)
668 compiler.__init__(self, *args, **kwargs)
668 for i in 'compiler compiler_so linker_exe linker_so'.split():
669 for i in 'compiler compiler_so linker_exe linker_so'.split():
669 try:
670 try:
670 getattr(self, i).remove('-mno-cygwin')
671 getattr(self, i).remove('-mno-cygwin')
671 except ValueError:
672 except ValueError:
672 pass
673 pass
673
674
674 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
675 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
675 except ImportError:
676 except ImportError:
676 # the cygwinccompiler package is not available on some Python
677 # the cygwinccompiler package is not available on some Python
677 # distributions like the ones from the optware project for Synology
678 # distributions like the ones from the optware project for Synology
678 # DiskStation boxes
679 # DiskStation boxes
679 class HackedMingw32CCompiler(object):
680 class HackedMingw32CCompiler(object):
680 pass
681 pass
681
682
682 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
683 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
683 'help/*.txt',
684 'help/*.txt',
684 'help/internals/*.txt',
685 'help/internals/*.txt',
685 'default.d/*.rc',
686 'default.d/*.rc',
686 'dummycert.pem']}
687 'dummycert.pem']}
687
688
688 def ordinarypath(p):
689 def ordinarypath(p):
689 return p and p[0] != '.' and p[-1] != '~'
690 return p and p[0] != '.' and p[-1] != '~'
690
691
691 for root in ('templates',):
692 for root in ('templates',):
692 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
693 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
693 curdir = curdir.split(os.sep, 1)[1]
694 curdir = curdir.split(os.sep, 1)[1]
694 dirs[:] = filter(ordinarypath, dirs)
695 dirs[:] = filter(ordinarypath, dirs)
695 for f in filter(ordinarypath, files):
696 for f in filter(ordinarypath, files):
696 f = os.path.join(curdir, f)
697 f = os.path.join(curdir, f)
697 packagedata['mercurial'].append(f)
698 packagedata['mercurial'].append(f)
698
699
699 datafiles = []
700 datafiles = []
700
701
701 # distutils expects version to be str/unicode. Converting it to
702 # distutils expects version to be str/unicode. Converting it to
702 # unicode on Python 2 still works because it won't contain any
703 # unicode on Python 2 still works because it won't contain any
703 # non-ascii bytes and will be implicitly converted back to bytes
704 # non-ascii bytes and will be implicitly converted back to bytes
704 # when operated on.
705 # when operated on.
705 assert isinstance(version, bytes)
706 assert isinstance(version, bytes)
706 setupversion = version.decode('ascii')
707 setupversion = version.decode('ascii')
707
708
708 extra = {}
709 extra = {}
709
710
710 if py2exeloaded:
711 if py2exeloaded:
711 extra['console'] = [
712 extra['console'] = [
712 {'script':'hg',
713 {'script':'hg',
713 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
714 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
714 'product_version':version}]
715 'product_version':version}]
715 # sub command of 'build' because 'py2exe' does not handle sub_commands
716 # sub command of 'build' because 'py2exe' does not handle sub_commands
716 build.sub_commands.insert(0, ('build_hgextindex', None))
717 build.sub_commands.insert(0, ('build_hgextindex', None))
717 # put dlls in sub directory so that they won't pollute PATH
718 # put dlls in sub directory so that they won't pollute PATH
718 extra['zipfile'] = 'lib/library.zip'
719 extra['zipfile'] = 'lib/library.zip'
719
720
720 if os.name == 'nt':
721 if os.name == 'nt':
721 # Windows binary file versions for exe/dll files must have the
722 # Windows binary file versions for exe/dll files must have the
722 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
723 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
723 setupversion = version.split('+', 1)[0]
724 setupversion = version.split('+', 1)[0]
724
725
725 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
726 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
726 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
727 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
727 if version:
728 if version:
728 version = version[0]
729 version = version[0]
729 if sys.version_info[0] == 3:
730 if sys.version_info[0] == 3:
730 version = version.decode('utf-8')
731 version = version.decode('utf-8')
731 xcode4 = (version.startswith('Xcode') and
732 xcode4 = (version.startswith('Xcode') and
732 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
733 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
733 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
734 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
734 else:
735 else:
735 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
736 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
736 # installed, but instead with only command-line tools. Assume
737 # installed, but instead with only command-line tools. Assume
737 # that only happens on >= Lion, thus no PPC support.
738 # that only happens on >= Lion, thus no PPC support.
738 xcode4 = True
739 xcode4 = True
739 xcode51 = False
740 xcode51 = False
740
741
741 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
742 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
742 # distutils.sysconfig
743 # distutils.sysconfig
743 if xcode4:
744 if xcode4:
744 os.environ['ARCHFLAGS'] = ''
745 os.environ['ARCHFLAGS'] = ''
745
746
746 # XCode 5.1 changes clang such that it now fails to compile if the
747 # XCode 5.1 changes clang such that it now fails to compile if the
747 # -mno-fused-madd flag is passed, but the version of Python shipped with
748 # -mno-fused-madd flag is passed, but the version of Python shipped with
748 # OS X 10.9 Mavericks includes this flag. This causes problems in all
749 # OS X 10.9 Mavericks includes this flag. This causes problems in all
749 # C extension modules, and a bug has been filed upstream at
750 # C extension modules, and a bug has been filed upstream at
750 # http://bugs.python.org/issue21244. We also need to patch this here
751 # http://bugs.python.org/issue21244. We also need to patch this here
751 # so Mercurial can continue to compile in the meantime.
752 # so Mercurial can continue to compile in the meantime.
752 if xcode51:
753 if xcode51:
753 cflags = get_config_var('CFLAGS')
754 cflags = get_config_var('CFLAGS')
754 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
755 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
755 os.environ['CFLAGS'] = (
756 os.environ['CFLAGS'] = (
756 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
757 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
757
758
758 setup(name='mercurial',
759 setup(name='mercurial',
759 version=setupversion,
760 version=setupversion,
760 author='Matt Mackall and many others',
761 author='Matt Mackall and many others',
761 author_email='mercurial@mercurial-scm.org',
762 author_email='mercurial@mercurial-scm.org',
762 url='https://mercurial-scm.org/',
763 url='https://mercurial-scm.org/',
763 download_url='https://mercurial-scm.org/release/',
764 download_url='https://mercurial-scm.org/release/',
764 description=('Fast scalable distributed SCM (revision control, version '
765 description=('Fast scalable distributed SCM (revision control, version '
765 'control) system'),
766 'control) system'),
766 long_description=('Mercurial is a distributed SCM tool written in Python.'
767 long_description=('Mercurial is a distributed SCM tool written in Python.'
767 ' It is used by a number of large projects that require'
768 ' It is used by a number of large projects that require'
768 ' fast, reliable distributed revision control, such as '
769 ' fast, reliable distributed revision control, such as '
769 'Mozilla.'),
770 'Mozilla.'),
770 license='GNU GPLv2 or any later version',
771 license='GNU GPLv2 or any later version',
771 classifiers=[
772 classifiers=[
772 'Development Status :: 6 - Mature',
773 'Development Status :: 6 - Mature',
773 'Environment :: Console',
774 'Environment :: Console',
774 'Intended Audience :: Developers',
775 'Intended Audience :: Developers',
775 'Intended Audience :: System Administrators',
776 'Intended Audience :: System Administrators',
776 'License :: OSI Approved :: GNU General Public License (GPL)',
777 'License :: OSI Approved :: GNU General Public License (GPL)',
777 'Natural Language :: Danish',
778 'Natural Language :: Danish',
778 'Natural Language :: English',
779 'Natural Language :: English',
779 'Natural Language :: German',
780 'Natural Language :: German',
780 'Natural Language :: Italian',
781 'Natural Language :: Italian',
781 'Natural Language :: Japanese',
782 'Natural Language :: Japanese',
782 'Natural Language :: Portuguese (Brazilian)',
783 'Natural Language :: Portuguese (Brazilian)',
783 'Operating System :: Microsoft :: Windows',
784 'Operating System :: Microsoft :: Windows',
784 'Operating System :: OS Independent',
785 'Operating System :: OS Independent',
785 'Operating System :: POSIX',
786 'Operating System :: POSIX',
786 'Programming Language :: C',
787 'Programming Language :: C',
787 'Programming Language :: Python',
788 'Programming Language :: Python',
788 'Topic :: Software Development :: Version Control',
789 'Topic :: Software Development :: Version Control',
789 ],
790 ],
790 scripts=scripts,
791 scripts=scripts,
791 packages=packages,
792 packages=packages,
792 ext_modules=extmodules,
793 ext_modules=extmodules,
793 data_files=datafiles,
794 data_files=datafiles,
794 package_data=packagedata,
795 package_data=packagedata,
795 cmdclass=cmdclass,
796 cmdclass=cmdclass,
796 distclass=hgdist,
797 distclass=hgdist,
797 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email']},
798 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email']},
798 'bdist_mpkg': {'zipdist': False,
799 'bdist_mpkg': {'zipdist': False,
799 'license': 'COPYING',
800 'license': 'COPYING',
800 'readme': 'contrib/macosx/Readme.html',
801 'readme': 'contrib/macosx/Readme.html',
801 'welcome': 'contrib/macosx/Welcome.html',
802 'welcome': 'contrib/macosx/Welcome.html',
802 },
803 },
803 },
804 },
804 **extra)
805 **extra)
General Comments 0
You need to be logged in to leave comments. Login now