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