##// END OF EJS Templates
statfs: change Linux feature detection...
Jun Wu -
r31622:2243ba21 default
parent child Browse files
Show More
@@ -1,1319 +1,1318 b''
1 1 /*
2 2 osutil.c - native operating system services
3 3
4 4 Copyright 2007 Matt Mackall and others
5 5
6 6 This software may be used and distributed according to the terms of
7 7 the GNU General Public License, incorporated herein by reference.
8 8 */
9 9
10 10 #define _ATFILE_SOURCE
11 11 #include <Python.h>
12 12 #include <fcntl.h>
13 13 #include <stdio.h>
14 14 #include <stdlib.h>
15 15 #include <string.h>
16 16 #include <errno.h>
17 17
18 18 #ifdef _WIN32
19 19 #include <windows.h>
20 20 #include <io.h>
21 21 #else
22 22 #include <dirent.h>
23 23 #include <sys/socket.h>
24 24 #include <sys/stat.h>
25 25 #include <sys/types.h>
26 26 #include <unistd.h>
27 #ifdef HAVE_LINUX_MAGIC_H
27 #ifdef HAVE_LINUX_STATFS
28 28 #include <linux/magic.h>
29 #include <sys/vfs.h>
29 30 #endif
30 31 #ifdef HAVE_BSD_STATFS
31 32 #include <sys/mount.h>
32 33 #include <sys/param.h>
33 34 #endif
34 #ifdef HAVE_SYS_VFS_H
35 #include <sys/vfs.h>
36 #endif
37 35 #endif
38 36
39 37 #ifdef __APPLE__
40 38 #include <sys/attr.h>
41 39 #include <sys/vnode.h>
42 40 #endif
43 41
44 42 #include "util.h"
45 43
46 44 /* some platforms lack the PATH_MAX definition (eg. GNU/Hurd) */
47 45 #ifndef PATH_MAX
48 46 #define PATH_MAX 4096
49 47 #endif
50 48
51 49 #ifdef _WIN32
52 50 /*
53 51 stat struct compatible with hg expectations
54 52 Mercurial only uses st_mode, st_size and st_mtime
55 53 the rest is kept to minimize changes between implementations
56 54 */
57 55 struct hg_stat {
58 56 int st_dev;
59 57 int st_mode;
60 58 int st_nlink;
61 59 __int64 st_size;
62 60 int st_mtime;
63 61 int st_ctime;
64 62 };
65 63 struct listdir_stat {
66 64 PyObject_HEAD
67 65 struct hg_stat st;
68 66 };
69 67 #else
70 68 struct listdir_stat {
71 69 PyObject_HEAD
72 70 struct stat st;
73 71 };
74 72 #endif
75 73
76 74 #ifdef IS_PY3K
77 75 #define listdir_slot(name) \
78 76 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
79 77 { \
80 78 return PyLong_FromLong(((struct listdir_stat *)self)->st.name); \
81 79 }
82 80 #else
83 81 #define listdir_slot(name) \
84 82 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
85 83 { \
86 84 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
87 85 }
88 86 #endif
89 87
90 88 listdir_slot(st_dev)
91 89 listdir_slot(st_mode)
92 90 listdir_slot(st_nlink)
93 91 #ifdef _WIN32
94 92 static PyObject *listdir_stat_st_size(PyObject *self, void *x)
95 93 {
96 94 return PyLong_FromLongLong(
97 95 (PY_LONG_LONG)((struct listdir_stat *)self)->st.st_size);
98 96 }
99 97 #else
100 98 listdir_slot(st_size)
101 99 #endif
102 100 listdir_slot(st_mtime)
103 101 listdir_slot(st_ctime)
104 102
105 103 static struct PyGetSetDef listdir_stat_getsets[] = {
106 104 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
107 105 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
108 106 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
109 107 {"st_size", listdir_stat_st_size, 0, 0, 0},
110 108 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
111 109 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
112 110 {0, 0, 0, 0, 0}
113 111 };
114 112
115 113 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
116 114 {
117 115 return t->tp_alloc(t, 0);
118 116 }
119 117
120 118 static void listdir_stat_dealloc(PyObject *o)
121 119 {
122 120 o->ob_type->tp_free(o);
123 121 }
124 122
125 123 static PyTypeObject listdir_stat_type = {
126 124 PyVarObject_HEAD_INIT(NULL, 0)
127 125 "osutil.stat", /*tp_name*/
128 126 sizeof(struct listdir_stat), /*tp_basicsize*/
129 127 0, /*tp_itemsize*/
130 128 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
131 129 0, /*tp_print*/
132 130 0, /*tp_getattr*/
133 131 0, /*tp_setattr*/
134 132 0, /*tp_compare*/
135 133 0, /*tp_repr*/
136 134 0, /*tp_as_number*/
137 135 0, /*tp_as_sequence*/
138 136 0, /*tp_as_mapping*/
139 137 0, /*tp_hash */
140 138 0, /*tp_call*/
141 139 0, /*tp_str*/
142 140 0, /*tp_getattro*/
143 141 0, /*tp_setattro*/
144 142 0, /*tp_as_buffer*/
145 143 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
146 144 "stat objects", /* tp_doc */
147 145 0, /* tp_traverse */
148 146 0, /* tp_clear */
149 147 0, /* tp_richcompare */
150 148 0, /* tp_weaklistoffset */
151 149 0, /* tp_iter */
152 150 0, /* tp_iternext */
153 151 0, /* tp_methods */
154 152 0, /* tp_members */
155 153 listdir_stat_getsets, /* tp_getset */
156 154 0, /* tp_base */
157 155 0, /* tp_dict */
158 156 0, /* tp_descr_get */
159 157 0, /* tp_descr_set */
160 158 0, /* tp_dictoffset */
161 159 0, /* tp_init */
162 160 0, /* tp_alloc */
163 161 listdir_stat_new, /* tp_new */
164 162 };
165 163
166 164 #ifdef _WIN32
167 165
168 166 static int to_python_time(const FILETIME *tm)
169 167 {
170 168 /* number of seconds between epoch and January 1 1601 */
171 169 const __int64 a0 = (__int64)134774L * (__int64)24L * (__int64)3600L;
172 170 /* conversion factor from 100ns to 1s */
173 171 const __int64 a1 = 10000000;
174 172 /* explicit (int) cast to suspend compiler warnings */
175 173 return (int)((((__int64)tm->dwHighDateTime << 32)
176 174 + tm->dwLowDateTime) / a1 - a0);
177 175 }
178 176
179 177 static PyObject *make_item(const WIN32_FIND_DATAA *fd, int wantstat)
180 178 {
181 179 PyObject *py_st;
182 180 struct hg_stat *stp;
183 181
184 182 int kind = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
185 183 ? _S_IFDIR : _S_IFREG;
186 184
187 185 if (!wantstat)
188 186 return Py_BuildValue("si", fd->cFileName, kind);
189 187
190 188 py_st = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
191 189 if (!py_st)
192 190 return NULL;
193 191
194 192 stp = &((struct listdir_stat *)py_st)->st;
195 193 /*
196 194 use kind as st_mode
197 195 rwx bits on Win32 are meaningless
198 196 and Hg does not use them anyway
199 197 */
200 198 stp->st_mode = kind;
201 199 stp->st_mtime = to_python_time(&fd->ftLastWriteTime);
202 200 stp->st_ctime = to_python_time(&fd->ftCreationTime);
203 201 if (kind == _S_IFREG)
204 202 stp->st_size = ((__int64)fd->nFileSizeHigh << 32)
205 203 + fd->nFileSizeLow;
206 204 return Py_BuildValue("siN", fd->cFileName,
207 205 kind, py_st);
208 206 }
209 207
210 208 static PyObject *_listdir(char *path, int plen, int wantstat, char *skip)
211 209 {
212 210 PyObject *rval = NULL; /* initialize - return value */
213 211 PyObject *list;
214 212 HANDLE fh;
215 213 WIN32_FIND_DATAA fd;
216 214 char *pattern;
217 215
218 216 /* build the path + \* pattern string */
219 217 pattern = PyMem_Malloc(plen + 3); /* path + \* + \0 */
220 218 if (!pattern) {
221 219 PyErr_NoMemory();
222 220 goto error_nomem;
223 221 }
224 222 memcpy(pattern, path, plen);
225 223
226 224 if (plen > 0) {
227 225 char c = path[plen-1];
228 226 if (c != ':' && c != '/' && c != '\\')
229 227 pattern[plen++] = '\\';
230 228 }
231 229 pattern[plen++] = '*';
232 230 pattern[plen] = '\0';
233 231
234 232 fh = FindFirstFileA(pattern, &fd);
235 233 if (fh == INVALID_HANDLE_VALUE) {
236 234 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
237 235 goto error_file;
238 236 }
239 237
240 238 list = PyList_New(0);
241 239 if (!list)
242 240 goto error_list;
243 241
244 242 do {
245 243 PyObject *item;
246 244
247 245 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
248 246 if (!strcmp(fd.cFileName, ".")
249 247 || !strcmp(fd.cFileName, ".."))
250 248 continue;
251 249
252 250 if (skip && !strcmp(fd.cFileName, skip)) {
253 251 rval = PyList_New(0);
254 252 goto error;
255 253 }
256 254 }
257 255
258 256 item = make_item(&fd, wantstat);
259 257 if (!item)
260 258 goto error;
261 259
262 260 if (PyList_Append(list, item)) {
263 261 Py_XDECREF(item);
264 262 goto error;
265 263 }
266 264
267 265 Py_XDECREF(item);
268 266 } while (FindNextFileA(fh, &fd));
269 267
270 268 if (GetLastError() != ERROR_NO_MORE_FILES) {
271 269 PyErr_SetFromWindowsErrWithFilename(GetLastError(), path);
272 270 goto error;
273 271 }
274 272
275 273 rval = list;
276 274 Py_XINCREF(rval);
277 275 error:
278 276 Py_XDECREF(list);
279 277 error_list:
280 278 FindClose(fh);
281 279 error_file:
282 280 PyMem_Free(pattern);
283 281 error_nomem:
284 282 return rval;
285 283 }
286 284
287 285 #else
288 286
289 287 int entkind(struct dirent *ent)
290 288 {
291 289 #ifdef DT_REG
292 290 switch (ent->d_type) {
293 291 case DT_REG: return S_IFREG;
294 292 case DT_DIR: return S_IFDIR;
295 293 case DT_LNK: return S_IFLNK;
296 294 case DT_BLK: return S_IFBLK;
297 295 case DT_CHR: return S_IFCHR;
298 296 case DT_FIFO: return S_IFIFO;
299 297 case DT_SOCK: return S_IFSOCK;
300 298 }
301 299 #endif
302 300 return -1;
303 301 }
304 302
305 303 static PyObject *makestat(const struct stat *st)
306 304 {
307 305 PyObject *stat;
308 306
309 307 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
310 308 if (stat)
311 309 memcpy(&((struct listdir_stat *)stat)->st, st, sizeof(*st));
312 310 return stat;
313 311 }
314 312
315 313 static PyObject *_listdir_stat(char *path, int pathlen, int keepstat,
316 314 char *skip)
317 315 {
318 316 PyObject *list, *elem, *stat = NULL, *ret = NULL;
319 317 char fullpath[PATH_MAX + 10];
320 318 int kind, err;
321 319 struct stat st;
322 320 struct dirent *ent;
323 321 DIR *dir;
324 322 #ifdef AT_SYMLINK_NOFOLLOW
325 323 int dfd = -1;
326 324 #endif
327 325
328 326 if (pathlen >= PATH_MAX) {
329 327 errno = ENAMETOOLONG;
330 328 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
331 329 goto error_value;
332 330 }
333 331 strncpy(fullpath, path, PATH_MAX);
334 332 fullpath[pathlen] = '/';
335 333
336 334 #ifdef AT_SYMLINK_NOFOLLOW
337 335 dfd = open(path, O_RDONLY);
338 336 if (dfd == -1) {
339 337 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
340 338 goto error_value;
341 339 }
342 340 dir = fdopendir(dfd);
343 341 #else
344 342 dir = opendir(path);
345 343 #endif
346 344 if (!dir) {
347 345 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
348 346 goto error_dir;
349 347 }
350 348
351 349 list = PyList_New(0);
352 350 if (!list)
353 351 goto error_list;
354 352
355 353 while ((ent = readdir(dir))) {
356 354 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
357 355 continue;
358 356
359 357 kind = entkind(ent);
360 358 if (kind == -1 || keepstat) {
361 359 #ifdef AT_SYMLINK_NOFOLLOW
362 360 err = fstatat(dfd, ent->d_name, &st,
363 361 AT_SYMLINK_NOFOLLOW);
364 362 #else
365 363 strncpy(fullpath + pathlen + 1, ent->d_name,
366 364 PATH_MAX - pathlen);
367 365 fullpath[PATH_MAX] = '\0';
368 366 err = lstat(fullpath, &st);
369 367 #endif
370 368 if (err == -1) {
371 369 /* race with file deletion? */
372 370 if (errno == ENOENT)
373 371 continue;
374 372 strncpy(fullpath + pathlen + 1, ent->d_name,
375 373 PATH_MAX - pathlen);
376 374 fullpath[PATH_MAX] = 0;
377 375 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
378 376 fullpath);
379 377 goto error;
380 378 }
381 379 kind = st.st_mode & S_IFMT;
382 380 }
383 381
384 382 /* quit early? */
385 383 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
386 384 ret = PyList_New(0);
387 385 goto error;
388 386 }
389 387
390 388 if (keepstat) {
391 389 stat = makestat(&st);
392 390 if (!stat)
393 391 goto error;
394 392 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
395 393 } else
396 394 elem = Py_BuildValue("si", ent->d_name, kind);
397 395 if (!elem)
398 396 goto error;
399 397 stat = NULL;
400 398
401 399 PyList_Append(list, elem);
402 400 Py_DECREF(elem);
403 401 }
404 402
405 403 ret = list;
406 404 Py_INCREF(ret);
407 405
408 406 error:
409 407 Py_DECREF(list);
410 408 Py_XDECREF(stat);
411 409 error_list:
412 410 closedir(dir);
413 411 /* closedir also closes its dirfd */
414 412 goto error_value;
415 413 error_dir:
416 414 #ifdef AT_SYMLINK_NOFOLLOW
417 415 close(dfd);
418 416 #endif
419 417 error_value:
420 418 return ret;
421 419 }
422 420
423 421 #ifdef __APPLE__
424 422
425 423 typedef struct {
426 424 u_int32_t length;
427 425 attrreference_t name;
428 426 fsobj_type_t obj_type;
429 427 struct timespec mtime;
430 428 #if __LITTLE_ENDIAN__
431 429 mode_t access_mask;
432 430 uint16_t padding;
433 431 #else
434 432 uint16_t padding;
435 433 mode_t access_mask;
436 434 #endif
437 435 off_t size;
438 436 } __attribute__((packed)) attrbuf_entry;
439 437
440 438 int attrkind(attrbuf_entry *entry)
441 439 {
442 440 switch (entry->obj_type) {
443 441 case VREG: return S_IFREG;
444 442 case VDIR: return S_IFDIR;
445 443 case VLNK: return S_IFLNK;
446 444 case VBLK: return S_IFBLK;
447 445 case VCHR: return S_IFCHR;
448 446 case VFIFO: return S_IFIFO;
449 447 case VSOCK: return S_IFSOCK;
450 448 }
451 449 return -1;
452 450 }
453 451
454 452 /* get these many entries at a time */
455 453 #define LISTDIR_BATCH_SIZE 50
456 454
457 455 static PyObject *_listdir_batch(char *path, int pathlen, int keepstat,
458 456 char *skip, bool *fallback)
459 457 {
460 458 PyObject *list, *elem, *stat = NULL, *ret = NULL;
461 459 int kind, err;
462 460 unsigned long index;
463 461 unsigned int count, old_state, new_state;
464 462 bool state_seen = false;
465 463 attrbuf_entry *entry;
466 464 /* from the getattrlist(2) man page: a path can be no longer than
467 465 (NAME_MAX * 3 + 1) bytes. Also, "The getattrlist() function will
468 466 silently truncate attribute data if attrBufSize is too small." So
469 467 pass in a buffer big enough for the worst case. */
470 468 char attrbuf[LISTDIR_BATCH_SIZE * (sizeof(attrbuf_entry) + NAME_MAX * 3 + 1)];
471 469 unsigned int basep_unused;
472 470
473 471 struct stat st;
474 472 int dfd = -1;
475 473
476 474 /* these must match the attrbuf_entry struct, otherwise you'll end up
477 475 with garbage */
478 476 struct attrlist requested_attr = {0};
479 477 requested_attr.bitmapcount = ATTR_BIT_MAP_COUNT;
480 478 requested_attr.commonattr = (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |
481 479 ATTR_CMN_MODTIME | ATTR_CMN_ACCESSMASK);
482 480 requested_attr.fileattr = ATTR_FILE_DATALENGTH;
483 481
484 482 *fallback = false;
485 483
486 484 if (pathlen >= PATH_MAX) {
487 485 errno = ENAMETOOLONG;
488 486 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
489 487 goto error_value;
490 488 }
491 489
492 490 dfd = open(path, O_RDONLY);
493 491 if (dfd == -1) {
494 492 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
495 493 goto error_value;
496 494 }
497 495
498 496 list = PyList_New(0);
499 497 if (!list)
500 498 goto error_dir;
501 499
502 500 do {
503 501 count = LISTDIR_BATCH_SIZE;
504 502 err = getdirentriesattr(dfd, &requested_attr, &attrbuf,
505 503 sizeof(attrbuf), &count, &basep_unused,
506 504 &new_state, 0);
507 505 if (err < 0) {
508 506 if (errno == ENOTSUP) {
509 507 /* We're on a filesystem that doesn't support
510 508 getdirentriesattr. Fall back to the
511 509 stat-based implementation. */
512 510 *fallback = true;
513 511 } else
514 512 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
515 513 goto error;
516 514 }
517 515
518 516 if (!state_seen) {
519 517 old_state = new_state;
520 518 state_seen = true;
521 519 } else if (old_state != new_state) {
522 520 /* There's an edge case with getdirentriesattr. Consider
523 521 the following initial list of files:
524 522
525 523 a
526 524 b
527 525 <--
528 526 c
529 527 d
530 528
531 529 If the iteration is paused at the arrow, and b is
532 530 deleted before it is resumed, getdirentriesattr will
533 531 not return d at all! Ordinarily we're expected to
534 532 restart the iteration from the beginning. To avoid
535 533 getting stuck in a retry loop here, fall back to
536 534 stat. */
537 535 *fallback = true;
538 536 goto error;
539 537 }
540 538
541 539 entry = (attrbuf_entry *)attrbuf;
542 540
543 541 for (index = 0; index < count; index++) {
544 542 char *filename = ((char *)&entry->name) +
545 543 entry->name.attr_dataoffset;
546 544
547 545 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
548 546 continue;
549 547
550 548 kind = attrkind(entry);
551 549 if (kind == -1) {
552 550 PyErr_Format(PyExc_OSError,
553 551 "unknown object type %u for file "
554 552 "%s%s!",
555 553 entry->obj_type, path, filename);
556 554 goto error;
557 555 }
558 556
559 557 /* quit early? */
560 558 if (skip && kind == S_IFDIR && !strcmp(filename, skip)) {
561 559 ret = PyList_New(0);
562 560 goto error;
563 561 }
564 562
565 563 if (keepstat) {
566 564 /* from the getattrlist(2) man page: "Only the
567 565 permission bits ... are valid". */
568 566 st.st_mode = (entry->access_mask & ~S_IFMT) | kind;
569 567 st.st_mtime = entry->mtime.tv_sec;
570 568 st.st_size = entry->size;
571 569 stat = makestat(&st);
572 570 if (!stat)
573 571 goto error;
574 572 elem = Py_BuildValue("siN", filename, kind, stat);
575 573 } else
576 574 elem = Py_BuildValue("si", filename, kind);
577 575 if (!elem)
578 576 goto error;
579 577 stat = NULL;
580 578
581 579 PyList_Append(list, elem);
582 580 Py_DECREF(elem);
583 581
584 582 entry = (attrbuf_entry *)((char *)entry + entry->length);
585 583 }
586 584 } while (err == 0);
587 585
588 586 ret = list;
589 587 Py_INCREF(ret);
590 588
591 589 error:
592 590 Py_DECREF(list);
593 591 Py_XDECREF(stat);
594 592 error_dir:
595 593 close(dfd);
596 594 error_value:
597 595 return ret;
598 596 }
599 597
600 598 #endif /* __APPLE__ */
601 599
602 600 static PyObject *_listdir(char *path, int pathlen, int keepstat, char *skip)
603 601 {
604 602 #ifdef __APPLE__
605 603 PyObject *ret;
606 604 bool fallback = false;
607 605
608 606 ret = _listdir_batch(path, pathlen, keepstat, skip, &fallback);
609 607 if (ret != NULL || !fallback)
610 608 return ret;
611 609 #endif
612 610 return _listdir_stat(path, pathlen, keepstat, skip);
613 611 }
614 612
615 613 static PyObject *statfiles(PyObject *self, PyObject *args)
616 614 {
617 615 PyObject *names, *stats;
618 616 Py_ssize_t i, count;
619 617
620 618 if (!PyArg_ParseTuple(args, "O:statfiles", &names))
621 619 return NULL;
622 620
623 621 count = PySequence_Length(names);
624 622 if (count == -1) {
625 623 PyErr_SetString(PyExc_TypeError, "not a sequence");
626 624 return NULL;
627 625 }
628 626
629 627 stats = PyList_New(count);
630 628 if (stats == NULL)
631 629 return NULL;
632 630
633 631 for (i = 0; i < count; i++) {
634 632 PyObject *stat, *pypath;
635 633 struct stat st;
636 634 int ret, kind;
637 635 char *path;
638 636
639 637 /* With a large file count or on a slow filesystem,
640 638 don't block signals for long (issue4878). */
641 639 if ((i % 1000) == 999 && PyErr_CheckSignals() == -1)
642 640 goto bail;
643 641
644 642 pypath = PySequence_GetItem(names, i);
645 643 if (!pypath)
646 644 goto bail;
647 645 path = PyBytes_AsString(pypath);
648 646 if (path == NULL) {
649 647 Py_DECREF(pypath);
650 648 PyErr_SetString(PyExc_TypeError, "not a string");
651 649 goto bail;
652 650 }
653 651 ret = lstat(path, &st);
654 652 Py_DECREF(pypath);
655 653 kind = st.st_mode & S_IFMT;
656 654 if (ret != -1 && (kind == S_IFREG || kind == S_IFLNK)) {
657 655 stat = makestat(&st);
658 656 if (stat == NULL)
659 657 goto bail;
660 658 PyList_SET_ITEM(stats, i, stat);
661 659 } else {
662 660 Py_INCREF(Py_None);
663 661 PyList_SET_ITEM(stats, i, Py_None);
664 662 }
665 663 }
666 664
667 665 return stats;
668 666
669 667 bail:
670 668 Py_DECREF(stats);
671 669 return NULL;
672 670 }
673 671
674 672 /*
675 673 * recvfds() simply does not release GIL during blocking io operation because
676 674 * command server is known to be single-threaded.
677 675 *
678 676 * Old systems such as Solaris don't provide CMSG_LEN, msg_control, etc.
679 677 * Currently, recvfds() is not supported on these platforms.
680 678 */
681 679 #ifdef CMSG_LEN
682 680
683 681 static ssize_t recvfdstobuf(int sockfd, int **rfds, void *cbuf, size_t cbufsize)
684 682 {
685 683 char dummy[1];
686 684 struct iovec iov = {dummy, sizeof(dummy)};
687 685 struct msghdr msgh = {0};
688 686 struct cmsghdr *cmsg;
689 687
690 688 msgh.msg_iov = &iov;
691 689 msgh.msg_iovlen = 1;
692 690 msgh.msg_control = cbuf;
693 691 msgh.msg_controllen = (socklen_t)cbufsize;
694 692 if (recvmsg(sockfd, &msgh, 0) < 0)
695 693 return -1;
696 694
697 695 for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg;
698 696 cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
699 697 if (cmsg->cmsg_level != SOL_SOCKET ||
700 698 cmsg->cmsg_type != SCM_RIGHTS)
701 699 continue;
702 700 *rfds = (int *)CMSG_DATA(cmsg);
703 701 return (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
704 702 }
705 703
706 704 *rfds = cbuf;
707 705 return 0;
708 706 }
709 707
710 708 static PyObject *recvfds(PyObject *self, PyObject *args)
711 709 {
712 710 int sockfd;
713 711 int *rfds = NULL;
714 712 ssize_t rfdscount, i;
715 713 char cbuf[256];
716 714 PyObject *rfdslist = NULL;
717 715
718 716 if (!PyArg_ParseTuple(args, "i", &sockfd))
719 717 return NULL;
720 718
721 719 rfdscount = recvfdstobuf(sockfd, &rfds, cbuf, sizeof(cbuf));
722 720 if (rfdscount < 0)
723 721 return PyErr_SetFromErrno(PyExc_OSError);
724 722
725 723 rfdslist = PyList_New(rfdscount);
726 724 if (!rfdslist)
727 725 goto bail;
728 726 for (i = 0; i < rfdscount; i++) {
729 727 PyObject *obj = PyLong_FromLong(rfds[i]);
730 728 if (!obj)
731 729 goto bail;
732 730 PyList_SET_ITEM(rfdslist, i, obj);
733 731 }
734 732 return rfdslist;
735 733
736 734 bail:
737 735 Py_XDECREF(rfdslist);
738 736 return NULL;
739 737 }
740 738
741 739 #endif /* CMSG_LEN */
742 740
743 741 #if defined(HAVE_SETPROCTITLE)
744 742 /* setproctitle is the first choice - available in FreeBSD */
745 743 #define SETPROCNAME_USE_SETPROCTITLE
746 744 #elif (defined(__linux__) || defined(__APPLE__)) && PY_MAJOR_VERSION == 2
747 745 /* rewrite the argv buffer in place - works in Linux and OS X. Py_GetArgcArgv
748 746 * in Python 3 returns the copied wchar_t **argv, thus unsupported. */
749 747 #define SETPROCNAME_USE_ARGVREWRITE
750 748 #else
751 749 #define SETPROCNAME_USE_NONE
752 750 #endif
753 751
754 752 #ifndef SETPROCNAME_USE_NONE
755 753 static PyObject *setprocname(PyObject *self, PyObject *args)
756 754 {
757 755 const char *name = NULL;
758 756 if (!PyArg_ParseTuple(args, "s", &name))
759 757 return NULL;
760 758
761 759 #if defined(SETPROCNAME_USE_SETPROCTITLE)
762 760 setproctitle("%s", name);
763 761 #elif defined(SETPROCNAME_USE_ARGVREWRITE)
764 762 {
765 763 static char *argvstart = NULL;
766 764 static size_t argvsize = 0;
767 765 if (argvstart == NULL) {
768 766 int argc = 0, i;
769 767 char **argv = NULL;
770 768 char *argvend;
771 769 extern void Py_GetArgcArgv(int *argc, char ***argv);
772 770 Py_GetArgcArgv(&argc, &argv);
773 771
774 772 /* Check the memory we can use. Typically, argv[i] and
775 773 * argv[i + 1] are continuous. */
776 774 argvend = argvstart = argv[0];
777 775 for (i = 0; i < argc; ++i) {
778 776 if (argv[i] > argvend || argv[i] < argvstart)
779 777 break; /* not continuous */
780 778 size_t len = strlen(argv[i]);
781 779 argvend = argv[i] + len + 1 /* '\0' */;
782 780 }
783 781 if (argvend > argvstart) /* sanity check */
784 782 argvsize = argvend - argvstart;
785 783 }
786 784
787 785 if (argvstart && argvsize > 1) {
788 786 int n = snprintf(argvstart, argvsize, "%s", name);
789 787 if (n >= 0 && (size_t)n < argvsize)
790 788 memset(argvstart + n, 0, argvsize - n);
791 789 }
792 790 }
793 791 #endif
794 792
795 793 Py_RETURN_NONE;
796 794 }
797 795 #endif /* ndef SETPROCNAME_USE_NONE */
798 796
799 #ifdef HAVE_STATFS
797 #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
800 798 /* given a directory path, return filesystem type (best-effort), or None */
801 799 const char *getfstype(const char *path) {
802 800 #ifdef HAVE_BSD_STATFS
803 801 /* need to return a string field */
804 802 static struct statfs buf;
805 803 #else
806 804 struct statfs buf;
807 805 #endif
808 806 int r;
809 807 memset(&buf, 0, sizeof(buf));
810 808 r = statfs(path, &buf);
811 809 if (r != 0)
812 810 return NULL;
813 #ifdef HAVE_BSD_STATFS
811 #if defined(HAVE_BSD_STATFS)
814 812 /* BSD or OSX provides a f_fstypename field */
815 813 return buf.f_fstypename;
816 #endif
814 #elif defined(HAVE_LINUX_STATFS)
817 815 /* Begin of Linux filesystems */
818 816 #ifdef ADFS_SUPER_MAGIC
819 817 if (buf.f_type == ADFS_SUPER_MAGIC)
820 818 return "adfs";
821 819 #endif
822 820 #ifdef AFFS_SUPER_MAGIC
823 821 if (buf.f_type == AFFS_SUPER_MAGIC)
824 822 return "affs";
825 823 #endif
826 824 #ifdef BDEVFS_MAGIC
827 825 if (buf.f_type == BDEVFS_MAGIC)
828 826 return "bdevfs";
829 827 #endif
830 828 #ifdef BEFS_SUPER_MAGIC
831 829 if (buf.f_type == BEFS_SUPER_MAGIC)
832 830 return "befs";
833 831 #endif
834 832 #ifdef BFS_MAGIC
835 833 if (buf.f_type == BFS_MAGIC)
836 834 return "bfs";
837 835 #endif
838 836 #ifdef BINFMTFS_MAGIC
839 837 if (buf.f_type == BINFMTFS_MAGIC)
840 838 return "binfmtfs";
841 839 #endif
842 840 #ifdef BTRFS_SUPER_MAGIC
843 841 if (buf.f_type == BTRFS_SUPER_MAGIC)
844 842 return "btrfs";
845 843 #endif
846 844 #ifdef CGROUP_SUPER_MAGIC
847 845 if (buf.f_type == CGROUP_SUPER_MAGIC)
848 846 return "cgroup";
849 847 #endif
850 848 #ifdef CIFS_MAGIC_NUMBER
851 849 if (buf.f_type == CIFS_MAGIC_NUMBER)
852 850 return "cifs";
853 851 #endif
854 852 #ifdef CODA_SUPER_MAGIC
855 853 if (buf.f_type == CODA_SUPER_MAGIC)
856 854 return "coda";
857 855 #endif
858 856 #ifdef COH_SUPER_MAGIC
859 857 if (buf.f_type == COH_SUPER_MAGIC)
860 858 return "coh";
861 859 #endif
862 860 #ifdef CRAMFS_MAGIC
863 861 if (buf.f_type == CRAMFS_MAGIC)
864 862 return "cramfs";
865 863 #endif
866 864 #ifdef DEBUGFS_MAGIC
867 865 if (buf.f_type == DEBUGFS_MAGIC)
868 866 return "debugfs";
869 867 #endif
870 868 #ifdef DEVFS_SUPER_MAGIC
871 869 if (buf.f_type == DEVFS_SUPER_MAGIC)
872 870 return "devfs";
873 871 #endif
874 872 #ifdef DEVPTS_SUPER_MAGIC
875 873 if (buf.f_type == DEVPTS_SUPER_MAGIC)
876 874 return "devpts";
877 875 #endif
878 876 #ifdef EFIVARFS_MAGIC
879 877 if (buf.f_type == EFIVARFS_MAGIC)
880 878 return "efivarfs";
881 879 #endif
882 880 #ifdef EFS_SUPER_MAGIC
883 881 if (buf.f_type == EFS_SUPER_MAGIC)
884 882 return "efs";
885 883 #endif
886 884 #ifdef EXT_SUPER_MAGIC
887 885 if (buf.f_type == EXT_SUPER_MAGIC)
888 886 return "ext";
889 887 #endif
890 888 #ifdef EXT2_OLD_SUPER_MAGIC
891 889 if (buf.f_type == EXT2_OLD_SUPER_MAGIC)
892 890 return "ext2";
893 891 #endif
894 892 #ifdef EXT2_SUPER_MAGIC
895 893 if (buf.f_type == EXT2_SUPER_MAGIC)
896 894 return "ext2";
897 895 #endif
898 896 #ifdef EXT3_SUPER_MAGIC
899 897 if (buf.f_type == EXT3_SUPER_MAGIC)
900 898 return "ext3";
901 899 #endif
902 900 #ifdef EXT4_SUPER_MAGIC
903 901 if (buf.f_type == EXT4_SUPER_MAGIC)
904 902 return "ext4";
905 903 #endif
906 904 #ifdef FUSE_SUPER_MAGIC
907 905 if (buf.f_type == FUSE_SUPER_MAGIC)
908 906 return "fuse";
909 907 #endif
910 908 #ifdef FUTEXFS_SUPER_MAGIC
911 909 if (buf.f_type == FUTEXFS_SUPER_MAGIC)
912 910 return "futexfs";
913 911 #endif
914 912 #ifdef HFS_SUPER_MAGIC
915 913 if (buf.f_type == HFS_SUPER_MAGIC)
916 914 return "hfs";
917 915 #endif
918 916 #ifdef HOSTFS_SUPER_MAGIC
919 917 if (buf.f_type == HOSTFS_SUPER_MAGIC)
920 918 return "hostfs";
921 919 #endif
922 920 #ifdef HPFS_SUPER_MAGIC
923 921 if (buf.f_type == HPFS_SUPER_MAGIC)
924 922 return "hpfs";
925 923 #endif
926 924 #ifdef HUGETLBFS_MAGIC
927 925 if (buf.f_type == HUGETLBFS_MAGIC)
928 926 return "hugetlbfs";
929 927 #endif
930 928 #ifdef ISOFS_SUPER_MAGIC
931 929 if (buf.f_type == ISOFS_SUPER_MAGIC)
932 930 return "isofs";
933 931 #endif
934 932 #ifdef JFFS2_SUPER_MAGIC
935 933 if (buf.f_type == JFFS2_SUPER_MAGIC)
936 934 return "jffs2";
937 935 #endif
938 936 #ifdef JFS_SUPER_MAGIC
939 937 if (buf.f_type == JFS_SUPER_MAGIC)
940 938 return "jfs";
941 939 #endif
942 940 #ifdef MINIX_SUPER_MAGIC
943 941 if (buf.f_type == MINIX_SUPER_MAGIC)
944 942 return "minix";
945 943 #endif
946 944 #ifdef MINIX2_SUPER_MAGIC
947 945 if (buf.f_type == MINIX2_SUPER_MAGIC)
948 946 return "minix2";
949 947 #endif
950 948 #ifdef MINIX3_SUPER_MAGIC
951 949 if (buf.f_type == MINIX3_SUPER_MAGIC)
952 950 return "minix3";
953 951 #endif
954 952 #ifdef MQUEUE_MAGIC
955 953 if (buf.f_type == MQUEUE_MAGIC)
956 954 return "mqueue";
957 955 #endif
958 956 #ifdef MSDOS_SUPER_MAGIC
959 957 if (buf.f_type == MSDOS_SUPER_MAGIC)
960 958 return "msdos";
961 959 #endif
962 960 #ifdef NCP_SUPER_MAGIC
963 961 if (buf.f_type == NCP_SUPER_MAGIC)
964 962 return "ncp";
965 963 #endif
966 964 #ifdef NFS_SUPER_MAGIC
967 965 if (buf.f_type == NFS_SUPER_MAGIC)
968 966 return "nfs";
969 967 #endif
970 968 #ifdef NILFS_SUPER_MAGIC
971 969 if (buf.f_type == NILFS_SUPER_MAGIC)
972 970 return "nilfs";
973 971 #endif
974 972 #ifdef NTFS_SB_MAGIC
975 973 if (buf.f_type == NTFS_SB_MAGIC)
976 974 return "ntfs-sb";
977 975 #endif
978 976 #ifdef OCFS2_SUPER_MAGIC
979 977 if (buf.f_type == OCFS2_SUPER_MAGIC)
980 978 return "ocfs2";
981 979 #endif
982 980 #ifdef OPENPROM_SUPER_MAGIC
983 981 if (buf.f_type == OPENPROM_SUPER_MAGIC)
984 982 return "openprom";
985 983 #endif
986 984 #ifdef PIPEFS_MAGIC
987 985 if (buf.f_type == PIPEFS_MAGIC)
988 986 return "pipefs";
989 987 #endif
990 988 #ifdef PROC_SUPER_MAGIC
991 989 if (buf.f_type == PROC_SUPER_MAGIC)
992 990 return "proc";
993 991 #endif
994 992 #ifdef PSTOREFS_MAGIC
995 993 if (buf.f_type == PSTOREFS_MAGIC)
996 994 return "pstorefs";
997 995 #endif
998 996 #ifdef QNX4_SUPER_MAGIC
999 997 if (buf.f_type == QNX4_SUPER_MAGIC)
1000 998 return "qnx4";
1001 999 #endif
1002 1000 #ifdef QNX6_SUPER_MAGIC
1003 1001 if (buf.f_type == QNX6_SUPER_MAGIC)
1004 1002 return "qnx6";
1005 1003 #endif
1006 1004 #ifdef RAMFS_MAGIC
1007 1005 if (buf.f_type == RAMFS_MAGIC)
1008 1006 return "ramfs";
1009 1007 #endif
1010 1008 #ifdef REISERFS_SUPER_MAGIC
1011 1009 if (buf.f_type == REISERFS_SUPER_MAGIC)
1012 1010 return "reiserfs";
1013 1011 #endif
1014 1012 #ifdef ROMFS_MAGIC
1015 1013 if (buf.f_type == ROMFS_MAGIC)
1016 1014 return "romfs";
1017 1015 #endif
1018 1016 #ifdef SELINUX_MAGIC
1019 1017 if (buf.f_type == SELINUX_MAGIC)
1020 1018 return "selinux";
1021 1019 #endif
1022 1020 #ifdef SMACK_MAGIC
1023 1021 if (buf.f_type == SMACK_MAGIC)
1024 1022 return "smack";
1025 1023 #endif
1026 1024 #ifdef SMB_SUPER_MAGIC
1027 1025 if (buf.f_type == SMB_SUPER_MAGIC)
1028 1026 return "smb";
1029 1027 #endif
1030 1028 #ifdef SOCKFS_MAGIC
1031 1029 if (buf.f_type == SOCKFS_MAGIC)
1032 1030 return "sockfs";
1033 1031 #endif
1034 1032 #ifdef SQUASHFS_MAGIC
1035 1033 if (buf.f_type == SQUASHFS_MAGIC)
1036 1034 return "squashfs";
1037 1035 #endif
1038 1036 #ifdef SYSFS_MAGIC
1039 1037 if (buf.f_type == SYSFS_MAGIC)
1040 1038 return "sysfs";
1041 1039 #endif
1042 1040 #ifdef SYSV2_SUPER_MAGIC
1043 1041 if (buf.f_type == SYSV2_SUPER_MAGIC)
1044 1042 return "sysv2";
1045 1043 #endif
1046 1044 #ifdef SYSV4_SUPER_MAGIC
1047 1045 if (buf.f_type == SYSV4_SUPER_MAGIC)
1048 1046 return "sysv4";
1049 1047 #endif
1050 1048 #ifdef TMPFS_MAGIC
1051 1049 if (buf.f_type == TMPFS_MAGIC)
1052 1050 return "tmpfs";
1053 1051 #endif
1054 1052 #ifdef UDF_SUPER_MAGIC
1055 1053 if (buf.f_type == UDF_SUPER_MAGIC)
1056 1054 return "udf";
1057 1055 #endif
1058 1056 #ifdef UFS_MAGIC
1059 1057 if (buf.f_type == UFS_MAGIC)
1060 1058 return "ufs";
1061 1059 #endif
1062 1060 #ifdef USBDEVICE_SUPER_MAGIC
1063 1061 if (buf.f_type == USBDEVICE_SUPER_MAGIC)
1064 1062 return "usbdevice";
1065 1063 #endif
1066 1064 #ifdef V9FS_MAGIC
1067 1065 if (buf.f_type == V9FS_MAGIC)
1068 1066 return "v9fs";
1069 1067 #endif
1070 1068 #ifdef VXFS_SUPER_MAGIC
1071 1069 if (buf.f_type == VXFS_SUPER_MAGIC)
1072 1070 return "vxfs";
1073 1071 #endif
1074 1072 #ifdef XENFS_SUPER_MAGIC
1075 1073 if (buf.f_type == XENFS_SUPER_MAGIC)
1076 1074 return "xenfs";
1077 1075 #endif
1078 1076 #ifdef XENIX_SUPER_MAGIC
1079 1077 if (buf.f_type == XENIX_SUPER_MAGIC)
1080 1078 return "xenix";
1081 1079 #endif
1082 1080 #ifdef XFS_SUPER_MAGIC
1083 1081 if (buf.f_type == XFS_SUPER_MAGIC)
1084 1082 return "xfs";
1085 1083 #endif
1086 1084 /* End of Linux filesystems */
1085 #endif /* def HAVE_LINUX_STATFS */
1087 1086 return NULL;
1088 1087 }
1089 1088
1090 1089 static PyObject *pygetfstype(PyObject *self, PyObject *args)
1091 1090 {
1092 1091 const char *path = NULL;
1093 1092 if (!PyArg_ParseTuple(args, "s", &path))
1094 1093 return NULL;
1095 1094
1096 1095 const char *type = getfstype(path);
1097 1096 if (type == NULL)
1098 1097 Py_RETURN_NONE;
1099 1098
1100 1099 PyObject *result = Py_BuildValue("s", type);
1101 1100 return result;
1102 1101 }
1103 #endif /* def HAVE_STATFS */
1102 #endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */
1104 1103
1105 1104 #endif /* ndef _WIN32 */
1106 1105
1107 1106 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
1108 1107 {
1109 1108 PyObject *statobj = NULL; /* initialize - optional arg */
1110 1109 PyObject *skipobj = NULL; /* initialize - optional arg */
1111 1110 char *path, *skip = NULL;
1112 1111 int wantstat, plen;
1113 1112
1114 1113 static char *kwlist[] = {"path", "stat", "skip", NULL};
1115 1114
1116 1115 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|OO:listdir",
1117 1116 kwlist, &path, &plen, &statobj, &skipobj))
1118 1117 return NULL;
1119 1118
1120 1119 wantstat = statobj && PyObject_IsTrue(statobj);
1121 1120
1122 1121 if (skipobj && skipobj != Py_None) {
1123 1122 skip = PyBytes_AsString(skipobj);
1124 1123 if (!skip)
1125 1124 return NULL;
1126 1125 }
1127 1126
1128 1127 return _listdir(path, plen, wantstat, skip);
1129 1128 }
1130 1129
1131 1130 #ifdef _WIN32
1132 1131 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds)
1133 1132 {
1134 1133 static char *kwlist[] = {"name", "mode", "buffering", NULL};
1135 1134 PyObject *file_obj = NULL;
1136 1135 char *name = NULL;
1137 1136 char *mode = "rb";
1138 1137 DWORD access = 0;
1139 1138 DWORD creation;
1140 1139 HANDLE handle;
1141 1140 int fd, flags = 0;
1142 1141 int bufsize = -1;
1143 1142 char m0, m1, m2;
1144 1143 char fpmode[4];
1145 1144 int fppos = 0;
1146 1145 int plus;
1147 1146 FILE *fp;
1148 1147
1149 1148 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist,
1150 1149 Py_FileSystemDefaultEncoding,
1151 1150 &name, &mode, &bufsize))
1152 1151 return NULL;
1153 1152
1154 1153 m0 = mode[0];
1155 1154 m1 = m0 ? mode[1] : '\0';
1156 1155 m2 = m1 ? mode[2] : '\0';
1157 1156 plus = m1 == '+' || m2 == '+';
1158 1157
1159 1158 fpmode[fppos++] = m0;
1160 1159 if (m1 == 'b' || m2 == 'b') {
1161 1160 flags = _O_BINARY;
1162 1161 fpmode[fppos++] = 'b';
1163 1162 }
1164 1163 else
1165 1164 flags = _O_TEXT;
1166 1165 if (m0 == 'r' && !plus) {
1167 1166 flags |= _O_RDONLY;
1168 1167 access = GENERIC_READ;
1169 1168 } else {
1170 1169 /*
1171 1170 work around http://support.microsoft.com/kb/899149 and
1172 1171 set _O_RDWR for 'w' and 'a', even if mode has no '+'
1173 1172 */
1174 1173 flags |= _O_RDWR;
1175 1174 access = GENERIC_READ | GENERIC_WRITE;
1176 1175 fpmode[fppos++] = '+';
1177 1176 }
1178 1177 fpmode[fppos++] = '\0';
1179 1178
1180 1179 switch (m0) {
1181 1180 case 'r':
1182 1181 creation = OPEN_EXISTING;
1183 1182 break;
1184 1183 case 'w':
1185 1184 creation = CREATE_ALWAYS;
1186 1185 break;
1187 1186 case 'a':
1188 1187 creation = OPEN_ALWAYS;
1189 1188 flags |= _O_APPEND;
1190 1189 break;
1191 1190 default:
1192 1191 PyErr_Format(PyExc_ValueError,
1193 1192 "mode string must begin with one of 'r', 'w', "
1194 1193 "or 'a', not '%c'", m0);
1195 1194 goto bail;
1196 1195 }
1197 1196
1198 1197 handle = CreateFile(name, access,
1199 1198 FILE_SHARE_READ | FILE_SHARE_WRITE |
1200 1199 FILE_SHARE_DELETE,
1201 1200 NULL,
1202 1201 creation,
1203 1202 FILE_ATTRIBUTE_NORMAL,
1204 1203 0);
1205 1204
1206 1205 if (handle == INVALID_HANDLE_VALUE) {
1207 1206 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name);
1208 1207 goto bail;
1209 1208 }
1210 1209
1211 1210 fd = _open_osfhandle((intptr_t)handle, flags);
1212 1211
1213 1212 if (fd == -1) {
1214 1213 CloseHandle(handle);
1215 1214 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
1216 1215 goto bail;
1217 1216 }
1218 1217 #ifndef IS_PY3K
1219 1218 fp = _fdopen(fd, fpmode);
1220 1219 if (fp == NULL) {
1221 1220 _close(fd);
1222 1221 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
1223 1222 goto bail;
1224 1223 }
1225 1224
1226 1225 file_obj = PyFile_FromFile(fp, name, mode, fclose);
1227 1226 if (file_obj == NULL) {
1228 1227 fclose(fp);
1229 1228 goto bail;
1230 1229 }
1231 1230
1232 1231 PyFile_SetBufSize(file_obj, bufsize);
1233 1232 #else
1234 1233 file_obj = PyFile_FromFd(fd, name, mode, bufsize, NULL, NULL, NULL, 1);
1235 1234 if (file_obj == NULL)
1236 1235 goto bail;
1237 1236 #endif
1238 1237 bail:
1239 1238 PyMem_Free(name);
1240 1239 return file_obj;
1241 1240 }
1242 1241 #endif
1243 1242
1244 1243 #ifdef __APPLE__
1245 1244 #include <ApplicationServices/ApplicationServices.h>
1246 1245
1247 1246 static PyObject *isgui(PyObject *self)
1248 1247 {
1249 1248 CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
1250 1249
1251 1250 if (dict != NULL) {
1252 1251 CFRelease(dict);
1253 1252 Py_RETURN_TRUE;
1254 1253 } else {
1255 1254 Py_RETURN_FALSE;
1256 1255 }
1257 1256 }
1258 1257 #endif
1259 1258
1260 1259 static char osutil_doc[] = "Native operating system services.";
1261 1260
1262 1261 static PyMethodDef methods[] = {
1263 1262 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
1264 1263 "list a directory\n"},
1265 1264 #ifdef _WIN32
1266 1265 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS,
1267 1266 "Open a file with POSIX-like semantics.\n"
1268 1267 "On error, this function may raise either a WindowsError or an IOError."},
1269 1268 #else
1270 1269 {"statfiles", (PyCFunction)statfiles, METH_VARARGS | METH_KEYWORDS,
1271 1270 "stat a series of files or symlinks\n"
1272 1271 "Returns None for non-existent entries and entries of other types.\n"},
1273 1272 #ifdef CMSG_LEN
1274 1273 {"recvfds", (PyCFunction)recvfds, METH_VARARGS,
1275 1274 "receive list of file descriptors via socket\n"},
1276 1275 #endif
1277 1276 #ifndef SETPROCNAME_USE_NONE
1278 1277 {"setprocname", (PyCFunction)setprocname, METH_VARARGS,
1279 1278 "set process title (best-effort)\n"},
1280 1279 #endif
1281 #ifdef HAVE_STATFS
1280 #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS)
1282 1281 {"getfstype", (PyCFunction)pygetfstype, METH_VARARGS,
1283 1282 "get filesystem type (best-effort)\n"},
1284 1283 #endif
1285 1284 #endif /* ndef _WIN32 */
1286 1285 #ifdef __APPLE__
1287 1286 {
1288 1287 "isgui", (PyCFunction)isgui, METH_NOARGS,
1289 1288 "Is a CoreGraphics session available?"
1290 1289 },
1291 1290 #endif
1292 1291 {NULL, NULL}
1293 1292 };
1294 1293
1295 1294 #ifdef IS_PY3K
1296 1295 static struct PyModuleDef osutil_module = {
1297 1296 PyModuleDef_HEAD_INIT,
1298 1297 "osutil",
1299 1298 osutil_doc,
1300 1299 -1,
1301 1300 methods
1302 1301 };
1303 1302
1304 1303 PyMODINIT_FUNC PyInit_osutil(void)
1305 1304 {
1306 1305 if (PyType_Ready(&listdir_stat_type) < 0)
1307 1306 return NULL;
1308 1307
1309 1308 return PyModule_Create(&osutil_module);
1310 1309 }
1311 1310 #else
1312 1311 PyMODINIT_FUNC initosutil(void)
1313 1312 {
1314 1313 if (PyType_Ready(&listdir_stat_type) == -1)
1315 1314 return;
1316 1315
1317 1316 Py_InitModule3("osutil", methods, osutil_doc);
1318 1317 }
1319 1318 #endif
@@ -1,792 +1,789 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 # We have issues with setuptools on some platforms and builders. Until
67 67 # those are resolved, setuptools is opt-in except for platforms where
68 68 # we don't have issues.
69 69 if os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ:
70 70 from setuptools import setup
71 71 else:
72 72 from distutils.core import setup
73 73 from distutils.ccompiler import new_compiler
74 74 from distutils.core import Command, Extension
75 75 from distutils.dist import Distribution
76 76 from distutils.command.build import build
77 77 from distutils.command.build_ext import build_ext
78 78 from distutils.command.build_py import build_py
79 79 from distutils.command.build_scripts import build_scripts
80 80 from distutils.command.install_lib import install_lib
81 81 from distutils.command.install_scripts import install_scripts
82 82 from distutils.spawn import spawn, find_executable
83 83 from distutils import file_util
84 84 from distutils.errors import (
85 85 CCompilerError,
86 86 DistutilsError,
87 87 DistutilsExecError,
88 88 )
89 89 from distutils.sysconfig import get_python_inc, get_config_var
90 90 from distutils.version import StrictVersion
91 91
92 92 scripts = ['hg']
93 93 if os.name == 'nt':
94 94 # We remove hg.bat if we are able to build hg.exe.
95 95 scripts.append('contrib/win32/hg.bat')
96 96
97 97 def cancompile(cc, code):
98 98 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
99 99 devnull = oldstderr = None
100 100 try:
101 101 fname = os.path.join(tmpdir, 'testcomp.c')
102 102 f = open(fname, 'w')
103 103 f.write(code)
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 # simplified version of distutils.ccompiler.CCompiler.has_function
125 125 # that actually removes its temporary files.
126 126 def hasfunction(cc, funcname):
127 127 code = 'int main(void) { %s(); }\n' % funcname
128 128 return cancompile(cc, code)
129 129
130 130 def hasheader(cc, headername):
131 131 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
132 132 return cancompile(cc, code)
133 133
134 134 # py2exe needs to be installed to work
135 135 try:
136 136 import py2exe
137 137 py2exe.Distribution # silence unused import warning
138 138 py2exeloaded = True
139 139 # import py2exe's patched Distribution class
140 140 from distutils.core import Distribution
141 141 except ImportError:
142 142 py2exeloaded = False
143 143
144 144 def runcmd(cmd, env):
145 145 if (sys.platform == 'plan9'
146 146 and (sys.version_info[0] == 2 and sys.version_info[1] < 7)):
147 147 # subprocess kludge to work around issues in half-baked Python
148 148 # ports, notably bichued/python:
149 149 _, out, err = os.popen3(cmd)
150 150 return str(out), str(err)
151 151 else:
152 152 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
153 153 stderr=subprocess.PIPE, env=env)
154 154 out, err = p.communicate()
155 155 return out, err
156 156
157 157 def runhg(cmd, env):
158 158 out, err = runcmd(cmd, env)
159 159 # If root is executing setup.py, but the repository is owned by
160 160 # another user (as in "sudo python setup.py install") we will get
161 161 # trust warnings since the .hg/hgrc file is untrusted. That is
162 162 # fine, we don't want to load it anyway. Python may warn about
163 163 # a missing __init__.py in mercurial/locale, we also ignore that.
164 164 err = [e for e in err.splitlines()
165 165 if not e.startswith(b'not trusting file') \
166 166 and not e.startswith(b'warning: Not importing') \
167 167 and not e.startswith(b'obsolete feature not enabled')]
168 168 if err:
169 169 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
170 170 printf(b'\n'.join([b' ' + e for e in err]), file=sys.stderr)
171 171 return ''
172 172 return out
173 173
174 174 version = ''
175 175
176 176 # Execute hg out of this directory with a custom environment which takes care
177 177 # to not use any hgrc files and do no localization.
178 178 env = {'HGMODULEPOLICY': 'py',
179 179 'HGRCPATH': '',
180 180 'LANGUAGE': 'C',
181 181 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
182 182 if 'LD_LIBRARY_PATH' in os.environ:
183 183 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
184 184 if 'SystemRoot' in os.environ:
185 185 # Copy SystemRoot into the custom environment for Python 2.6
186 186 # under Windows. Otherwise, the subprocess will fail with
187 187 # error 0xc0150004. See: http://bugs.python.org/issue3440
188 188 env['SystemRoot'] = os.environ['SystemRoot']
189 189
190 190 if os.path.isdir('.hg'):
191 191 cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
192 192 numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
193 193 hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()
194 194 if numerictags: # tag(s) found
195 195 version = numerictags[-1]
196 196 if hgid.endswith('+'): # propagate the dirty status to the tag
197 197 version += '+'
198 198 else: # no tag found
199 199 ltagcmd = [sys.executable, 'hg', 'parents', '--template',
200 200 '{latesttag}']
201 201 ltag = runhg(ltagcmd, env)
202 202 changessincecmd = [sys.executable, 'hg', 'log', '-T', 'x\n', '-r',
203 203 "only(.,'%s')" % ltag]
204 204 changessince = len(runhg(changessincecmd, env).splitlines())
205 205 version = '%s+%s-%s' % (ltag, changessince, hgid)
206 206 if version.endswith('+'):
207 207 version += time.strftime('%Y%m%d')
208 208 elif os.path.exists('.hg_archival.txt'):
209 209 kw = dict([[t.strip() for t in l.split(':', 1)]
210 210 for l in open('.hg_archival.txt')])
211 211 if 'tag' in kw:
212 212 version = kw['tag']
213 213 elif 'latesttag' in kw:
214 214 if 'changessincelatesttag' in kw:
215 215 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
216 216 else:
217 217 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
218 218 else:
219 219 version = kw.get('node', '')[:12]
220 220
221 221 if version:
222 222 with open("mercurial/__version__.py", "w") as f:
223 223 f.write('# this file is autogenerated by setup.py\n')
224 224 f.write('version = "%s"\n' % version)
225 225
226 226 try:
227 227 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
228 228 os.environ['HGMODULEPOLICY'] = 'py'
229 229 from mercurial import __version__
230 230 version = __version__.version
231 231 except ImportError:
232 232 version = 'unknown'
233 233 finally:
234 234 if oldpolicy is None:
235 235 del os.environ['HGMODULEPOLICY']
236 236 else:
237 237 os.environ['HGMODULEPOLICY'] = oldpolicy
238 238
239 239 class hgbuild(build):
240 240 # Insert hgbuildmo first so that files in mercurial/locale/ are found
241 241 # when build_py is run next.
242 242 sub_commands = [('build_mo', None)] + build.sub_commands
243 243
244 244 class hgbuildmo(build):
245 245
246 246 description = "build translations (.mo files)"
247 247
248 248 def run(self):
249 249 if not find_executable('msgfmt'):
250 250 self.warn("could not find msgfmt executable, no translations "
251 251 "will be built")
252 252 return
253 253
254 254 podir = 'i18n'
255 255 if not os.path.isdir(podir):
256 256 self.warn("could not find %s/ directory" % podir)
257 257 return
258 258
259 259 join = os.path.join
260 260 for po in os.listdir(podir):
261 261 if not po.endswith('.po'):
262 262 continue
263 263 pofile = join(podir, po)
264 264 modir = join('locale', po[:-3], 'LC_MESSAGES')
265 265 mofile = join(modir, 'hg.mo')
266 266 mobuildfile = join('mercurial', mofile)
267 267 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
268 268 if sys.platform != 'sunos5':
269 269 # msgfmt on Solaris does not know about -c
270 270 cmd.append('-c')
271 271 self.mkpath(join('mercurial', modir))
272 272 self.make_file([pofile], mobuildfile, spawn, (cmd,))
273 273
274 274
275 275 class hgdist(Distribution):
276 276 pure = False
277 277 cffi = ispypy
278 278
279 279 global_options = Distribution.global_options + \
280 280 [('pure', None, "use pure (slow) Python "
281 281 "code instead of C extensions"),
282 282 ]
283 283
284 284 def has_ext_modules(self):
285 285 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
286 286 # too late for some cases
287 287 return not self.pure and Distribution.has_ext_modules(self)
288 288
289 289 # This is ugly as a one-liner. So use a variable.
290 290 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
291 291 buildextnegops['no-zstd'] = 'zstd'
292 292
293 293 class hgbuildext(build_ext):
294 294 user_options = build_ext.user_options + [
295 295 ('zstd', None, 'compile zstd bindings [default]'),
296 296 ('no-zstd', None, 'do not compile zstd bindings'),
297 297 ]
298 298
299 299 boolean_options = build_ext.boolean_options + ['zstd']
300 300 negative_opt = buildextnegops
301 301
302 302 def initialize_options(self):
303 303 self.zstd = True
304 304 return build_ext.initialize_options(self)
305 305
306 306 def build_extensions(self):
307 307 # Filter out zstd if disabled via argument.
308 308 if not self.zstd:
309 309 self.extensions = [e for e in self.extensions
310 310 if e.name != 'mercurial.zstd']
311 311
312 312 return build_ext.build_extensions(self)
313 313
314 314 def build_extension(self, ext):
315 315 try:
316 316 build_ext.build_extension(self, ext)
317 317 except CCompilerError:
318 318 if not getattr(ext, 'optional', False):
319 319 raise
320 320 log.warn("Failed to build optional extension '%s' (skipping)",
321 321 ext.name)
322 322
323 323 class hgbuildscripts(build_scripts):
324 324 def run(self):
325 325 if os.name != 'nt' or self.distribution.pure:
326 326 return build_scripts.run(self)
327 327
328 328 exebuilt = False
329 329 try:
330 330 self.run_command('build_hgexe')
331 331 exebuilt = True
332 332 except (DistutilsError, CCompilerError):
333 333 log.warn('failed to build optional hg.exe')
334 334
335 335 if exebuilt:
336 336 # Copying hg.exe to the scripts build directory ensures it is
337 337 # installed by the install_scripts command.
338 338 hgexecommand = self.get_finalized_command('build_hgexe')
339 339 dest = os.path.join(self.build_dir, 'hg.exe')
340 340 self.mkpath(self.build_dir)
341 341 self.copy_file(hgexecommand.hgexepath, dest)
342 342
343 343 # Remove hg.bat because it is redundant with hg.exe.
344 344 self.scripts.remove('contrib/win32/hg.bat')
345 345
346 346 return build_scripts.run(self)
347 347
348 348 class hgbuildpy(build_py):
349 349 def finalize_options(self):
350 350 build_py.finalize_options(self)
351 351
352 352 if self.distribution.pure:
353 353 self.distribution.ext_modules = []
354 354 elif self.distribution.cffi:
355 355 from mercurial.cffi import (
356 356 bdiff,
357 357 mpatch,
358 358 )
359 359 exts = [mpatch.ffi.distutils_extension(),
360 360 bdiff.ffi.distutils_extension()]
361 361 # cffi modules go here
362 362 if sys.platform == 'darwin':
363 363 from mercurial.cffi import osutil
364 364 exts.append(osutil.ffi.distutils_extension())
365 365 self.distribution.ext_modules = exts
366 366 else:
367 367 h = os.path.join(get_python_inc(), 'Python.h')
368 368 if not os.path.exists(h):
369 369 raise SystemExit('Python headers are required to build '
370 370 'Mercurial but weren\'t found in %s' % h)
371 371
372 372 def run(self):
373 373 if self.distribution.pure:
374 374 modulepolicy = 'py'
375 375 else:
376 376 modulepolicy = 'c'
377 377 with open("mercurial/__modulepolicy__.py", "w") as f:
378 378 f.write('# this file is autogenerated by setup.py\n')
379 379 f.write('modulepolicy = b"%s"\n' % modulepolicy)
380 380
381 381 build_py.run(self)
382 382
383 383 class buildhgextindex(Command):
384 384 description = 'generate prebuilt index of hgext (for frozen package)'
385 385 user_options = []
386 386 _indexfilename = 'hgext/__index__.py'
387 387
388 388 def initialize_options(self):
389 389 pass
390 390
391 391 def finalize_options(self):
392 392 pass
393 393
394 394 def run(self):
395 395 if os.path.exists(self._indexfilename):
396 396 with open(self._indexfilename, 'w') as f:
397 397 f.write('# empty\n')
398 398
399 399 # here no extension enabled, disabled() lists up everything
400 400 code = ('import pprint; from mercurial import extensions; '
401 401 'pprint.pprint(extensions.disabled())')
402 402 out, err = runcmd([sys.executable, '-c', code], env)
403 403 if err:
404 404 raise DistutilsExecError(err)
405 405
406 406 with open(self._indexfilename, 'w') as f:
407 407 f.write('# this file is autogenerated by setup.py\n')
408 408 f.write('docs = ')
409 409 f.write(out)
410 410
411 411 class buildhgexe(build_ext):
412 412 description = 'compile hg.exe from mercurial/exewrapper.c'
413 413
414 414 def build_extensions(self):
415 415 if os.name != 'nt':
416 416 return
417 417 if isinstance(self.compiler, HackedMingw32CCompiler):
418 418 self.compiler.compiler_so = self.compiler.compiler # no -mdll
419 419 self.compiler.dll_libraries = [] # no -lmsrvc90
420 420
421 421 # Different Python installs can have different Python library
422 422 # names. e.g. the official CPython distribution uses pythonXY.dll
423 423 # and MinGW uses libpythonX.Y.dll.
424 424 _kernel32 = ctypes.windll.kernel32
425 425 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
426 426 ctypes.c_void_p,
427 427 ctypes.c_ulong]
428 428 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
429 429 size = 1000
430 430 buf = ctypes.create_string_buffer(size + 1)
431 431 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
432 432 size)
433 433
434 434 if filelen > 0 and filelen != size:
435 435 dllbasename = os.path.basename(buf.value)
436 436 if not dllbasename.lower().endswith('.dll'):
437 437 raise SystemExit('Python DLL does not end with .dll: %s' %
438 438 dllbasename)
439 439 pythonlib = dllbasename[:-4]
440 440 else:
441 441 log.warn('could not determine Python DLL filename; '
442 442 'assuming pythonXY')
443 443
444 444 hv = sys.hexversion
445 445 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
446 446
447 447 log.info('using %s as Python library name' % pythonlib)
448 448 with open('mercurial/hgpythonlib.h', 'wb') as f:
449 449 f.write('/* this file is autogenerated by setup.py */\n')
450 450 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
451 451 objects = self.compiler.compile(['mercurial/exewrapper.c'],
452 452 output_dir=self.build_temp)
453 453 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
454 454 target = os.path.join(dir, 'hg')
455 455 self.compiler.link_executable(objects, target,
456 456 libraries=[],
457 457 output_dir=self.build_temp)
458 458
459 459 @property
460 460 def hgexepath(self):
461 461 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
462 462 return os.path.join(self.build_temp, dir, 'hg.exe')
463 463
464 464 class hginstalllib(install_lib):
465 465 '''
466 466 This is a specialization of install_lib that replaces the copy_file used
467 467 there so that it supports setting the mode of files after copying them,
468 468 instead of just preserving the mode that the files originally had. If your
469 469 system has a umask of something like 027, preserving the permissions when
470 470 copying will lead to a broken install.
471 471
472 472 Note that just passing keep_permissions=False to copy_file would be
473 473 insufficient, as it might still be applying a umask.
474 474 '''
475 475
476 476 def run(self):
477 477 realcopyfile = file_util.copy_file
478 478 def copyfileandsetmode(*args, **kwargs):
479 479 src, dst = args[0], args[1]
480 480 dst, copied = realcopyfile(*args, **kwargs)
481 481 if copied:
482 482 st = os.stat(src)
483 483 # Persist executable bit (apply it to group and other if user
484 484 # has it)
485 485 if st[stat.ST_MODE] & stat.S_IXUSR:
486 486 setmode = int('0755', 8)
487 487 else:
488 488 setmode = int('0644', 8)
489 489 m = stat.S_IMODE(st[stat.ST_MODE])
490 490 m = (m & ~int('0777', 8)) | setmode
491 491 os.chmod(dst, m)
492 492 file_util.copy_file = copyfileandsetmode
493 493 try:
494 494 install_lib.run(self)
495 495 finally:
496 496 file_util.copy_file = realcopyfile
497 497
498 498 class hginstallscripts(install_scripts):
499 499 '''
500 500 This is a specialization of install_scripts that replaces the @LIBDIR@ with
501 501 the configured directory for modules. If possible, the path is made relative
502 502 to the directory for scripts.
503 503 '''
504 504
505 505 def initialize_options(self):
506 506 install_scripts.initialize_options(self)
507 507
508 508 self.install_lib = None
509 509
510 510 def finalize_options(self):
511 511 install_scripts.finalize_options(self)
512 512 self.set_undefined_options('install',
513 513 ('install_lib', 'install_lib'))
514 514
515 515 def run(self):
516 516 install_scripts.run(self)
517 517
518 518 # It only makes sense to replace @LIBDIR@ with the install path if
519 519 # the install path is known. For wheels, the logic below calculates
520 520 # the libdir to be "../..". This is because the internal layout of a
521 521 # wheel archive looks like:
522 522 #
523 523 # mercurial-3.6.1.data/scripts/hg
524 524 # mercurial/__init__.py
525 525 #
526 526 # When installing wheels, the subdirectories of the "<pkg>.data"
527 527 # directory are translated to system local paths and files therein
528 528 # are copied in place. The mercurial/* files are installed into the
529 529 # site-packages directory. However, the site-packages directory
530 530 # isn't known until wheel install time. This means we have no clue
531 531 # at wheel generation time what the installed site-packages directory
532 532 # will be. And, wheels don't appear to provide the ability to register
533 533 # custom code to run during wheel installation. This all means that
534 534 # we can't reliably set the libdir in wheels: the default behavior
535 535 # of looking in sys.path must do.
536 536
537 537 if (os.path.splitdrive(self.install_dir)[0] !=
538 538 os.path.splitdrive(self.install_lib)[0]):
539 539 # can't make relative paths from one drive to another, so use an
540 540 # absolute path instead
541 541 libdir = self.install_lib
542 542 else:
543 543 common = os.path.commonprefix((self.install_dir, self.install_lib))
544 544 rest = self.install_dir[len(common):]
545 545 uplevel = len([n for n in os.path.split(rest) if n])
546 546
547 547 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
548 548
549 549 for outfile in self.outfiles:
550 550 with open(outfile, 'rb') as fp:
551 551 data = fp.read()
552 552
553 553 # skip binary files
554 554 if b'\0' in data:
555 555 continue
556 556
557 557 # During local installs, the shebang will be rewritten to the final
558 558 # install path. During wheel packaging, the shebang has a special
559 559 # value.
560 560 if data.startswith(b'#!python'):
561 561 log.info('not rewriting @LIBDIR@ in %s because install path '
562 562 'not known' % outfile)
563 563 continue
564 564
565 565 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
566 566 with open(outfile, 'wb') as fp:
567 567 fp.write(data)
568 568
569 569 cmdclass = {'build': hgbuild,
570 570 'build_mo': hgbuildmo,
571 571 'build_ext': hgbuildext,
572 572 'build_py': hgbuildpy,
573 573 'build_scripts': hgbuildscripts,
574 574 'build_hgextindex': buildhgextindex,
575 575 'install_lib': hginstalllib,
576 576 'install_scripts': hginstallscripts,
577 577 'build_hgexe': buildhgexe,
578 578 }
579 579
580 580 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
581 581 'mercurial.pure',
582 582 'hgext', 'hgext.convert', 'hgext.fsmonitor',
583 583 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
584 584 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd']
585 585
586 586 common_depends = ['mercurial/bitmanipulation.h',
587 587 'mercurial/compat.h',
588 588 'mercurial/util.h']
589 589
590 590 osutil_cflags = []
591 591 osutil_ldflags = []
592 592
593 593 # platform specific macros
594 for plat, func in [('bsd', 'setproctitle'), ('bsd|darwin|linux', 'statfs')]:
594 for plat, func in [('bsd', 'setproctitle')]:
595 595 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
596 596 osutil_cflags.append('-DHAVE_%s' % func.upper())
597 597
598 for plat, header in [
599 ('linux', 'linux/magic.h'),
600 ('linux', 'sys/vfs.h'),
601 ]:
602 if re.search(plat, sys.platform) and hasheader(new_compiler(), header):
603 macro = header.replace('/', '_').replace('.', '_').upper()
604 osutil_cflags.append('-DHAVE_%s' % macro)
605
606 598 for plat, macro, code in [
607 599 ('bsd|darwin', 'BSD_STATFS', '''
608 600 #include <sys/param.h>
609 601 #include <sys/mount.h>
610 602 int main() { struct statfs s; return sizeof(s.f_fstypename); }
611 603 '''),
604 ('linux', 'LINUX_STATFS', '''
605 #include <linux/magic.h>
606 #include <sys/vfs.h>
607 int main() { struct statfs s; return sizeof(s.f_type); }
608 '''),
612 609 ]:
613 610 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
614 611 osutil_cflags.append('-DHAVE_%s' % macro)
615 612
616 613 if sys.platform == 'darwin':
617 614 osutil_ldflags += ['-framework', 'ApplicationServices']
618 615
619 616 extmodules = [
620 617 Extension('mercurial.base85', ['mercurial/base85.c'],
621 618 depends=common_depends),
622 619 Extension('mercurial.bdiff', ['mercurial/bdiff.c',
623 620 'mercurial/bdiff_module.c'],
624 621 depends=common_depends + ['mercurial/bdiff.h']),
625 622 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
626 623 depends=common_depends),
627 624 Extension('mercurial.mpatch', ['mercurial/mpatch.c',
628 625 'mercurial/mpatch_module.c'],
629 626 depends=common_depends),
630 627 Extension('mercurial.parsers', ['mercurial/dirs.c',
631 628 'mercurial/manifest.c',
632 629 'mercurial/parsers.c',
633 630 'mercurial/pathencode.c'],
634 631 depends=common_depends),
635 632 Extension('mercurial.osutil', ['mercurial/osutil.c'],
636 633 extra_compile_args=osutil_cflags,
637 634 extra_link_args=osutil_ldflags,
638 635 depends=common_depends),
639 636 Extension('hgext.fsmonitor.pywatchman.bser',
640 637 ['hgext/fsmonitor/pywatchman/bser.c']),
641 638 ]
642 639
643 640 sys.path.insert(0, 'contrib/python-zstandard')
644 641 import setup_zstd
645 642 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
646 643
647 644 try:
648 645 from distutils import cygwinccompiler
649 646
650 647 # the -mno-cygwin option has been deprecated for years
651 648 compiler = cygwinccompiler.Mingw32CCompiler
652 649
653 650 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
654 651 def __init__(self, *args, **kwargs):
655 652 compiler.__init__(self, *args, **kwargs)
656 653 for i in 'compiler compiler_so linker_exe linker_so'.split():
657 654 try:
658 655 getattr(self, i).remove('-mno-cygwin')
659 656 except ValueError:
660 657 pass
661 658
662 659 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
663 660 except ImportError:
664 661 # the cygwinccompiler package is not available on some Python
665 662 # distributions like the ones from the optware project for Synology
666 663 # DiskStation boxes
667 664 class HackedMingw32CCompiler(object):
668 665 pass
669 666
670 667 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
671 668 'help/*.txt',
672 669 'help/internals/*.txt',
673 670 'default.d/*.rc',
674 671 'dummycert.pem']}
675 672
676 673 def ordinarypath(p):
677 674 return p and p[0] != '.' and p[-1] != '~'
678 675
679 676 for root in ('templates',):
680 677 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
681 678 curdir = curdir.split(os.sep, 1)[1]
682 679 dirs[:] = filter(ordinarypath, dirs)
683 680 for f in filter(ordinarypath, files):
684 681 f = os.path.join(curdir, f)
685 682 packagedata['mercurial'].append(f)
686 683
687 684 datafiles = []
688 685
689 686 # distutils expects version to be str/unicode. Converting it to
690 687 # unicode on Python 2 still works because it won't contain any
691 688 # non-ascii bytes and will be implicitly converted back to bytes
692 689 # when operated on.
693 690 assert isinstance(version, bytes)
694 691 setupversion = version.decode('ascii')
695 692
696 693 extra = {}
697 694
698 695 if py2exeloaded:
699 696 extra['console'] = [
700 697 {'script':'hg',
701 698 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
702 699 'product_version':version}]
703 700 # sub command of 'build' because 'py2exe' does not handle sub_commands
704 701 build.sub_commands.insert(0, ('build_hgextindex', None))
705 702 # put dlls in sub directory so that they won't pollute PATH
706 703 extra['zipfile'] = 'lib/library.zip'
707 704
708 705 if os.name == 'nt':
709 706 # Windows binary file versions for exe/dll files must have the
710 707 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
711 708 setupversion = version.split('+', 1)[0]
712 709
713 710 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
714 711 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
715 712 if version:
716 713 version = version[0]
717 714 if sys.version_info[0] == 3:
718 715 version = version.decode('utf-8')
719 716 xcode4 = (version.startswith('Xcode') and
720 717 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
721 718 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
722 719 else:
723 720 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
724 721 # installed, but instead with only command-line tools. Assume
725 722 # that only happens on >= Lion, thus no PPC support.
726 723 xcode4 = True
727 724 xcode51 = False
728 725
729 726 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
730 727 # distutils.sysconfig
731 728 if xcode4:
732 729 os.environ['ARCHFLAGS'] = ''
733 730
734 731 # XCode 5.1 changes clang such that it now fails to compile if the
735 732 # -mno-fused-madd flag is passed, but the version of Python shipped with
736 733 # OS X 10.9 Mavericks includes this flag. This causes problems in all
737 734 # C extension modules, and a bug has been filed upstream at
738 735 # http://bugs.python.org/issue21244. We also need to patch this here
739 736 # so Mercurial can continue to compile in the meantime.
740 737 if xcode51:
741 738 cflags = get_config_var('CFLAGS')
742 739 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
743 740 os.environ['CFLAGS'] = (
744 741 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
745 742
746 743 setup(name='mercurial',
747 744 version=setupversion,
748 745 author='Matt Mackall and many others',
749 746 author_email='mercurial@mercurial-scm.org',
750 747 url='https://mercurial-scm.org/',
751 748 download_url='https://mercurial-scm.org/release/',
752 749 description=('Fast scalable distributed SCM (revision control, version '
753 750 'control) system'),
754 751 long_description=('Mercurial is a distributed SCM tool written in Python.'
755 752 ' It is used by a number of large projects that require'
756 753 ' fast, reliable distributed revision control, such as '
757 754 'Mozilla.'),
758 755 license='GNU GPLv2 or any later version',
759 756 classifiers=[
760 757 'Development Status :: 6 - Mature',
761 758 'Environment :: Console',
762 759 'Intended Audience :: Developers',
763 760 'Intended Audience :: System Administrators',
764 761 'License :: OSI Approved :: GNU General Public License (GPL)',
765 762 'Natural Language :: Danish',
766 763 'Natural Language :: English',
767 764 'Natural Language :: German',
768 765 'Natural Language :: Italian',
769 766 'Natural Language :: Japanese',
770 767 'Natural Language :: Portuguese (Brazilian)',
771 768 'Operating System :: Microsoft :: Windows',
772 769 'Operating System :: OS Independent',
773 770 'Operating System :: POSIX',
774 771 'Programming Language :: C',
775 772 'Programming Language :: Python',
776 773 'Topic :: Software Development :: Version Control',
777 774 ],
778 775 scripts=scripts,
779 776 packages=packages,
780 777 ext_modules=extmodules,
781 778 data_files=datafiles,
782 779 package_data=packagedata,
783 780 cmdclass=cmdclass,
784 781 distclass=hgdist,
785 782 options={'py2exe': {'packages': ['hgext', 'email']},
786 783 'bdist_mpkg': {'zipdist': False,
787 784 'license': 'COPYING',
788 785 'readme': 'contrib/macosx/Readme.html',
789 786 'welcome': 'contrib/macosx/Welcome.html',
790 787 },
791 788 },
792 789 **extra)
General Comments 0
You need to be logged in to leave comments. Login now