##// END OF EJS Templates
building: build inotify for sys.platform='linux*'...
Nikolaj Sjujskij -
r15163:f4bc0b9e stable
parent child Browse files
Show More
@@ -1,373 +1,373 b''
1 1 #
2 2 # This is an experimental py3k-enabled mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6
7 7 from distutils.command.build_py import build_py_2to3
8 8 from lib2to3.refactor import get_fixers_from_package as getfixers
9 9
10 10 import sys
11 11 if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
12 12 raise SystemExit("Mercurial requires Python 2.4 or later.")
13 13
14 14 if sys.version_info[0] >= 3:
15 15 def b(s):
16 16 '''A helper function to emulate 2.6+ bytes literals using string
17 17 literals.'''
18 18 return s.encode('latin1')
19 19 else:
20 20 def b(s):
21 21 '''A helper function to emulate 2.6+ bytes literals using string
22 22 literals.'''
23 23 return s
24 24
25 25 # Solaris Python packaging brain damage
26 26 try:
27 27 import hashlib
28 28 sha = hashlib.sha1()
29 29 except:
30 30 try:
31 31 import sha
32 32 except:
33 33 raise SystemExit(
34 34 "Couldn't import standard hashlib (incomplete Python install).")
35 35
36 36 try:
37 37 import zlib
38 38 except:
39 39 raise SystemExit(
40 40 "Couldn't import standard zlib (incomplete Python install).")
41 41
42 42 try:
43 43 import bz2
44 44 except:
45 45 raise SystemExit(
46 46 "Couldn't import standard bz2 (incomplete Python install).")
47 47
48 48 import os, subprocess, time
49 49 import shutil
50 50 import tempfile
51 51 from distutils import log
52 52 from distutils.core import setup, Extension
53 53 from distutils.dist import Distribution
54 54 from distutils.command.build import build
55 55 from distutils.command.build_ext import build_ext
56 56 from distutils.command.build_py import build_py
57 57 from distutils.spawn import spawn, find_executable
58 58 from distutils.ccompiler import new_compiler
59 59 from distutils.errors import CCompilerError
60 60
61 61 scripts = ['hg']
62 62 if os.name == 'nt':
63 63 scripts.append('contrib/win32/hg.bat')
64 64
65 65 # simplified version of distutils.ccompiler.CCompiler.has_function
66 66 # that actually removes its temporary files.
67 67 def hasfunction(cc, funcname):
68 68 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
69 69 devnull = oldstderr = None
70 70 try:
71 71 try:
72 72 fname = os.path.join(tmpdir, 'funcname.c')
73 73 f = open(fname, 'w')
74 74 f.write('int main(void) {\n')
75 75 f.write(' %s();\n' % funcname)
76 76 f.write('}\n')
77 77 f.close()
78 78 # Redirect stderr to /dev/null to hide any error messages
79 79 # from the compiler.
80 80 # This will have to be changed if we ever have to check
81 81 # for a function on Windows.
82 82 devnull = open('/dev/null', 'w')
83 83 oldstderr = os.dup(sys.stderr.fileno())
84 84 os.dup2(devnull.fileno(), sys.stderr.fileno())
85 85 objects = cc.compile([fname], output_dir=tmpdir)
86 86 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
87 87 except:
88 88 return False
89 89 return True
90 90 finally:
91 91 if oldstderr is not None:
92 92 os.dup2(oldstderr, sys.stderr.fileno())
93 93 if devnull is not None:
94 94 devnull.close()
95 95 shutil.rmtree(tmpdir)
96 96
97 97 # py2exe needs to be installed to work
98 98 try:
99 99 import py2exe
100 100 py2exeloaded = True
101 101
102 102 # Help py2exe to find win32com.shell
103 103 try:
104 104 import modulefinder
105 105 import win32com
106 106 for p in win32com.__path__[1:]: # Take the path to win32comext
107 107 modulefinder.AddPackagePath("win32com", p)
108 108 pn = "win32com.shell"
109 109 __import__(pn)
110 110 m = sys.modules[pn]
111 111 for p in m.__path__[1:]:
112 112 modulefinder.AddPackagePath(pn, p)
113 113 except ImportError:
114 114 pass
115 115
116 116 except ImportError:
117 117 py2exeloaded = False
118 118 pass
119 119
120 120 def runcmd(cmd, env):
121 121 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
122 122 stderr=subprocess.PIPE, env=env)
123 123 out, err = p.communicate()
124 124 # If root is executing setup.py, but the repository is owned by
125 125 # another user (as in "sudo python setup.py install") we will get
126 126 # trust warnings since the .hg/hgrc file is untrusted. That is
127 127 # fine, we don't want to load it anyway. Python may warn about
128 128 # a missing __init__.py in mercurial/locale, we also ignore that.
129 129 err = [e for e in err.splitlines()
130 130 if not e.startswith(b('Not trusting file')) \
131 131 and not e.startswith(b('warning: Not importing'))]
132 132 if err:
133 133 return ''
134 134 return out
135 135
136 136 version = ''
137 137
138 138 if os.path.isdir('.hg'):
139 139 # Execute hg out of this directory with a custom environment which
140 140 # includes the pure Python modules in mercurial/pure. We also take
141 141 # care to not use any hgrc files and do no localization.
142 142 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
143 143 env = {'PYTHONPATH': os.pathsep.join(pypath),
144 144 'HGRCPATH': '',
145 145 'LANGUAGE': 'C'}
146 146 if 'LD_LIBRARY_PATH' in os.environ:
147 147 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
148 148 if 'SystemRoot' in os.environ:
149 149 # Copy SystemRoot into the custom environment for Python 2.6
150 150 # under Windows. Otherwise, the subprocess will fail with
151 151 # error 0xc0150004. See: http://bugs.python.org/issue3440
152 152 env['SystemRoot'] = os.environ['SystemRoot']
153 153 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
154 154 l = runcmd(cmd, env).split()
155 155 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
156 156 l.pop()
157 157 if len(l) > 1: # tag found
158 158 version = l[-1]
159 159 if l[0].endswith('+'): # propagate the dirty status to the tag
160 160 version += '+'
161 161 elif len(l) == 1: # no tag found
162 162 cmd = [sys.executable, 'hg', 'parents', '--template',
163 163 '{latesttag}+{latesttagdistance}-']
164 164 version = runcmd(cmd, env) + l[0]
165 165 if version.endswith('+'):
166 166 version += time.strftime('%Y%m%d')
167 167 elif os.path.exists('.hg_archival.txt'):
168 168 kw = dict([[t.strip() for t in l.split(':', 1)]
169 169 for l in open('.hg_archival.txt')])
170 170 if 'tag' in kw:
171 171 version = kw['tag']
172 172 elif 'latesttag' in kw:
173 173 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
174 174 else:
175 175 version = kw.get('node', '')[:12]
176 176
177 177 if version:
178 178 f = open("mercurial/__version__.py", "w")
179 179 f.write('# this file is autogenerated by setup.py\n')
180 180 f.write('version = "%s"\n' % version)
181 181 f.close()
182 182
183 183
184 184 try:
185 185 from mercurial import __version__
186 186 version = __version__.version
187 187 except ImportError:
188 188 version = 'unknown'
189 189
190 190 class hgbuildmo(build):
191 191
192 192 description = "build translations (.mo files)"
193 193
194 194 def run(self):
195 195 if not find_executable('msgfmt'):
196 196 self.warn("could not find msgfmt executable, no translations "
197 197 "will be built")
198 198 return
199 199
200 200 podir = 'i18n'
201 201 if not os.path.isdir(podir):
202 202 self.warn("could not find %s/ directory" % podir)
203 203 return
204 204
205 205 join = os.path.join
206 206 for po in os.listdir(podir):
207 207 if not po.endswith('.po'):
208 208 continue
209 209 pofile = join(podir, po)
210 210 modir = join('locale', po[:-3], 'LC_MESSAGES')
211 211 mofile = join(modir, 'hg.mo')
212 212 mobuildfile = join('mercurial', mofile)
213 213 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
214 214 if sys.platform != 'sunos5':
215 215 # msgfmt on Solaris does not know about -c
216 216 cmd.append('-c')
217 217 self.mkpath(join('mercurial', modir))
218 218 self.make_file([pofile], mobuildfile, spawn, (cmd,))
219 219
220 220 # Insert hgbuildmo first so that files in mercurial/locale/ are found
221 221 # when build_py is run next.
222 222 build.sub_commands.insert(0, ('build_mo', None))
223 223 # We also need build_ext before build_py. Otherwise, when 2to3 is called (in
224 224 # build_py), it will not find osutil & friends, thinking that those modules are
225 225 # global and, consequently, making a mess, now that all module imports are
226 226 # global.
227 227 build.sub_commands.insert(1, ('build_ext', None))
228 228
229 229 Distribution.pure = 0
230 230 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
231 231 "code instead of C extensions"))
232 232
233 233 class hgbuildext(build_ext):
234 234
235 235 def build_extension(self, ext):
236 236 try:
237 237 build_ext.build_extension(self, ext)
238 238 except CCompilerError:
239 239 if not hasattr(ext, 'optional') or not ext.optional:
240 240 raise
241 241 log.warn("Failed to build optional extension '%s' (skipping)",
242 242 ext.name)
243 243
244 244 class hgbuildpy(build_py_2to3):
245 245 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
246 246 getfixers("hgfixes")))
247 247
248 248 def finalize_options(self):
249 249 build_py.finalize_options(self)
250 250
251 251 if self.distribution.pure:
252 252 if self.py_modules is None:
253 253 self.py_modules = []
254 254 for ext in self.distribution.ext_modules:
255 255 if ext.name.startswith("mercurial."):
256 256 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
257 257 self.distribution.ext_modules = []
258 258
259 259 def find_modules(self):
260 260 modules = build_py.find_modules(self)
261 261 for module in modules:
262 262 if module[0] == "mercurial.pure":
263 263 if module[1] != "__init__":
264 264 yield ("mercurial", module[1], module[2])
265 265 else:
266 266 yield module
267 267
268 268 def run(self):
269 269 # In the build_py_2to3 class, self.updated_files = [], but I couldn't
270 270 # see when that variable was updated to point to the updated files, as
271 271 # its names suggests. Thus, I decided to just find_all_modules and feed
272 272 # them to 2to3. Unfortunately, subsequent calls to setup3k.py will
273 273 # incur in 2to3 analysis overhead.
274 274 self.updated_files = [i[2] for i in self.find_all_modules()]
275 275
276 276 # Base class code
277 277 if self.py_modules:
278 278 self.build_modules()
279 279 if self.packages:
280 280 self.build_packages()
281 281 self.build_package_data()
282 282
283 283 # 2to3
284 284 self.run_2to3(self.updated_files)
285 285
286 286 # Remaining base class code
287 287 self.byte_compile(self.get_outputs(include_bytecode=0))
288 288
289 289 cmdclass = {'build_mo': hgbuildmo,
290 290 'build_ext': hgbuildext,
291 291 'build_py': hgbuildpy}
292 292
293 293 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
294 294 'hgext.highlight', 'hgext.zeroconf']
295 295
296 296 pymodules = []
297 297
298 298 extmodules = [
299 299 Extension('mercurial.base85', ['mercurial/base85.c']),
300 300 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
301 301 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
302 302 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
303 303 Extension('mercurial.parsers', ['mercurial/parsers.c']),
304 304 ]
305 305
306 306 # disable osutil.c under windows + python 2.4 (issue1364)
307 307 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
308 308 pymodules.append('mercurial.pure.osutil')
309 309 else:
310 310 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
311 311
312 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
312 if sys.platform.startswith('linux') and os.uname()[2] > '2.6':
313 313 # The inotify extension is only usable with Linux 2.6 kernels.
314 314 # You also need a reasonably recent C library.
315 315 # In any case, if it fails to build the error will be skipped ('optional').
316 316 cc = new_compiler()
317 317 if hasfunction(cc, 'inotify_add_watch'):
318 318 inotify = Extension('hgext.inotify.linux._inotify',
319 319 ['hgext/inotify/linux/_inotify.c'],
320 320 ['mercurial'])
321 321 inotify.optional = True
322 322 extmodules.append(inotify)
323 323 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
324 324
325 325 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
326 326 'help/*.txt']}
327 327
328 328 def ordinarypath(p):
329 329 return p and p[0] != '.' and p[-1] != '~'
330 330
331 331 for root in ('templates',):
332 332 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
333 333 curdir = curdir.split(os.sep, 1)[1]
334 334 dirs[:] = filter(ordinarypath, dirs)
335 335 for f in filter(ordinarypath, files):
336 336 f = os.path.join(curdir, f)
337 337 packagedata['mercurial'].append(f)
338 338
339 339 datafiles = []
340 340 setupversion = version
341 341 extra = {}
342 342
343 343 if py2exeloaded:
344 344 extra['console'] = [
345 345 {'script':'hg',
346 346 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
347 347 'product_version':version}]
348 348
349 349 if os.name == 'nt':
350 350 # Windows binary file versions for exe/dll files must have the
351 351 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
352 352 setupversion = version.split('+', 1)[0]
353 353
354 354 setup(name='mercurial',
355 355 version=setupversion,
356 356 author='Matt Mackall',
357 357 author_email='mpm@selenic.com',
358 358 url='http://mercurial.selenic.com/',
359 359 description='Scalable distributed SCM',
360 360 license='GNU GPLv2+',
361 361 scripts=scripts,
362 362 packages=packages,
363 363 py_modules=pymodules,
364 364 ext_modules=extmodules,
365 365 data_files=datafiles,
366 366 package_data=packagedata,
367 367 cmdclass=cmdclass,
368 368 options=dict(py2exe=dict(packages=['hgext', 'email']),
369 369 bdist_mpkg=dict(zipdist=True,
370 370 license='COPYING',
371 371 readme='contrib/macosx/Readme.html',
372 372 welcome='contrib/macosx/Welcome.html')),
373 373 **extra)
@@ -1,492 +1,492 b''
1 1 # server.py - common entry point for inotify status server
2 2 #
3 3 # Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from mercurial.i18n import _
9 9 from mercurial import cmdutil, osutil, util
10 10 import common
11 11
12 12 import errno
13 13 import os
14 14 import socket
15 15 import stat
16 16 import struct
17 17 import sys
18 18 import tempfile
19 19
20 20 class AlreadyStartedException(Exception):
21 21 pass
22 22 class TimeoutException(Exception):
23 23 pass
24 24
25 25 def join(a, b):
26 26 if a:
27 27 if a[-1] == '/':
28 28 return a + b
29 29 return a + '/' + b
30 30 return b
31 31
32 32 def split(path):
33 33 c = path.rfind('/')
34 34 if c == -1:
35 35 return '', path
36 36 return path[:c], path[c + 1:]
37 37
38 38 walk_ignored_errors = (errno.ENOENT, errno.ENAMETOOLONG)
39 39
40 40 def walk(dirstate, absroot, root):
41 41 '''Like os.walk, but only yields regular files.'''
42 42
43 43 # This function is critical to performance during startup.
44 44
45 45 def walkit(root, reporoot):
46 46 files, dirs = [], []
47 47
48 48 try:
49 49 fullpath = join(absroot, root)
50 50 for name, kind in osutil.listdir(fullpath):
51 51 if kind == stat.S_IFDIR:
52 52 if name == '.hg':
53 53 if not reporoot:
54 54 return
55 55 else:
56 56 dirs.append(name)
57 57 path = join(root, name)
58 58 if dirstate._ignore(path):
59 59 continue
60 60 for result in walkit(path, False):
61 61 yield result
62 62 elif kind in (stat.S_IFREG, stat.S_IFLNK):
63 63 files.append(name)
64 64 yield fullpath, dirs, files
65 65
66 66 except OSError, err:
67 67 if err.errno == errno.ENOTDIR:
68 68 # fullpath was a directory, but has since been replaced
69 69 # by a file.
70 70 yield fullpath, dirs, files
71 71 elif err.errno not in walk_ignored_errors:
72 72 raise
73 73
74 74 return walkit(root, root == '')
75 75
76 76 class directory(object):
77 77 """
78 78 Representing a directory
79 79
80 80 * path is the relative path from repo root to this directory
81 81 * files is a dict listing the files in this directory
82 82 - keys are file names
83 83 - values are file status
84 84 * dirs is a dict listing the subdirectories
85 85 - key are subdirectories names
86 86 - values are directory objects
87 87 """
88 88 def __init__(self, relpath=''):
89 89 self.path = relpath
90 90 self.files = {}
91 91 self.dirs = {}
92 92
93 93 def dir(self, relpath):
94 94 """
95 95 Returns the directory contained at the relative path relpath.
96 96 Creates the intermediate directories if necessary.
97 97 """
98 98 if not relpath:
99 99 return self
100 100 l = relpath.split('/')
101 101 ret = self
102 102 while l:
103 103 next = l.pop(0)
104 104 try:
105 105 ret = ret.dirs[next]
106 106 except KeyError:
107 107 d = directory(join(ret.path, next))
108 108 ret.dirs[next] = d
109 109 ret = d
110 110 return ret
111 111
112 112 def walk(self, states, visited=None):
113 113 """
114 114 yield (filename, status) pairs for items in the trees
115 115 that have status in states.
116 116 filenames are relative to the repo root
117 117 """
118 118 for file, st in self.files.iteritems():
119 119 if st in states:
120 120 yield join(self.path, file), st
121 121 for dir in self.dirs.itervalues():
122 122 if visited is not None:
123 123 visited.add(dir.path)
124 124 for e in dir.walk(states):
125 125 yield e
126 126
127 127 def lookup(self, states, path, visited):
128 128 """
129 129 yield root-relative filenames that match path, and whose
130 130 status are in states:
131 131 * if path is a file, yield path
132 132 * if path is a directory, yield directory files
133 133 * if path is not tracked, yield nothing
134 134 """
135 135 if path[-1] == '/':
136 136 path = path[:-1]
137 137
138 138 paths = path.split('/')
139 139
140 140 # we need to check separately for last node
141 141 last = paths.pop()
142 142
143 143 tree = self
144 144 try:
145 145 for dir in paths:
146 146 tree = tree.dirs[dir]
147 147 except KeyError:
148 148 # path is not tracked
149 149 visited.add(tree.path)
150 150 return
151 151
152 152 try:
153 153 # if path is a directory, walk it
154 154 target = tree.dirs[last]
155 155 visited.add(target.path)
156 156 for file, st in target.walk(states, visited):
157 157 yield file
158 158 except KeyError:
159 159 try:
160 160 if tree.files[last] in states:
161 161 # path is a file
162 162 visited.add(tree.path)
163 163 yield path
164 164 except KeyError:
165 165 # path is not tracked
166 166 pass
167 167
168 168 class repowatcher(object):
169 169 """
170 170 Watches inotify events
171 171 """
172 172 statuskeys = 'almr!?'
173 173
174 174 def __init__(self, ui, dirstate, root):
175 175 self.ui = ui
176 176 self.dirstate = dirstate
177 177
178 178 self.wprefix = join(root, '')
179 179 self.prefixlen = len(self.wprefix)
180 180
181 181 self.tree = directory()
182 182 self.statcache = {}
183 183 self.statustrees = dict([(s, directory()) for s in self.statuskeys])
184 184
185 185 self.ds_info = self.dirstate_info()
186 186
187 187 self.last_event = None
188 188
189 189
190 190 def handle_timeout(self):
191 191 pass
192 192
193 193 def dirstate_info(self):
194 194 try:
195 195 st = os.lstat(self.wprefix + '.hg/dirstate')
196 196 return st.st_mtime, st.st_ino
197 197 except OSError, err:
198 198 if err.errno != errno.ENOENT:
199 199 raise
200 200 return 0, 0
201 201
202 202 def filestatus(self, fn, st):
203 203 try:
204 204 type_, mode, size, time = self.dirstate._map[fn][:4]
205 205 except KeyError:
206 206 type_ = '?'
207 207 if type_ == 'n':
208 208 st_mode, st_size, st_mtime = st
209 209 if size == -1:
210 210 return 'l'
211 211 if size and (size != st_size or (mode ^ st_mode) & 0100):
212 212 return 'm'
213 213 if time != int(st_mtime):
214 214 return 'l'
215 215 return 'n'
216 216 if type_ == '?' and self.dirstate._dirignore(fn):
217 217 # we must check not only if the file is ignored, but if any part
218 218 # of its path match an ignore pattern
219 219 return 'i'
220 220 return type_
221 221
222 222 def updatefile(self, wfn, osstat):
223 223 '''
224 224 update the file entry of an existing file.
225 225
226 226 osstat: (mode, size, time) tuple, as returned by os.lstat(wfn)
227 227 '''
228 228
229 229 self._updatestatus(wfn, self.filestatus(wfn, osstat))
230 230
231 231 def deletefile(self, wfn, oldstatus):
232 232 '''
233 233 update the entry of a file which has been deleted.
234 234
235 235 oldstatus: char in statuskeys, status of the file before deletion
236 236 '''
237 237 if oldstatus == 'r':
238 238 newstatus = 'r'
239 239 elif oldstatus in 'almn':
240 240 newstatus = '!'
241 241 else:
242 242 newstatus = None
243 243
244 244 self.statcache.pop(wfn, None)
245 245 self._updatestatus(wfn, newstatus)
246 246
247 247 def _updatestatus(self, wfn, newstatus):
248 248 '''
249 249 Update the stored status of a file.
250 250
251 251 newstatus: - char in (statuskeys + 'ni'), new status to apply.
252 252 - or None, to stop tracking wfn
253 253 '''
254 254 root, fn = split(wfn)
255 255 d = self.tree.dir(root)
256 256
257 257 oldstatus = d.files.get(fn)
258 258 # oldstatus can be either:
259 259 # - None : fn is new
260 260 # - a char in statuskeys: fn is a (tracked) file
261 261
262 262 if self.ui.debugflag and oldstatus != newstatus:
263 263 self.ui.note(_('status: %r %s -> %s\n') %
264 264 (wfn, oldstatus, newstatus))
265 265
266 266 if oldstatus and oldstatus in self.statuskeys \
267 267 and oldstatus != newstatus:
268 268 del self.statustrees[oldstatus].dir(root).files[fn]
269 269
270 270 if newstatus in (None, 'i'):
271 271 d.files.pop(fn, None)
272 272 elif oldstatus != newstatus:
273 273 d.files[fn] = newstatus
274 274 if newstatus != 'n':
275 275 self.statustrees[newstatus].dir(root).files[fn] = newstatus
276 276
277 277 def check_deleted(self, key):
278 278 # Files that had been deleted but were present in the dirstate
279 279 # may have vanished from the dirstate; we must clean them up.
280 280 nuke = []
281 281 for wfn, ignore in self.statustrees[key].walk(key):
282 282 if wfn not in self.dirstate:
283 283 nuke.append(wfn)
284 284 for wfn in nuke:
285 285 root, fn = split(wfn)
286 286 del self.statustrees[key].dir(root).files[fn]
287 287 del self.tree.dir(root).files[fn]
288 288
289 289 def update_hgignore(self):
290 290 # An update of the ignore file can potentially change the
291 291 # states of all unknown and ignored files.
292 292
293 293 # XXX If the user has other ignore files outside the repo, or
294 294 # changes their list of ignore files at run time, we'll
295 295 # potentially never see changes to them. We could get the
296 296 # client to report to us what ignore data they're using.
297 297 # But it's easier to do nothing than to open that can of
298 298 # worms.
299 299
300 300 if '_ignore' in self.dirstate.__dict__:
301 301 delattr(self.dirstate, '_ignore')
302 302 self.ui.note(_('rescanning due to .hgignore change\n'))
303 303 self.handle_timeout()
304 304 self.scan()
305 305
306 306 def getstat(self, wpath):
307 307 try:
308 308 return self.statcache[wpath]
309 309 except KeyError:
310 310 try:
311 311 return self.stat(wpath)
312 312 except OSError, err:
313 313 if err.errno != errno.ENOENT:
314 314 raise
315 315
316 316 def stat(self, wpath):
317 317 try:
318 318 st = os.lstat(join(self.wprefix, wpath))
319 319 ret = st.st_mode, st.st_size, st.st_mtime
320 320 self.statcache[wpath] = ret
321 321 return ret
322 322 except OSError:
323 323 self.statcache.pop(wpath, None)
324 324 raise
325 325
326 326 class socketlistener(object):
327 327 """
328 328 Listens for client queries on unix socket inotify.sock
329 329 """
330 330 def __init__(self, ui, root, repowatcher, timeout):
331 331 self.ui = ui
332 332 self.repowatcher = repowatcher
333 333 self.sock = socket.socket(socket.AF_UNIX)
334 334 self.sockpath = join(root, '.hg/inotify.sock')
335 335
336 336 self.realsockpath = self.sockpath
337 337 if os.path.islink(self.sockpath):
338 338 if os.path.exists(self.sockpath):
339 339 self.realsockpath = os.readlink(self.sockpath)
340 340 else:
341 341 raise util.Abort('inotify-server: cannot start: '
342 342 '.hg/inotify.sock is a broken symlink')
343 343 try:
344 344 self.sock.bind(self.realsockpath)
345 345 except socket.error, err:
346 346 if err.args[0] == errno.EADDRINUSE:
347 347 raise AlreadyStartedException(_('cannot start: socket is '
348 348 'already bound'))
349 349 if err.args[0] == "AF_UNIX path too long":
350 350 tempdir = tempfile.mkdtemp(prefix="hg-inotify-")
351 351 self.realsockpath = os.path.join(tempdir, "inotify.sock")
352 352 try:
353 353 self.sock.bind(self.realsockpath)
354 354 os.symlink(self.realsockpath, self.sockpath)
355 355 except (OSError, socket.error), inst:
356 356 try:
357 357 os.unlink(self.realsockpath)
358 358 except:
359 359 pass
360 360 os.rmdir(tempdir)
361 361 if inst.errno == errno.EEXIST:
362 362 raise AlreadyStartedException(_('cannot start: tried '
363 363 'linking .hg/inotify.sock to a temporary socket but'
364 364 ' .hg/inotify.sock already exists'))
365 365 raise
366 366 else:
367 367 raise
368 368 self.sock.listen(5)
369 369 self.fileno = self.sock.fileno
370 370
371 371 def answer_stat_query(self, cs):
372 372 names = cs.read().split('\0')
373 373
374 374 states = names.pop()
375 375
376 376 self.ui.note(_('answering query for %r\n') % states)
377 377
378 378 visited = set()
379 379 if not names:
380 380 def genresult(states, tree):
381 381 for fn, state in tree.walk(states):
382 382 yield fn
383 383 else:
384 384 def genresult(states, tree):
385 385 for fn in names:
386 386 for f in tree.lookup(states, fn, visited):
387 387 yield f
388 388
389 389 return ['\0'.join(r) for r in [
390 390 genresult('l', self.repowatcher.statustrees['l']),
391 391 genresult('m', self.repowatcher.statustrees['m']),
392 392 genresult('a', self.repowatcher.statustrees['a']),
393 393 genresult('r', self.repowatcher.statustrees['r']),
394 394 genresult('!', self.repowatcher.statustrees['!']),
395 395 '?' in states
396 396 and genresult('?', self.repowatcher.statustrees['?'])
397 397 or [],
398 398 [],
399 399 'c' in states and genresult('n', self.repowatcher.tree) or [],
400 400 visited
401 401 ]]
402 402
403 403 def answer_dbug_query(self):
404 404 return ['\0'.join(self.repowatcher.debug())]
405 405
406 406 def accept_connection(self):
407 407 sock, addr = self.sock.accept()
408 408
409 409 cs = common.recvcs(sock)
410 410 version = ord(cs.read(1))
411 411
412 412 if version != common.version:
413 413 self.ui.warn(_('received query from incompatible client '
414 414 'version %d\n') % version)
415 415 try:
416 416 # try to send back our version to the client
417 417 # this way, the client too is informed of the mismatch
418 418 sock.sendall(chr(common.version))
419 419 except:
420 420 pass
421 421 return
422 422
423 423 type = cs.read(4)
424 424
425 425 if type == 'STAT':
426 426 results = self.answer_stat_query(cs)
427 427 elif type == 'DBUG':
428 428 results = self.answer_dbug_query()
429 429 else:
430 430 self.ui.warn(_('unrecognized query type: %s\n') % type)
431 431 return
432 432
433 433 try:
434 434 try:
435 435 v = chr(common.version)
436 436
437 437 sock.sendall(v + type + struct.pack(common.resphdrfmts[type],
438 438 *map(len, results)))
439 439 sock.sendall(''.join(results))
440 440 finally:
441 441 sock.shutdown(socket.SHUT_WR)
442 442 except socket.error, err:
443 443 if err.args[0] != errno.EPIPE:
444 444 raise
445 445
446 if sys.platform == 'linux2':
446 if sys.platform.startswith('linux'):
447 447 import linuxserver as _server
448 448 else:
449 449 raise ImportError
450 450
451 451 master = _server.master
452 452
453 453 def start(ui, dirstate, root, opts):
454 454 timeout = opts.get('idle_timeout')
455 455 if timeout:
456 456 timeout = float(timeout) * 60000
457 457 else:
458 458 timeout = None
459 459
460 460 class service(object):
461 461 def init(self):
462 462 try:
463 463 self.master = master(ui, dirstate, root, timeout)
464 464 except AlreadyStartedException, inst:
465 465 raise util.Abort("inotify-server: %s" % inst)
466 466
467 467 def run(self):
468 468 try:
469 469 try:
470 470 self.master.run()
471 471 except TimeoutException:
472 472 pass
473 473 finally:
474 474 self.master.shutdown()
475 475
476 476 if 'inserve' not in sys.argv:
477 477 runargs = util.hgcmd() + ['inserve', '-R', root]
478 478 else:
479 479 runargs = util.hgcmd() + sys.argv[1:]
480 480
481 481 pidfile = ui.config('inotify', 'pidfile')
482 482 if opts['daemon'] and pidfile is not None and 'pid-file' not in runargs:
483 483 runargs.append("--pid-file=%s" % pidfile)
484 484
485 485 service = service()
486 486 logfile = ui.config('inotify', 'log')
487 487
488 488 appendpid = ui.configbool('inotify', 'appendpid', False)
489 489
490 490 ui.debug('starting inotify server: %s\n' % ' '.join(runargs))
491 491 cmdutil.service(opts, initfn=service.init, runfn=service.run,
492 492 logfile=logfile, runargs=runargs, appendpid=appendpid)
@@ -1,441 +1,441 b''
1 1 #
2 2 # This is the mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6
7 7 import sys, platform
8 8 if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
9 9 raise SystemExit("Mercurial requires Python 2.4 or later.")
10 10
11 11 if sys.version_info[0] >= 3:
12 12 def b(s):
13 13 '''A helper function to emulate 2.6+ bytes literals using string
14 14 literals.'''
15 15 return s.encode('latin1')
16 16 else:
17 17 def b(s):
18 18 '''A helper function to emulate 2.6+ bytes literals using string
19 19 literals.'''
20 20 return s
21 21
22 22 # Solaris Python packaging brain damage
23 23 try:
24 24 import hashlib
25 25 sha = hashlib.sha1()
26 26 except:
27 27 try:
28 28 import sha
29 29 except:
30 30 raise SystemExit(
31 31 "Couldn't import standard hashlib (incomplete Python install).")
32 32
33 33 try:
34 34 import zlib
35 35 except:
36 36 raise SystemExit(
37 37 "Couldn't import standard zlib (incomplete Python install).")
38 38
39 39 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
40 40 isironpython = False
41 41 try:
42 42 isironpython = platform.python_implementation().lower().find("ironpython") != -1
43 43 except:
44 44 pass
45 45
46 46 if isironpython:
47 47 print "warning: IronPython detected (no bz2 support)"
48 48 else:
49 49 try:
50 50 import bz2
51 51 except:
52 52 raise SystemExit(
53 53 "Couldn't import standard bz2 (incomplete Python install).")
54 54
55 55 import os, subprocess, time
56 56 import shutil
57 57 import tempfile
58 58 from distutils import log
59 59 from distutils.core import setup, Command, Extension
60 60 from distutils.dist import Distribution
61 61 from distutils.command.build import build
62 62 from distutils.command.build_ext import build_ext
63 63 from distutils.command.build_py import build_py
64 64 from distutils.command.install_scripts import install_scripts
65 65 from distutils.spawn import spawn, find_executable
66 66 from distutils.ccompiler import new_compiler
67 67 from distutils.errors import CCompilerError, DistutilsExecError
68 68 from distutils.sysconfig import get_python_inc
69 69 from distutils.version import StrictVersion
70 70
71 71 scripts = ['hg']
72 72 if os.name == 'nt':
73 73 scripts.append('contrib/win32/hg.bat')
74 74
75 75 # simplified version of distutils.ccompiler.CCompiler.has_function
76 76 # that actually removes its temporary files.
77 77 def hasfunction(cc, funcname):
78 78 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
79 79 devnull = oldstderr = None
80 80 try:
81 81 try:
82 82 fname = os.path.join(tmpdir, 'funcname.c')
83 83 f = open(fname, 'w')
84 84 f.write('int main(void) {\n')
85 85 f.write(' %s();\n' % funcname)
86 86 f.write('}\n')
87 87 f.close()
88 88 # Redirect stderr to /dev/null to hide any error messages
89 89 # from the compiler.
90 90 # This will have to be changed if we ever have to check
91 91 # for a function on Windows.
92 92 devnull = open('/dev/null', 'w')
93 93 oldstderr = os.dup(sys.stderr.fileno())
94 94 os.dup2(devnull.fileno(), sys.stderr.fileno())
95 95 objects = cc.compile([fname], output_dir=tmpdir)
96 96 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
97 97 except:
98 98 return False
99 99 return True
100 100 finally:
101 101 if oldstderr is not None:
102 102 os.dup2(oldstderr, sys.stderr.fileno())
103 103 if devnull is not None:
104 104 devnull.close()
105 105 shutil.rmtree(tmpdir)
106 106
107 107 # py2exe needs to be installed to work
108 108 try:
109 109 import py2exe
110 110 py2exeloaded = True
111 111 except ImportError:
112 112 py2exeloaded = False
113 113
114 114 def runcmd(cmd, env):
115 115 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
116 116 stderr=subprocess.PIPE, env=env)
117 117 out, err = p.communicate()
118 118 return out, err
119 119
120 120 def runhg(cmd, env):
121 121 out, err = runcmd(cmd, env)
122 122 # If root is executing setup.py, but the repository is owned by
123 123 # another user (as in "sudo python setup.py install") we will get
124 124 # trust warnings since the .hg/hgrc file is untrusted. That is
125 125 # fine, we don't want to load it anyway. Python may warn about
126 126 # a missing __init__.py in mercurial/locale, we also ignore that.
127 127 err = [e for e in err.splitlines()
128 128 if not e.startswith(b('Not trusting file')) \
129 129 and not e.startswith(b('warning: Not importing'))]
130 130 if err:
131 131 return ''
132 132 return out
133 133
134 134 version = ''
135 135
136 136 if os.path.isdir('.hg'):
137 137 # Execute hg out of this directory with a custom environment which
138 138 # includes the pure Python modules in mercurial/pure. We also take
139 139 # care to not use any hgrc files and do no localization.
140 140 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
141 141 env = {'PYTHONPATH': os.pathsep.join(pypath),
142 142 'HGRCPATH': '',
143 143 'LANGUAGE': 'C'}
144 144 if 'LD_LIBRARY_PATH' in os.environ:
145 145 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
146 146 if 'SystemRoot' in os.environ:
147 147 # Copy SystemRoot into the custom environment for Python 2.6
148 148 # under Windows. Otherwise, the subprocess will fail with
149 149 # error 0xc0150004. See: http://bugs.python.org/issue3440
150 150 env['SystemRoot'] = os.environ['SystemRoot']
151 151 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
152 152 l = runhg(cmd, env).split()
153 153 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
154 154 l.pop()
155 155 if len(l) > 1: # tag found
156 156 version = l[-1]
157 157 if l[0].endswith('+'): # propagate the dirty status to the tag
158 158 version += '+'
159 159 elif len(l) == 1: # no tag found
160 160 cmd = [sys.executable, 'hg', 'parents', '--template',
161 161 '{latesttag}+{latesttagdistance}-']
162 162 version = runhg(cmd, env) + l[0]
163 163 if version.endswith('+'):
164 164 version += time.strftime('%Y%m%d')
165 165 elif os.path.exists('.hg_archival.txt'):
166 166 kw = dict([[t.strip() for t in l.split(':', 1)]
167 167 for l in open('.hg_archival.txt')])
168 168 if 'tag' in kw:
169 169 version = kw['tag']
170 170 elif 'latesttag' in kw:
171 171 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
172 172 else:
173 173 version = kw.get('node', '')[:12]
174 174
175 175 if version:
176 176 f = open("mercurial/__version__.py", "w")
177 177 f.write('# this file is autogenerated by setup.py\n')
178 178 f.write('version = "%s"\n' % version)
179 179 f.close()
180 180
181 181
182 182 try:
183 183 from mercurial import __version__
184 184 version = __version__.version
185 185 except ImportError:
186 186 version = 'unknown'
187 187
188 188 class hgbuildmo(build):
189 189
190 190 description = "build translations (.mo files)"
191 191
192 192 def run(self):
193 193 if not find_executable('msgfmt'):
194 194 self.warn("could not find msgfmt executable, no translations "
195 195 "will be built")
196 196 return
197 197
198 198 podir = 'i18n'
199 199 if not os.path.isdir(podir):
200 200 self.warn("could not find %s/ directory" % podir)
201 201 return
202 202
203 203 join = os.path.join
204 204 for po in os.listdir(podir):
205 205 if not po.endswith('.po'):
206 206 continue
207 207 pofile = join(podir, po)
208 208 modir = join('locale', po[:-3], 'LC_MESSAGES')
209 209 mofile = join(modir, 'hg.mo')
210 210 mobuildfile = join('mercurial', mofile)
211 211 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
212 212 if sys.platform != 'sunos5':
213 213 # msgfmt on Solaris does not know about -c
214 214 cmd.append('-c')
215 215 self.mkpath(join('mercurial', modir))
216 216 self.make_file([pofile], mobuildfile, spawn, (cmd,))
217 217
218 218
219 219 # Insert hgbuildmo first so that files in mercurial/locale/ are found
220 220 # when build_py is run next.
221 221 build.sub_commands.insert(0, ('build_mo', None))
222 222
223 223 Distribution.pure = 0
224 224 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
225 225 "code instead of C extensions"))
226 226
227 227 class hgbuildext(build_ext):
228 228
229 229 def build_extension(self, ext):
230 230 try:
231 231 build_ext.build_extension(self, ext)
232 232 except CCompilerError:
233 233 if not getattr(ext, 'optional', False):
234 234 raise
235 235 log.warn("Failed to build optional extension '%s' (skipping)",
236 236 ext.name)
237 237
238 238 class hgbuildpy(build_py):
239 239
240 240 def finalize_options(self):
241 241 build_py.finalize_options(self)
242 242
243 243 if self.distribution.pure:
244 244 if self.py_modules is None:
245 245 self.py_modules = []
246 246 for ext in self.distribution.ext_modules:
247 247 if ext.name.startswith("mercurial."):
248 248 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
249 249 self.distribution.ext_modules = []
250 250 else:
251 251 if not os.path.exists(os.path.join(get_python_inc(), 'Python.h')):
252 252 raise SystemExit("Python headers are required to build Mercurial")
253 253
254 254 def find_modules(self):
255 255 modules = build_py.find_modules(self)
256 256 for module in modules:
257 257 if module[0] == "mercurial.pure":
258 258 if module[1] != "__init__":
259 259 yield ("mercurial", module[1], module[2])
260 260 else:
261 261 yield module
262 262
263 263 class buildhgextindex(Command):
264 264 description = 'generate prebuilt index of hgext (for frozen package)'
265 265 user_options = []
266 266 _indexfilename = 'hgext/__index__.py'
267 267
268 268 def initialize_options(self):
269 269 pass
270 270
271 271 def finalize_options(self):
272 272 pass
273 273
274 274 def run(self):
275 275 if os.path.exists(self._indexfilename):
276 276 os.unlink(self._indexfilename)
277 277
278 278 # here no extension enabled, disabled() lists up everything
279 279 code = ('import pprint; from mercurial import extensions; '
280 280 'pprint.pprint(extensions.disabled())')
281 281 out, err = runcmd([sys.executable, '-c', code], env)
282 282 if err:
283 283 raise DistutilsExecError(err)
284 284
285 285 f = open(self._indexfilename, 'w')
286 286 f.write('# this file is autogenerated by setup.py\n')
287 287 f.write('docs = ')
288 288 f.write(out)
289 289 f.close()
290 290
291 291 class hginstallscripts(install_scripts):
292 292 '''
293 293 This is a specialization of install_scripts that replaces the @LIBDIR@ with
294 294 the configured directory for modules. If possible, the path is made relative
295 295 to the directory for scripts.
296 296 '''
297 297
298 298 def initialize_options(self):
299 299 install_scripts.initialize_options(self)
300 300
301 301 self.install_lib = None
302 302
303 303 def finalize_options(self):
304 304 install_scripts.finalize_options(self)
305 305 self.set_undefined_options('install',
306 306 ('install_lib', 'install_lib'))
307 307
308 308 def run(self):
309 309 install_scripts.run(self)
310 310
311 311 if (os.path.splitdrive(self.install_dir)[0] !=
312 312 os.path.splitdrive(self.install_lib)[0]):
313 313 # can't make relative paths from one drive to another, so use an
314 314 # absolute path instead
315 315 libdir = self.install_lib
316 316 else:
317 317 common = os.path.commonprefix((self.install_dir, self.install_lib))
318 318 rest = self.install_dir[len(common):]
319 319 uplevel = len([n for n in os.path.split(rest) if n])
320 320
321 321 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
322 322
323 323 for outfile in self.outfiles:
324 324 fp = open(outfile, 'rb')
325 325 data = fp.read()
326 326 fp.close()
327 327
328 328 # skip binary files
329 329 if '\0' in data:
330 330 continue
331 331
332 332 data = data.replace('@LIBDIR@', libdir.encode('string_escape'))
333 333 fp = open(outfile, 'wb')
334 334 fp.write(data)
335 335 fp.close()
336 336
337 337 cmdclass = {'build_mo': hgbuildmo,
338 338 'build_ext': hgbuildext,
339 339 'build_py': hgbuildpy,
340 340 'build_hgextindex': buildhgextindex,
341 341 'install_scripts': hginstallscripts}
342 342
343 343 packages = ['mercurial', 'mercurial.hgweb',
344 344 'mercurial.httpclient', 'mercurial.httpclient.tests',
345 345 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf']
346 346
347 347 pymodules = []
348 348
349 349 extmodules = [
350 350 Extension('mercurial.base85', ['mercurial/base85.c']),
351 351 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
352 352 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
353 353 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
354 354 Extension('mercurial.parsers', ['mercurial/parsers.c']),
355 355 ]
356 356
357 357 osutil_ldflags = []
358 358
359 359 if sys.platform == 'darwin':
360 360 osutil_ldflags += ['-framework', 'ApplicationServices']
361 361
362 362 # disable osutil.c under windows + python 2.4 (issue1364)
363 363 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
364 364 pymodules.append('mercurial.pure.osutil')
365 365 else:
366 366 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
367 367 extra_link_args=osutil_ldflags))
368 368
369 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
369 if sys.platform.startswith('linux') and os.uname()[2] > '2.6':
370 370 # The inotify extension is only usable with Linux 2.6 kernels.
371 371 # You also need a reasonably recent C library.
372 372 # In any case, if it fails to build the error will be skipped ('optional').
373 373 cc = new_compiler()
374 374 if hasfunction(cc, 'inotify_add_watch'):
375 375 inotify = Extension('hgext.inotify.linux._inotify',
376 376 ['hgext/inotify/linux/_inotify.c'],
377 377 ['mercurial'])
378 378 inotify.optional = True
379 379 extmodules.append(inotify)
380 380 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
381 381
382 382 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
383 383 'help/*.txt']}
384 384
385 385 def ordinarypath(p):
386 386 return p and p[0] != '.' and p[-1] != '~'
387 387
388 388 for root in ('templates',):
389 389 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
390 390 curdir = curdir.split(os.sep, 1)[1]
391 391 dirs[:] = filter(ordinarypath, dirs)
392 392 for f in filter(ordinarypath, files):
393 393 f = os.path.join(curdir, f)
394 394 packagedata['mercurial'].append(f)
395 395
396 396 datafiles = []
397 397 setupversion = version
398 398 extra = {}
399 399
400 400 if py2exeloaded:
401 401 extra['console'] = [
402 402 {'script':'hg',
403 403 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
404 404 'product_version':version}]
405 405 # sub command of 'build' because 'py2exe' does not handle sub_commands
406 406 build.sub_commands.insert(0, ('build_hgextindex', None))
407 407
408 408 if os.name == 'nt':
409 409 # Windows binary file versions for exe/dll files must have the
410 410 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
411 411 setupversion = version.split('+', 1)[0]
412 412
413 413 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
414 414 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
415 415 # distutils.sysconfig
416 416 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()[0]
417 417 # Also parse only first digit, because 3.2.1 can't be parsed nicely
418 418 if (version.startswith('Xcode') and
419 419 StrictVersion(version.split()[1]) >= StrictVersion('4.0')):
420 420 os.environ['ARCHFLAGS'] = ''
421 421
422 422 setup(name='mercurial',
423 423 version=setupversion,
424 424 author='Matt Mackall',
425 425 author_email='mpm@selenic.com',
426 426 url='http://mercurial.selenic.com/',
427 427 description='Scalable distributed SCM',
428 428 license='GNU GPLv2+',
429 429 scripts=scripts,
430 430 packages=packages,
431 431 py_modules=pymodules,
432 432 ext_modules=extmodules,
433 433 data_files=datafiles,
434 434 package_data=packagedata,
435 435 cmdclass=cmdclass,
436 436 options=dict(py2exe=dict(packages=['hgext', 'email']),
437 437 bdist_mpkg=dict(zipdist=True,
438 438 license='COPYING',
439 439 readme='contrib/macosx/Readme.html',
440 440 welcome='contrib/macosx/Welcome.html')),
441 441 **extra)
General Comments 0
You need to be logged in to leave comments. Login now