Show More
@@ -0,0 +1,325 b'' | |||
|
1 | /* | |
|
2 | osutil.c - native operating system services | |
|
3 | ||
|
4 | Copyright 2007 Matt Mackall and others | |
|
5 | ||
|
6 | This software may be used and distributed according to the terms of | |
|
7 | the GNU General Public License, incorporated herein by reference. | |
|
8 | */ | |
|
9 | ||
|
10 | #define _ATFILE_SOURCE | |
|
11 | #define _LARGEFILE64_SOURCE | |
|
12 | #include <alloca.h> | |
|
13 | #include <dirent.h> | |
|
14 | #include <fcntl.h> | |
|
15 | #include <string.h> | |
|
16 | #include <sys/stat.h> | |
|
17 | #include <sys/types.h> | |
|
18 | #include <unistd.h> | |
|
19 | ||
|
20 | #include "Python.h" | |
|
21 | ||
|
22 | struct listdir_stat { | |
|
23 | PyObject_HEAD | |
|
24 | struct stat st; | |
|
25 | }; | |
|
26 | ||
|
27 | #define listdir_slot(name) \ | |
|
28 | static PyObject *listdir_stat_##name(PyObject *self, void *x) \ | |
|
29 | { \ | |
|
30 | return PyInt_FromLong(((struct listdir_stat *) self)->st.name); \ | |
|
31 | } | |
|
32 | ||
|
33 | listdir_slot(st_dev); | |
|
34 | listdir_slot(st_mode); | |
|
35 | listdir_slot(st_nlink); | |
|
36 | listdir_slot(st_size); | |
|
37 | listdir_slot(st_mtime); | |
|
38 | listdir_slot(st_ctime); | |
|
39 | ||
|
40 | static struct PyGetSetDef listdir_stat_getsets[] = { | |
|
41 | {"st_dev", listdir_stat_st_dev, 0, 0, 0}, | |
|
42 | {"st_mode", listdir_stat_st_mode, 0, 0, 0}, | |
|
43 | {"st_nlink", listdir_stat_st_nlink, 0, 0, 0}, | |
|
44 | {"st_size", listdir_stat_st_size, 0, 0, 0}, | |
|
45 | {"st_mtime", listdir_stat_st_mtime, 0, 0, 0}, | |
|
46 | {"st_ctime", listdir_stat_st_ctime, 0, 0, 0}, | |
|
47 | {0, 0, 0, 0, 0} | |
|
48 | }; | |
|
49 | ||
|
50 | static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k) | |
|
51 | { | |
|
52 | return (*t->tp_alloc)(t, 0); | |
|
53 | } | |
|
54 | ||
|
55 | static void listdir_stat_dealloc(PyObject *o) | |
|
56 | { | |
|
57 | (*o->ob_type->tp_free)(o); | |
|
58 | } | |
|
59 | ||
|
60 | static PyTypeObject listdir_stat_type = { | |
|
61 | PyObject_HEAD_INIT(NULL) | |
|
62 | 0, /*ob_size*/ | |
|
63 | "osutil.stat", /*tp_name*/ | |
|
64 | sizeof(struct listdir_stat), /*tp_basicsize*/ | |
|
65 | 0, /*tp_itemsize*/ | |
|
66 | (destructor)listdir_stat_dealloc, /*tp_dealloc*/ | |
|
67 | 0, /*tp_print*/ | |
|
68 | 0, /*tp_getattr*/ | |
|
69 | 0, /*tp_setattr*/ | |
|
70 | 0, /*tp_compare*/ | |
|
71 | 0, /*tp_repr*/ | |
|
72 | 0, /*tp_as_number*/ | |
|
73 | 0, /*tp_as_sequence*/ | |
|
74 | 0, /*tp_as_mapping*/ | |
|
75 | 0, /*tp_hash */ | |
|
76 | 0, /*tp_call*/ | |
|
77 | 0, /*tp_str*/ | |
|
78 | 0, /*tp_getattro*/ | |
|
79 | 0, /*tp_setattro*/ | |
|
80 | 0, /*tp_as_buffer*/ | |
|
81 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ | |
|
82 | "stat objects", /* tp_doc */ | |
|
83 | 0, /* tp_traverse */ | |
|
84 | 0, /* tp_clear */ | |
|
85 | 0, /* tp_richcompare */ | |
|
86 | 0, /* tp_weaklistoffset */ | |
|
87 | 0, /* tp_iter */ | |
|
88 | 0, /* tp_iternext */ | |
|
89 | 0, /* tp_methods */ | |
|
90 | 0, /* tp_members */ | |
|
91 | listdir_stat_getsets, /* tp_getset */ | |
|
92 | 0, /* tp_base */ | |
|
93 | 0, /* tp_dict */ | |
|
94 | 0, /* tp_descr_get */ | |
|
95 | 0, /* tp_descr_set */ | |
|
96 | 0, /* tp_dictoffset */ | |
|
97 | 0, /* tp_init */ | |
|
98 | 0, /* tp_alloc */ | |
|
99 | listdir_stat_new, /* tp_new */ | |
|
100 | }; | |
|
101 | ||
|
102 | static inline int mode_to_kind(int mode) | |
|
103 | { | |
|
104 | if (S_ISREG(mode)) return S_IFREG; | |
|
105 | if (S_ISDIR(mode)) return S_IFDIR; | |
|
106 | if (S_ISLNK(mode)) return S_IFLNK; | |
|
107 | if (S_ISBLK(mode)) return S_IFBLK; | |
|
108 | if (S_ISCHR(mode)) return S_IFCHR; | |
|
109 | if (S_ISFIFO(mode)) return S_IFIFO; | |
|
110 | if (S_ISSOCK(mode)) return S_IFSOCK; | |
|
111 | return mode; | |
|
112 | } | |
|
113 | ||
|
114 | static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs) | |
|
115 | { | |
|
116 | DIR *dir = NULL; | |
|
117 | struct dirent64 *ent; | |
|
118 | PyObject *list = NULL; | |
|
119 | PyObject *ctor_args = NULL; | |
|
120 | int all_kinds = 1; | |
|
121 | char *full_path; | |
|
122 | int path_len; | |
|
123 | int do_stat; | |
|
124 | char *path; | |
|
125 | int ret; | |
|
126 | ||
|
127 | { | |
|
128 | static char *kwlist[] = { "path", "stat", NULL }; | |
|
129 | PyObject *statobj = NULL; | |
|
130 | ||
|
131 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|O:listdir", kwlist, | |
|
132 | &path, &path_len, &statobj)) | |
|
133 | goto bail; | |
|
134 | ||
|
135 | do_stat = statobj && PyObject_IsTrue(statobj); | |
|
136 | } | |
|
137 | ||
|
138 | if ((dir = opendir(path)) == NULL) { | |
|
139 | list = PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); | |
|
140 | goto bail; | |
|
141 | } | |
|
142 | ||
|
143 | if ((list = PyList_New(0)) == NULL) | |
|
144 | goto bail; | |
|
145 | ||
|
146 | full_path = alloca(path_len + PATH_MAX + 2); | |
|
147 | memcpy(full_path, path, path_len); | |
|
148 | full_path[path_len] = '/'; | |
|
149 | ||
|
150 | while ((ent = readdir64(dir))) { | |
|
151 | PyObject *name = NULL; | |
|
152 | PyObject *py_kind = NULL; | |
|
153 | PyObject *val = NULL; | |
|
154 | unsigned char d_type; | |
|
155 | int kind = -1; | |
|
156 | ||
|
157 | if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) | |
|
158 | continue; | |
|
159 | ||
|
160 | #ifdef DT_REG | |
|
161 | if (do_stat) | |
|
162 | d_type = 0; | |
|
163 | else | |
|
164 | d_type = ent->d_type; | |
|
165 | #else | |
|
166 | d_type = 0; | |
|
167 | #endif | |
|
168 | ||
|
169 | switch (d_type) { | |
|
170 | #ifdef DT_REG | |
|
171 | case DT_REG: kind = S_IFREG; break; | |
|
172 | case DT_DIR: kind = S_IFDIR; break; | |
|
173 | case DT_LNK: kind = S_IFLNK; break; | |
|
174 | case DT_BLK: kind = S_IFBLK; break; | |
|
175 | case DT_CHR: kind = S_IFCHR; break; | |
|
176 | case DT_FIFO: kind = S_IFIFO; break; | |
|
177 | case DT_SOCK: kind = S_IFSOCK; break; | |
|
178 | #endif | |
|
179 | default: | |
|
180 | if (all_kinds) | |
|
181 | all_kinds = 0; | |
|
182 | break; | |
|
183 | } | |
|
184 | ||
|
185 | name = PyString_FromString(ent->d_name); | |
|
186 | if (kind != -1) | |
|
187 | py_kind = PyInt_FromLong(kind); | |
|
188 | else { | |
|
189 | py_kind = Py_None; | |
|
190 | Py_INCREF(Py_None); | |
|
191 | } | |
|
192 | ||
|
193 | val = PyTuple_New(do_stat ? 3 : 2); | |
|
194 | ||
|
195 | if (name == NULL || py_kind == NULL || val == NULL) { | |
|
196 | Py_XDECREF(name); | |
|
197 | Py_XDECREF(py_kind); | |
|
198 | Py_XDECREF(val); | |
|
199 | ||
|
200 | goto bail; | |
|
201 | } | |
|
202 | ||
|
203 | PyTuple_SET_ITEM(val, 0, name); | |
|
204 | PyTuple_SET_ITEM(val, 1, py_kind); | |
|
205 | if (do_stat) { | |
|
206 | PyTuple_SET_ITEM(val, 2, Py_None); | |
|
207 | Py_INCREF(Py_None); | |
|
208 | } | |
|
209 | ||
|
210 | PyList_Append(list, val); | |
|
211 | Py_DECREF(val); | |
|
212 | } | |
|
213 | ||
|
214 | PyList_Sort(list); | |
|
215 | ||
|
216 | if (do_stat || !all_kinds) { | |
|
217 | ssize_t size = PyList_Size(list); | |
|
218 | ssize_t i; | |
|
219 | #ifdef AT_SYMLINK_NOFOLLOW | |
|
220 | int dfd = dirfd(dir); | |
|
221 | #endif | |
|
222 | ||
|
223 | for (i = 0; i < size; i++) { | |
|
224 | PyObject *elt = PyList_GetItem(list, i); | |
|
225 | char *name = PyString_AsString(PyTuple_GET_ITEM(elt, 0)); | |
|
226 | PyObject *py_st = NULL; | |
|
227 | PyObject *py_kind = PyTuple_GET_ITEM(elt, 1); | |
|
228 | int kind; | |
|
229 | ||
|
230 | kind = py_kind == Py_None ? -1 : PyInt_AsLong(py_kind); | |
|
231 | ||
|
232 | if (kind != -1 && !do_stat) | |
|
233 | continue; | |
|
234 | ||
|
235 | strcpy(full_path + path_len + 1, name); | |
|
236 | ||
|
237 | if (do_stat) { | |
|
238 | struct listdir_stat *st; | |
|
239 | ||
|
240 | if (ctor_args == NULL) { | |
|
241 | ctor_args = PyTuple_New(0); | |
|
242 | if (ctor_args == NULL) | |
|
243 | goto bail; | |
|
244 | } | |
|
245 | ||
|
246 | st = (struct listdir_stat *) | |
|
247 | PyObject_CallObject((PyObject *) &listdir_stat_type, | |
|
248 | ctor_args); | |
|
249 | if (st == NULL) | |
|
250 | goto bail; | |
|
251 | #ifdef AT_SYMLINK_NOFOLLOW | |
|
252 | ret = fstatat(dfd, name, &st->st, AT_SYMLINK_NOFOLLOW); | |
|
253 | #else | |
|
254 | ret = lstat(full_path, &st->st); | |
|
255 | #endif | |
|
256 | if (ret == -1) { | |
|
257 | list = PyErr_SetFromErrnoWithFilename(PyExc_OSError, | |
|
258 | full_path); | |
|
259 | goto bail; | |
|
260 | } | |
|
261 | if (kind == -1) | |
|
262 | kind = mode_to_kind(st->st.st_mode); | |
|
263 | py_st = (PyObject *) st; | |
|
264 | } else { | |
|
265 | struct stat buf; | |
|
266 | #ifdef AT_SYMLINK_NOFOLLOW | |
|
267 | ret = fstatat(dfd, ent->d_name, &buf, AT_SYMLINK_NOFOLLOW); | |
|
268 | #else | |
|
269 | ret = lstat(full_path, &buf); | |
|
270 | #endif | |
|
271 | if (ret == -1) { | |
|
272 | list = PyErr_SetFromErrnoWithFilename(PyExc_OSError, | |
|
273 | full_path); | |
|
274 | goto bail; | |
|
275 | } | |
|
276 | if (kind == -1) | |
|
277 | kind = mode_to_kind(buf.st_mode); | |
|
278 | } | |
|
279 | ||
|
280 | if (py_kind == Py_None && kind != -1) { | |
|
281 | py_kind = PyInt_FromLong(kind); | |
|
282 | if (py_kind == NULL) | |
|
283 | goto bail; | |
|
284 | Py_XDECREF(Py_None); | |
|
285 | PyTuple_SET_ITEM(elt, 1, py_kind); | |
|
286 | } | |
|
287 | ||
|
288 | if (do_stat) { | |
|
289 | if (py_st == NULL) { | |
|
290 | py_st = Py_None; | |
|
291 | Py_INCREF(Py_None); | |
|
292 | } | |
|
293 | PyTuple_SET_ITEM(elt, 2, py_st); | |
|
294 | } | |
|
295 | } | |
|
296 | } | |
|
297 | ||
|
298 | goto done; | |
|
299 | ||
|
300 | bail: | |
|
301 | Py_XDECREF(list); | |
|
302 | ||
|
303 | done: | |
|
304 | Py_XDECREF(ctor_args); | |
|
305 | if (dir) | |
|
306 | closedir(dir); | |
|
307 | ||
|
308 | return list; | |
|
309 | } | |
|
310 | ||
|
311 | static char osutil_doc[] = "Native operating system services."; | |
|
312 | ||
|
313 | static PyMethodDef methods[] = { | |
|
314 | {"listdir", (PyCFunction) listdir, METH_VARARGS | METH_KEYWORDS, | |
|
315 | "list a directory\n"}, | |
|
316 | {NULL, NULL} | |
|
317 | }; | |
|
318 | ||
|
319 | PyMODINIT_FUNC initosutil(void) | |
|
320 | { | |
|
321 | if (PyType_Ready(&listdir_stat_type) == -1) | |
|
322 | return; | |
|
323 | ||
|
324 | Py_InitModule3("osutil", methods, osutil_doc); | |
|
325 | } |
@@ -0,0 +1,37 b'' | |||
|
1 | import os, stat | |
|
2 | ||
|
3 | def _mode_to_kind(mode): | |
|
4 | if stat.S_ISREG(mode): return stat.S_IFREG | |
|
5 | if stat.S_ISDIR(mode): return stat.S_IFDIR | |
|
6 | if stat.S_ISLNK(mode): return stat.S_IFLNK | |
|
7 | if stat.S_ISBLK(mode): return stat.S_IFBLK | |
|
8 | if stat.S_ISCHR(mode): return stat.S_IFCHR | |
|
9 | if stat.S_ISFIFO(mode): return stat.S_IFIFO | |
|
10 | if stat.S_ISSOCK(mode): return stat.S_IFSOCK | |
|
11 | return mode | |
|
12 | ||
|
13 | def listdir(path, stat=False): | |
|
14 | '''listdir(path, stat=False) -> list_of_tuples | |
|
15 | ||
|
16 | Return a sorted list containing information about the entries | |
|
17 | in the directory. | |
|
18 | ||
|
19 | If stat is True, each element is a 3-tuple: | |
|
20 | ||
|
21 | (name, type, stat object) | |
|
22 | ||
|
23 | Otherwise, each element is a 2-tuple: | |
|
24 | ||
|
25 | (name, type) | |
|
26 | ''' | |
|
27 | result = [] | |
|
28 | prefix = path + os.sep | |
|
29 | names = os.listdir(path) | |
|
30 | names.sort() | |
|
31 | for fn in names: | |
|
32 | st = os.lstat(prefix + fn) | |
|
33 | if stat: | |
|
34 | result.append((fn, _mode_to_kind(st.st_mode), st)) | |
|
35 | else: | |
|
36 | result.append((fn, _mode_to_kind(st.st_mode))) | |
|
37 | return result |
@@ -10,7 +10,7 b' of the GNU General Public License, incor' | |||
|
10 | 10 | from node import * |
|
11 | 11 | from i18n import _ |
|
12 | 12 | import struct, os, time, bisect, stat, strutil, util, re, errno, ignore |
|
13 | import cStringIO | |
|
13 | import cStringIO, osutil | |
|
14 | 14 | |
|
15 | 15 | _unknown = ('?', 0, 0, 0) |
|
16 | 16 | _format = ">cllll" |
@@ -389,7 +389,7 b' class dirstate(object):' | |||
|
389 | 389 | common_prefix_len += 1 |
|
390 | 390 | |
|
391 | 391 | normpath = util.normpath |
|
392 | listdir = os.listdir | |
|
392 | listdir = osutil.listdir | |
|
393 | 393 | lstat = os.lstat |
|
394 | 394 | bisect_left = bisect.bisect_left |
|
395 | 395 | isdir = os.path.isdir |
@@ -410,8 +410,7 b' class dirstate(object):' | |||
|
410 | 410 | add((normpath(s[common_prefix_len:]), 'd', lstat(s))) |
|
411 | 411 | while work: |
|
412 | 412 | top = work.pop() |
|
413 |
|
|
|
414 | names.sort() | |
|
413 | entries = listdir(top, stat=True) | |
|
415 | 414 | # nd is the top of the repository dir tree |
|
416 | 415 | nd = normpath(top[common_prefix_len:]) |
|
417 | 416 | if nd == '.': |
@@ -420,19 +419,19 b' class dirstate(object):' | |||
|
420 | 419 | # do not recurse into a repo contained in this |
|
421 | 420 | # one. use bisect to find .hg directory so speed |
|
422 | 421 | # is good on big directory. |
|
422 | names = [e[0] for e in entries] | |
|
423 | 423 | hg = bisect_left(names, '.hg') |
|
424 | 424 | if hg < len(names) and names[hg] == '.hg': |
|
425 | 425 | if isdir(join(top, '.hg')): |
|
426 | 426 | continue |
|
427 |
for f in |
|
|
427 | for f, kind, st in entries: | |
|
428 | 428 | np = pconvert(join(nd, f)) |
|
429 | 429 | if np in known: |
|
430 | 430 | continue |
|
431 | 431 | known[np] = 1 |
|
432 | 432 | p = join(top, f) |
|
433 | 433 | # don't trip over symlinks |
|
434 |
|
|
|
435 | if s_isdir(st.st_mode): | |
|
434 | if kind == stat.S_IFDIR: | |
|
436 | 435 | if not ignore(np): |
|
437 | 436 | wadd(p) |
|
438 | 437 | if directories: |
@@ -6,7 +6,7 b'' | |||
|
6 | 6 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | 7 | |
|
8 | 8 | from i18n import _ |
|
9 | import os, stat, util, lock | |
|
9 | import os, osutil, stat, util, lock | |
|
10 | 10 | |
|
11 | 11 | # if server supports streaming clone, it advertises "stream" |
|
12 | 12 | # capability with value that is version+flags of repo it is serving. |
@@ -19,17 +19,14 b' def walkrepo(root):' | |||
|
19 | 19 | |
|
20 | 20 | strip_count = len(root) + len(os.sep) |
|
21 | 21 | def walk(path, recurse): |
|
22 |
|
|
|
23 | ents.sort() | |
|
24 | for e in ents: | |
|
22 | for e, kind, st in osutil.listdir(path, stat=True): | |
|
25 | 23 | pe = os.path.join(path, e) |
|
26 | st = os.lstat(pe) | |
|
27 | if stat.S_ISDIR(st.st_mode): | |
|
24 | if kind == stat.S_IFDIR: | |
|
28 | 25 | if recurse: |
|
29 | 26 | for x in walk(pe, True): |
|
30 | 27 | yield x |
|
31 | 28 | else: |
|
32 |
if |
|
|
29 | if kind != stat.S_IFREG or len(e) < 2: | |
|
33 | 30 | continue |
|
34 | 31 | sfx = e[-2:] |
|
35 | 32 | if sfx in ('.d', '.i'): |
@@ -14,7 +14,7 b' platform-specific details from the core.' | |||
|
14 | 14 | |
|
15 | 15 | from i18n import _ |
|
16 | 16 | import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil |
|
17 | import os, stat, threading, time, calendar, ConfigParser, locale, glob | |
|
17 | import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil | |
|
18 | 18 | |
|
19 | 19 | try: |
|
20 | 20 | set = set |
@@ -676,7 +676,7 b' def copyfiles(src, dst, hardlink=None):' | |||
|
676 | 676 | |
|
677 | 677 | if os.path.isdir(src): |
|
678 | 678 | os.mkdir(dst) |
|
679 | for name in os.listdir(src): | |
|
679 | for name, kind in osutil.listdir(src): | |
|
680 | 680 | srcname = os.path.join(src, name) |
|
681 | 681 | dstname = os.path.join(dst, name) |
|
682 | 682 | copyfiles(srcname, dstname, hardlink) |
@@ -1060,7 +1060,8 b' else:' | |||
|
1060 | 1060 | rcs = [os.path.join(path, 'hgrc')] |
|
1061 | 1061 | rcdir = os.path.join(path, 'hgrc.d') |
|
1062 | 1062 | try: |
|
1063 |
rcs.extend([os.path.join(rcdir, f) |
|
|
1063 | rcs.extend([os.path.join(rcdir, f) | |
|
1064 | for f, kind in osutil.listdir(rcdir) | |
|
1064 | 1065 | if f.endswith(".rc")]) |
|
1065 | 1066 | except OSError: |
|
1066 | 1067 | pass |
@@ -1653,7 +1654,7 b' def rcpath():' | |||
|
1653 | 1654 | for p in os.environ['HGRCPATH'].split(os.pathsep): |
|
1654 | 1655 | if not p: continue |
|
1655 | 1656 | if os.path.isdir(p): |
|
1656 | for f in os.listdir(p): | |
|
1657 | for f, kind in osutil.listdir(p): | |
|
1657 | 1658 | if f.endswith('.rc'): |
|
1658 | 1659 | _rcpath.append(os.path.join(p, f)) |
|
1659 | 1660 | else: |
@@ -52,6 +52,19 b' class install_package_data(install_data)' | |||
|
52 | 52 | mercurial.version.remember_version(version) |
|
53 | 53 | cmdclass = {'install_data': install_package_data} |
|
54 | 54 | |
|
55 | ext_modules=[ | |
|
56 | Extension('mercurial.mpatch', ['mercurial/mpatch.c']), | |
|
57 | Extension('mercurial.bdiff', ['mercurial/bdiff.c']), | |
|
58 | Extension('mercurial.base85', ['mercurial/base85.c']), | |
|
59 | Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']) | |
|
60 | ] | |
|
61 | ||
|
62 | try: | |
|
63 | import posix | |
|
64 | ext_modules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'])) | |
|
65 | except ImportError: | |
|
66 | pass | |
|
67 | ||
|
55 | 68 | setup(name='mercurial', |
|
56 | 69 | version=mercurial.version.get_version(), |
|
57 | 70 | author='Matt Mackall', |
@@ -60,10 +73,7 b" setup(name='mercurial'," | |||
|
60 | 73 | description='Scalable distributed SCM', |
|
61 | 74 | license='GNU GPL', |
|
62 | 75 | packages=['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert'], |
|
63 | ext_modules=[Extension('mercurial.mpatch', ['mercurial/mpatch.c']), | |
|
64 | Extension('mercurial.bdiff', ['mercurial/bdiff.c']), | |
|
65 | Extension('mercurial.base85', ['mercurial/base85.c']), | |
|
66 | Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'])], | |
|
76 | ext_modules=ext_modules, | |
|
67 | 77 | data_files=[(os.path.join('mercurial', root), |
|
68 | 78 | [os.path.join(root, file_) for file_ in files]) |
|
69 | 79 | for root, dirs, files in os.walk('templates')], |
General Comments 0
You need to be logged in to leave comments.
Login now