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