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