##// END OF EJS Templates
setup.py, make: avoid problems with outdated, existing hgext/__index__.py*...
Thomas Arendsen Hein -
r21229:54d7657d stable
parent child Browse files
Show More
@@ -1,137 +1,137
1 1 # If you want to change PREFIX, do not just edit it below. The changed
2 2 # value wont get passed on to recursive make calls. You should instead
3 3 # override the variable on the command like:
4 4 #
5 5 # % make PREFIX=/opt/ install
6 6
7 7 PREFIX=/usr/local
8 8 export PREFIX
9 9 PYTHON=python
10 10 PURE=
11 11 PYFILES:=$(shell find mercurial hgext doc -name '*.py')
12 12 DOCFILES=mercurial/help/*.txt
13 13 export LANGUAGE=C
14 14 export LC_ALL=C
15 15
16 16 # Set this to e.g. "mingw32" to use a non-default compiler.
17 17 COMPILER=
18 18
19 19 help:
20 20 @echo 'Commonly used make targets:'
21 21 @echo ' all - build program and documentation'
22 22 @echo ' install - install program and man pages to $$PREFIX ($(PREFIX))'
23 23 @echo ' install-home - install with setup.py install --home=$$HOME ($(HOME))'
24 24 @echo ' local - build for inplace usage'
25 25 @echo ' tests - run all tests in the automatic test suite'
26 26 @echo ' test-foo - run only specified tests (e.g. test-merge1.t)'
27 27 @echo ' dist - run all tests and create a source tarball in dist/'
28 28 @echo ' clean - remove files created by other targets'
29 29 @echo ' (except installed files or dist source tarball)'
30 30 @echo ' update-pot - update i18n/hg.pot'
31 31 @echo
32 32 @echo 'Example for a system-wide installation under /usr/local:'
33 33 @echo ' make all && su -c "make install" && hg version'
34 34 @echo
35 35 @echo 'Example for a local installation (usable in this directory):'
36 36 @echo ' make local && ./hg version'
37 37
38 38 all: build doc
39 39
40 40 local:
41 41 $(PYTHON) setup.py $(PURE) \
42 42 build_py -c -d . \
43 43 build_ext $(COMPILER:%=-c %) -i \
44 44 build_hgexe $(COMPILER:%=-c %) -i \
45 45 build_mo
46 46 env HGRCPATH= $(PYTHON) hg version
47 47
48 48 build:
49 49 $(PYTHON) setup.py $(PURE) build $(COMPILER:%=-c %)
50 50
51 51 doc:
52 52 $(MAKE) -C doc
53 53
54 54 clean:
55 55 -$(PYTHON) setup.py clean --all # ignore errors from this command
56 56 find contrib doc hgext i18n mercurial tests \
57 57 \( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';'
58 58 rm -f $(addprefix mercurial/,$(notdir $(wildcard mercurial/pure/[a-z]*.py)))
59 rm -f MANIFEST MANIFEST.in mercurial/__version__.py tests/*.err
59 rm -f MANIFEST MANIFEST.in mercurial/__version__.py hgext/__index__.py tests/*.err
60 60 rm -rf build mercurial/locale
61 61 $(MAKE) -C doc clean
62 62
63 63 install: install-bin install-doc
64 64
65 65 install-bin: build
66 66 $(PYTHON) setup.py $(PURE) install --root="$(DESTDIR)/" --prefix="$(PREFIX)" --force
67 67
68 68 install-doc: doc
69 69 cd doc && $(MAKE) $(MFLAGS) install
70 70
71 71 install-home: install-home-bin install-home-doc
72 72
73 73 install-home-bin: build
74 74 $(PYTHON) setup.py $(PURE) install --home="$(HOME)" --prefix="" --force
75 75
76 76 install-home-doc: doc
77 77 cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install
78 78
79 79 MANIFEST-doc:
80 80 $(MAKE) -C doc MANIFEST
81 81
82 82 MANIFEST.in: MANIFEST-doc
83 83 hg manifest | sed -e 's/^/include /' > MANIFEST.in
84 84 echo include mercurial/__version__.py >> MANIFEST.in
85 85 sed -e 's/^/include /' < doc/MANIFEST >> MANIFEST.in
86 86
87 87 dist: tests dist-notests
88 88
89 89 dist-notests: doc MANIFEST.in
90 90 TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist
91 91
92 92 check: tests
93 93
94 94 tests:
95 95 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS)
96 96
97 97 test-%:
98 98 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@
99 99
100 100 check-code:
101 101 hg manifest | xargs python contrib/check-code.py
102 102
103 103 update-pot: i18n/hg.pot
104 104
105 105 i18n/hg.pot: $(PYFILES) $(DOCFILES) i18n/posplit i18n/hggettext
106 106 $(PYTHON) i18n/hggettext mercurial/commands.py \
107 107 hgext/*.py hgext/*/__init__.py \
108 108 mercurial/fileset.py mercurial/revset.py \
109 109 mercurial/templatefilters.py mercurial/templatekw.py \
110 110 mercurial/filemerge.py \
111 111 $(DOCFILES) > i18n/hg.pot.tmp
112 112 # All strings marked for translation in Mercurial contain
113 113 # ASCII characters only. But some files contain string
114 114 # literals like this '\037\213'. xgettext thinks it has to
115 115 # parse them even though they are not marked for translation.
116 116 # Extracting with an explicit encoding of ISO-8859-1 will make
117 117 # xgettext "parse" and ignore them.
118 118 echo $(PYFILES) | xargs \
119 119 xgettext --package-name "Mercurial" \
120 120 --msgid-bugs-address "<mercurial-devel@selenic.com>" \
121 121 --copyright-holder "Matt Mackall <mpm@selenic.com> and others" \
122 122 --from-code ISO-8859-1 --join --sort-by-file --add-comments=i18n: \
123 123 -d hg -p i18n -o hg.pot.tmp
124 124 $(PYTHON) i18n/posplit i18n/hg.pot.tmp
125 125 # The target file is not created before the last step. So it never is in
126 126 # an intermediate state.
127 127 mv -f i18n/hg.pot.tmp i18n/hg.pot
128 128
129 129 %.po: i18n/hg.pot
130 130 # work on a temporary copy for never having a half completed target
131 131 cp $@ $@.tmp
132 132 msgmerge --no-location --update $@.tmp $^
133 133 mv -f $@.tmp $@
134 134
135 135 .PHONY: help all local build doc clean install install-bin install-doc \
136 136 install-home install-home-bin install-home-doc dist dist-notests tests \
137 137 update-pot
@@ -1,369 +1,369
1 1 # extensions.py - extension handling for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 import imp, os
9 9 import util, cmdutil, error
10 10 from i18n import _, gettext
11 11
12 12 _extensions = {}
13 13 _order = []
14 14 _ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg', 'inotify']
15 15
16 16 def extensions(ui=None):
17 17 if ui:
18 18 def enabled(name):
19 19 for format in ['%s', 'hgext.%s']:
20 20 conf = ui.config('extensions', format % name)
21 21 if conf is not None and not conf.startswith('!'):
22 22 return True
23 23 else:
24 24 enabled = lambda name: True
25 25 for name in _order:
26 26 module = _extensions[name]
27 27 if module and enabled(name):
28 28 yield name, module
29 29
30 30 def find(name):
31 31 '''return module with given extension name'''
32 32 mod = None
33 33 try:
34 34 mod = _extensions[name]
35 35 except KeyError:
36 36 for k, v in _extensions.iteritems():
37 37 if k.endswith('.' + name) or k.endswith('/' + name):
38 38 mod = v
39 39 break
40 40 if not mod:
41 41 raise KeyError(name)
42 42 return mod
43 43
44 44 def loadpath(path, module_name):
45 45 module_name = module_name.replace('.', '_')
46 46 path = util.normpath(util.expandpath(path))
47 47 if os.path.isdir(path):
48 48 # module/__init__.py style
49 49 d, f = os.path.split(path)
50 50 fd, fpath, desc = imp.find_module(f, [d])
51 51 return imp.load_module(module_name, fd, fpath, desc)
52 52 else:
53 53 try:
54 54 return imp.load_source(module_name, path)
55 55 except IOError, exc:
56 56 if not exc.filename:
57 57 exc.filename = path # python does not fill this
58 58 raise
59 59
60 60 def load(ui, name, path):
61 61 if name.startswith('hgext.') or name.startswith('hgext/'):
62 62 shortname = name[6:]
63 63 else:
64 64 shortname = name
65 65 if shortname in _ignore:
66 66 return None
67 67 if shortname in _extensions:
68 68 return _extensions[shortname]
69 69 _extensions[shortname] = None
70 70 if path:
71 71 # the module will be loaded in sys.modules
72 72 # choose an unique name so that it doesn't
73 73 # conflicts with other modules
74 74 mod = loadpath(path, 'hgext.%s' % name)
75 75 else:
76 76 def importh(name):
77 77 mod = __import__(name)
78 78 components = name.split('.')
79 79 for comp in components[1:]:
80 80 mod = getattr(mod, comp)
81 81 return mod
82 82 try:
83 83 mod = importh("hgext.%s" % name)
84 84 except ImportError, err:
85 85 ui.debug('could not import hgext.%s (%s): trying %s\n'
86 86 % (name, err, name))
87 87 mod = importh(name)
88 88 _extensions[shortname] = mod
89 89 _order.append(shortname)
90 90 return mod
91 91
92 92 def loadall(ui):
93 93 result = ui.configitems("extensions")
94 94 newindex = len(_order)
95 95 for (name, path) in result:
96 96 if path:
97 97 if path[0] == '!':
98 98 continue
99 99 try:
100 100 load(ui, name, path)
101 101 except KeyboardInterrupt:
102 102 raise
103 103 except Exception, inst:
104 104 if path:
105 105 ui.warn(_("*** failed to import extension %s from %s: %s\n")
106 106 % (name, path, inst))
107 107 else:
108 108 ui.warn(_("*** failed to import extension %s: %s\n")
109 109 % (name, inst))
110 110 if ui.traceback():
111 111 return 1
112 112
113 113 for name in _order[newindex:]:
114 114 uisetup = getattr(_extensions[name], 'uisetup', None)
115 115 if uisetup:
116 116 uisetup(ui)
117 117
118 118 for name in _order[newindex:]:
119 119 extsetup = getattr(_extensions[name], 'extsetup', None)
120 120 if extsetup:
121 121 try:
122 122 extsetup(ui)
123 123 except TypeError:
124 124 if extsetup.func_code.co_argcount != 0:
125 125 raise
126 126 extsetup() # old extsetup with no ui argument
127 127
128 128 def wrapcommand(table, command, wrapper):
129 129 '''Wrap the command named `command' in table
130 130
131 131 Replace command in the command table with wrapper. The wrapped command will
132 132 be inserted into the command table specified by the table argument.
133 133
134 134 The wrapper will be called like
135 135
136 136 wrapper(orig, *args, **kwargs)
137 137
138 138 where orig is the original (wrapped) function, and *args, **kwargs
139 139 are the arguments passed to it.
140 140 '''
141 141 assert util.safehasattr(wrapper, '__call__')
142 142 aliases, entry = cmdutil.findcmd(command, table)
143 143 for alias, e in table.iteritems():
144 144 if e is entry:
145 145 key = alias
146 146 break
147 147
148 148 origfn = entry[0]
149 149 def wrap(*args, **kwargs):
150 150 return util.checksignature(wrapper)(
151 151 util.checksignature(origfn), *args, **kwargs)
152 152
153 153 wrap.__doc__ = getattr(origfn, '__doc__')
154 154 wrap.__module__ = getattr(origfn, '__module__')
155 155
156 156 newentry = list(entry)
157 157 newentry[0] = wrap
158 158 table[key] = tuple(newentry)
159 159 return entry
160 160
161 161 def wrapfunction(container, funcname, wrapper):
162 162 '''Wrap the function named funcname in container
163 163
164 164 Replace the funcname member in the given container with the specified
165 165 wrapper. The container is typically a module, class, or instance.
166 166
167 167 The wrapper will be called like
168 168
169 169 wrapper(orig, *args, **kwargs)
170 170
171 171 where orig is the original (wrapped) function, and *args, **kwargs
172 172 are the arguments passed to it.
173 173
174 174 Wrapping methods of the repository object is not recommended since
175 175 it conflicts with extensions that extend the repository by
176 176 subclassing. All extensions that need to extend methods of
177 177 localrepository should use this subclassing trick: namely,
178 178 reposetup() should look like
179 179
180 180 def reposetup(ui, repo):
181 181 class myrepo(repo.__class__):
182 182 def whatever(self, *args, **kwargs):
183 183 [...extension stuff...]
184 184 super(myrepo, self).whatever(*args, **kwargs)
185 185 [...extension stuff...]
186 186
187 187 repo.__class__ = myrepo
188 188
189 189 In general, combining wrapfunction() with subclassing does not
190 190 work. Since you cannot control what other extensions are loaded by
191 191 your end users, you should play nicely with others by using the
192 192 subclass trick.
193 193 '''
194 194 assert util.safehasattr(wrapper, '__call__')
195 195 def wrap(*args, **kwargs):
196 196 return wrapper(origfn, *args, **kwargs)
197 197
198 198 origfn = getattr(container, funcname)
199 199 assert util.safehasattr(origfn, '__call__')
200 200 setattr(container, funcname, wrap)
201 201 return origfn
202 202
203 203 def _disabledpaths(strip_init=False):
204 204 '''find paths of disabled extensions. returns a dict of {name: path}
205 205 removes /__init__.py from packages if strip_init is True'''
206 206 import hgext
207 207 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
208 208 try: # might not be a filesystem path
209 209 files = os.listdir(extpath)
210 210 except OSError:
211 211 return {}
212 212
213 213 exts = {}
214 214 for e in files:
215 215 if e.endswith('.py'):
216 216 name = e.rsplit('.', 1)[0]
217 217 path = os.path.join(extpath, e)
218 218 else:
219 219 name = e
220 220 path = os.path.join(extpath, e, '__init__.py')
221 221 if not os.path.exists(path):
222 222 continue
223 223 if strip_init:
224 224 path = os.path.dirname(path)
225 225 if name in exts or name in _order or name == '__init__':
226 226 continue
227 227 exts[name] = path
228 228 return exts
229 229
230 230 def _moduledoc(file):
231 231 '''return the top-level python documentation for the given file
232 232
233 233 Loosely inspired by pydoc.source_synopsis(), but rewritten to
234 234 handle triple quotes and to return the whole text instead of just
235 235 the synopsis'''
236 236 result = []
237 237
238 238 line = file.readline()
239 239 while line[:1] == '#' or not line.strip():
240 240 line = file.readline()
241 241 if not line:
242 242 break
243 243
244 244 start = line[:3]
245 245 if start == '"""' or start == "'''":
246 246 line = line[3:]
247 247 while line:
248 248 if line.rstrip().endswith(start):
249 249 line = line.split(start)[0]
250 250 if line:
251 251 result.append(line)
252 252 break
253 253 elif not line:
254 254 return None # unmatched delimiter
255 255 result.append(line)
256 256 line = file.readline()
257 257 else:
258 258 return None
259 259
260 260 return ''.join(result)
261 261
262 262 def _disabledhelp(path):
263 263 '''retrieve help synopsis of a disabled extension (without importing)'''
264 264 try:
265 265 file = open(path)
266 266 except IOError:
267 267 return
268 268 else:
269 269 doc = _moduledoc(file)
270 270 file.close()
271 271
272 272 if doc: # extracting localized synopsis
273 273 return gettext(doc).splitlines()[0]
274 274 else:
275 275 return _('(no help text available)')
276 276
277 277 def disabled():
278 278 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
279 279 try:
280 280 from hgext import __index__
281 281 return dict((name, gettext(desc))
282 282 for name, desc in __index__.docs.iteritems()
283 283 if name not in _order)
284 except ImportError:
284 except (ImportError, AttributeError):
285 285 pass
286 286
287 287 paths = _disabledpaths()
288 288 if not paths:
289 289 return {}
290 290
291 291 exts = {}
292 292 for name, path in paths.iteritems():
293 293 doc = _disabledhelp(path)
294 294 if doc:
295 295 exts[name] = doc
296 296
297 297 return exts
298 298
299 299 def disabledext(name):
300 300 '''find a specific disabled extension from hgext. returns desc'''
301 301 try:
302 302 from hgext import __index__
303 303 if name in _order: # enabled
304 304 return
305 305 else:
306 306 return gettext(__index__.docs.get(name))
307 except ImportError:
307 except (ImportError, AttributeError):
308 308 pass
309 309
310 310 paths = _disabledpaths()
311 311 if name in paths:
312 312 return _disabledhelp(paths[name])
313 313
314 314 def disabledcmd(ui, cmd, strict=False):
315 315 '''import disabled extensions until cmd is found.
316 316 returns (cmdname, extname, module)'''
317 317
318 318 paths = _disabledpaths(strip_init=True)
319 319 if not paths:
320 320 raise error.UnknownCommand(cmd)
321 321
322 322 def findcmd(cmd, name, path):
323 323 try:
324 324 mod = loadpath(path, 'hgext.%s' % name)
325 325 except Exception:
326 326 return
327 327 try:
328 328 aliases, entry = cmdutil.findcmd(cmd,
329 329 getattr(mod, 'cmdtable', {}), strict)
330 330 except (error.AmbiguousCommand, error.UnknownCommand):
331 331 return
332 332 except Exception:
333 333 ui.warn(_('warning: error finding commands in %s\n') % path)
334 334 ui.traceback()
335 335 return
336 336 for c in aliases:
337 337 if c.startswith(cmd):
338 338 cmd = c
339 339 break
340 340 else:
341 341 cmd = aliases[0]
342 342 return (cmd, name, mod)
343 343
344 344 ext = None
345 345 # first, search for an extension with the same name as the command
346 346 path = paths.pop(cmd, None)
347 347 if path:
348 348 ext = findcmd(cmd, cmd, path)
349 349 if not ext:
350 350 # otherwise, interrogate each extension until there's a match
351 351 for name, path in paths.iteritems():
352 352 ext = findcmd(cmd, name, path)
353 353 if ext:
354 354 break
355 355 if ext and 'DEPRECATED' not in ext.__doc__:
356 356 return ext
357 357
358 358 raise error.UnknownCommand(cmd)
359 359
360 360 def enabled(shortname=True):
361 361 '''return a dict of {name: desc} of extensions'''
362 362 exts = {}
363 363 for ename, ext in extensions():
364 364 doc = (gettext(ext.__doc__) or _('(no help text available)'))
365 365 if shortname:
366 366 ename = ename.split('.')[-1]
367 367 exts[ename] = doc.splitlines()[0].strip()
368 368
369 369 return exts
@@ -1,586 +1,588
1 1 #
2 2 # This is the mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6
7 7 import sys, platform
8 8 if getattr(sys, 'version_info', (0, 0, 0)) < (2, 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 printf = eval('print')
17 17 libdir_escape = 'unicode_escape'
18 18 else:
19 19 libdir_escape = 'string_escape'
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 def printf(*args, **kwargs):
25 25 f = kwargs.get('file', sys.stdout)
26 26 end = kwargs.get('end', '\n')
27 27 f.write(b(' ').join(args) + end)
28 28
29 29 # Solaris Python packaging brain damage
30 30 try:
31 31 import hashlib
32 32 sha = hashlib.sha1()
33 33 except ImportError:
34 34 try:
35 35 import sha
36 36 except ImportError:
37 37 raise SystemExit(
38 38 "Couldn't import standard hashlib (incomplete Python install).")
39 39
40 40 try:
41 41 import zlib
42 42 except ImportError:
43 43 raise SystemExit(
44 44 "Couldn't import standard zlib (incomplete Python install).")
45 45
46 46 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
47 47 isironpython = False
48 48 try:
49 49 isironpython = (platform.python_implementation()
50 50 .lower().find("ironpython") != -1)
51 51 except AttributeError:
52 52 pass
53 53
54 54 if isironpython:
55 55 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
56 56 else:
57 57 try:
58 58 import bz2
59 59 except ImportError:
60 60 raise SystemExit(
61 61 "Couldn't import standard bz2 (incomplete Python install).")
62 62
63 63 import os, subprocess, time
64 64 import re
65 65 import shutil
66 66 import tempfile
67 67 from distutils import log
68 68 from distutils.core import setup, Command, Extension
69 69 from distutils.dist import Distribution
70 70 from distutils.command.build import build
71 71 from distutils.command.build_ext import build_ext
72 72 from distutils.command.build_py import build_py
73 73 from distutils.command.install_scripts import install_scripts
74 74 from distutils.spawn import spawn, find_executable
75 75 from distutils import cygwinccompiler
76 76 from distutils.errors import CCompilerError, DistutilsExecError
77 77 from distutils.sysconfig import get_python_inc, get_config_var
78 78 from distutils.version import StrictVersion
79 79
80 80 convert2to3 = '--c2to3' in sys.argv
81 81 if convert2to3:
82 82 try:
83 83 from distutils.command.build_py import build_py_2to3 as build_py
84 84 from lib2to3.refactor import get_fixers_from_package as getfixers
85 85 except ImportError:
86 86 if sys.version_info[0] < 3:
87 87 raise SystemExit("--c2to3 is only compatible with python3.")
88 88 raise
89 89 sys.path.append('contrib')
90 90 elif sys.version_info[0] >= 3:
91 91 raise SystemExit("setup.py with python3 needs --c2to3 (experimental)")
92 92
93 93 scripts = ['hg']
94 94 if os.name == 'nt':
95 95 scripts.append('contrib/win32/hg.bat')
96 96
97 97 # simplified version of distutils.ccompiler.CCompiler.has_function
98 98 # that actually removes its temporary files.
99 99 def hasfunction(cc, funcname):
100 100 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
101 101 devnull = oldstderr = None
102 102 try:
103 103 try:
104 104 fname = os.path.join(tmpdir, 'funcname.c')
105 105 f = open(fname, 'w')
106 106 f.write('int main(void) {\n')
107 107 f.write(' %s();\n' % funcname)
108 108 f.write('}\n')
109 109 f.close()
110 110 # Redirect stderr to /dev/null to hide any error messages
111 111 # from the compiler.
112 112 # This will have to be changed if we ever have to check
113 113 # for a function on Windows.
114 114 devnull = open('/dev/null', 'w')
115 115 oldstderr = os.dup(sys.stderr.fileno())
116 116 os.dup2(devnull.fileno(), sys.stderr.fileno())
117 117 objects = cc.compile([fname], output_dir=tmpdir)
118 118 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
119 119 except Exception:
120 120 return False
121 121 return True
122 122 finally:
123 123 if oldstderr is not None:
124 124 os.dup2(oldstderr, sys.stderr.fileno())
125 125 if devnull is not None:
126 126 devnull.close()
127 127 shutil.rmtree(tmpdir)
128 128
129 129 # py2exe needs to be installed to work
130 130 try:
131 131 import py2exe
132 132 py2exeloaded = True
133 133 # import py2exe's patched Distribution class
134 134 from distutils.core import Distribution
135 135 except ImportError:
136 136 py2exeloaded = False
137 137
138 138 def runcmd(cmd, env):
139 139 if sys.platform == 'plan9':
140 140 # subprocess kludge to work around issues in half-baked Python
141 141 # ports, notably bichued/python:
142 142 _, out, err = os.popen3(cmd)
143 143 return str(out), str(err)
144 144 else:
145 145 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
146 146 stderr=subprocess.PIPE, env=env)
147 147 out, err = p.communicate()
148 148 return out, err
149 149
150 150 def runhg(cmd, env):
151 151 out, err = runcmd(cmd, env)
152 152 # If root is executing setup.py, but the repository is owned by
153 153 # another user (as in "sudo python setup.py install") we will get
154 154 # trust warnings since the .hg/hgrc file is untrusted. That is
155 155 # fine, we don't want to load it anyway. Python may warn about
156 156 # a missing __init__.py in mercurial/locale, we also ignore that.
157 157 err = [e for e in err.splitlines()
158 158 if not e.startswith(b('not trusting file')) \
159 159 and not e.startswith(b('warning: Not importing')) \
160 160 and not e.startswith(b('obsolete feature not enabled'))]
161 161 if err:
162 162 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
163 163 printf(b('\n').join([b(' ') + e for e in err]), file=sys.stderr)
164 164 return ''
165 165 return out
166 166
167 167 version = ''
168 168
169 169 # Execute hg out of this directory with a custom environment which
170 170 # includes the pure Python modules in mercurial/pure. We also take
171 171 # care to not use any hgrc files and do no localization.
172 172 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
173 173 env = {'PYTHONPATH': os.pathsep.join(pypath),
174 174 'HGRCPATH': '',
175 175 'LANGUAGE': 'C'}
176 176 if 'LD_LIBRARY_PATH' in os.environ:
177 177 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
178 178 if 'SystemRoot' in os.environ:
179 179 # Copy SystemRoot into the custom environment for Python 2.6
180 180 # under Windows. Otherwise, the subprocess will fail with
181 181 # error 0xc0150004. See: http://bugs.python.org/issue3440
182 182 env['SystemRoot'] = os.environ['SystemRoot']
183 183
184 184 if os.path.isdir('.hg'):
185 185 cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
186 186 numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
187 187 hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()
188 188 if numerictags: # tag(s) found
189 189 version = numerictags[-1]
190 190 if hgid.endswith('+'): # propagate the dirty status to the tag
191 191 version += '+'
192 192 else: # no tag found
193 193 cmd = [sys.executable, 'hg', 'parents', '--template',
194 194 '{latesttag}+{latesttagdistance}-']
195 195 version = runhg(cmd, env) + hgid
196 196 if version.endswith('+'):
197 197 version += time.strftime('%Y%m%d')
198 198 elif os.path.exists('.hg_archival.txt'):
199 199 kw = dict([[t.strip() for t in l.split(':', 1)]
200 200 for l in open('.hg_archival.txt')])
201 201 if 'tag' in kw:
202 202 version = kw['tag']
203 203 elif 'latesttag' in kw:
204 204 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
205 205 else:
206 206 version = kw.get('node', '')[:12]
207 207
208 208 if version:
209 209 f = open("mercurial/__version__.py", "w")
210 210 f.write('# this file is autogenerated by setup.py\n')
211 211 f.write('version = "%s"\n' % version)
212 212 f.close()
213 213
214 214
215 215 try:
216 216 from mercurial import __version__
217 217 version = __version__.version
218 218 except ImportError:
219 219 version = 'unknown'
220 220
221 221 class hgbuild(build):
222 222 # Insert hgbuildmo first so that files in mercurial/locale/ are found
223 223 # when build_py is run next.
224 224 sub_commands = [('build_mo', None),
225 225
226 226 # We also need build_ext before build_py. Otherwise, when 2to3 is
227 227 # called (in build_py), it will not find osutil & friends,
228 228 # thinking that those modules are global and, consequently, making
229 229 # a mess, now that all module imports are global.
230 230
231 231 ('build_ext', build.has_ext_modules),
232 232 ] + build.sub_commands
233 233
234 234 class hgbuildmo(build):
235 235
236 236 description = "build translations (.mo files)"
237 237
238 238 def run(self):
239 239 if not find_executable('msgfmt'):
240 240 self.warn("could not find msgfmt executable, no translations "
241 241 "will be built")
242 242 return
243 243
244 244 podir = 'i18n'
245 245 if not os.path.isdir(podir):
246 246 self.warn("could not find %s/ directory" % podir)
247 247 return
248 248
249 249 join = os.path.join
250 250 for po in os.listdir(podir):
251 251 if not po.endswith('.po'):
252 252 continue
253 253 pofile = join(podir, po)
254 254 modir = join('locale', po[:-3], 'LC_MESSAGES')
255 255 mofile = join(modir, 'hg.mo')
256 256 mobuildfile = join('mercurial', mofile)
257 257 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
258 258 if sys.platform != 'sunos5':
259 259 # msgfmt on Solaris does not know about -c
260 260 cmd.append('-c')
261 261 self.mkpath(join('mercurial', modir))
262 262 self.make_file([pofile], mobuildfile, spawn, (cmd,))
263 263
264 264
265 265 class hgdist(Distribution):
266 266 pure = 0
267 267
268 268 global_options = Distribution.global_options + \
269 269 [('pure', None, "use pure (slow) Python "
270 270 "code instead of C extensions"),
271 271 ('c2to3', None, "(experimental!) convert "
272 272 "code with 2to3"),
273 273 ]
274 274
275 275 def has_ext_modules(self):
276 276 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
277 277 # too late for some cases
278 278 return not self.pure and Distribution.has_ext_modules(self)
279 279
280 280 class hgbuildext(build_ext):
281 281
282 282 def build_extension(self, ext):
283 283 try:
284 284 build_ext.build_extension(self, ext)
285 285 except CCompilerError:
286 286 if not getattr(ext, 'optional', False):
287 287 raise
288 288 log.warn("Failed to build optional extension '%s' (skipping)",
289 289 ext.name)
290 290
291 291 class hgbuildpy(build_py):
292 292 if convert2to3:
293 293 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
294 294 getfixers("hgfixes")))
295 295
296 296 def finalize_options(self):
297 297 build_py.finalize_options(self)
298 298
299 299 if self.distribution.pure:
300 300 if self.py_modules is None:
301 301 self.py_modules = []
302 302 for ext in self.distribution.ext_modules:
303 303 if ext.name.startswith("mercurial."):
304 304 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
305 305 self.distribution.ext_modules = []
306 306 else:
307 307 h = os.path.join(get_python_inc(), 'Python.h')
308 308 if not os.path.exists(h):
309 309 raise SystemExit('Python headers are required to build '
310 310 'Mercurial but weren\'t found in %s' % h)
311 311
312 312 def find_modules(self):
313 313 modules = build_py.find_modules(self)
314 314 for module in modules:
315 315 if module[0] == "mercurial.pure":
316 316 if module[1] != "__init__":
317 317 yield ("mercurial", module[1], module[2])
318 318 else:
319 319 yield module
320 320
321 321 class buildhgextindex(Command):
322 322 description = 'generate prebuilt index of hgext (for frozen package)'
323 323 user_options = []
324 324 _indexfilename = 'hgext/__index__.py'
325 325
326 326 def initialize_options(self):
327 327 pass
328 328
329 329 def finalize_options(self):
330 330 pass
331 331
332 332 def run(self):
333 333 if os.path.exists(self._indexfilename):
334 os.unlink(self._indexfilename)
334 f = open(self._indexfilename, 'w')
335 f.write('# empty\n')
336 f.close()
335 337
336 338 # here no extension enabled, disabled() lists up everything
337 339 code = ('import pprint; from mercurial import extensions; '
338 340 'pprint.pprint(extensions.disabled())')
339 341 out, err = runcmd([sys.executable, '-c', code], env)
340 342 if err:
341 343 raise DistutilsExecError(err)
342 344
343 345 f = open(self._indexfilename, 'w')
344 346 f.write('# this file is autogenerated by setup.py\n')
345 347 f.write('docs = ')
346 348 f.write(out)
347 349 f.close()
348 350
349 351 class buildhgexe(build_ext):
350 352 description = 'compile hg.exe from mercurial/exewrapper.c'
351 353
352 354 def build_extensions(self):
353 355 if os.name != 'nt':
354 356 return
355 357 if isinstance(self.compiler, HackedMingw32CCompiler):
356 358 self.compiler.compiler_so = self.compiler.compiler # no -mdll
357 359 self.compiler.dll_libraries = [] # no -lmsrvc90
358 360 hv = sys.hexversion
359 361 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
360 362 f = open('mercurial/hgpythonlib.h', 'wb')
361 363 f.write('/* this file is autogenerated by setup.py */\n')
362 364 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
363 365 f.close()
364 366 objects = self.compiler.compile(['mercurial/exewrapper.c'],
365 367 output_dir=self.build_temp)
366 368 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
367 369 target = os.path.join(dir, 'hg')
368 370 self.compiler.link_executable(objects, target,
369 371 libraries=[],
370 372 output_dir=self.build_temp)
371 373
372 374 class hginstallscripts(install_scripts):
373 375 '''
374 376 This is a specialization of install_scripts that replaces the @LIBDIR@ with
375 377 the configured directory for modules. If possible, the path is made relative
376 378 to the directory for scripts.
377 379 '''
378 380
379 381 def initialize_options(self):
380 382 install_scripts.initialize_options(self)
381 383
382 384 self.install_lib = None
383 385
384 386 def finalize_options(self):
385 387 install_scripts.finalize_options(self)
386 388 self.set_undefined_options('install',
387 389 ('install_lib', 'install_lib'))
388 390
389 391 def run(self):
390 392 install_scripts.run(self)
391 393
392 394 if (os.path.splitdrive(self.install_dir)[0] !=
393 395 os.path.splitdrive(self.install_lib)[0]):
394 396 # can't make relative paths from one drive to another, so use an
395 397 # absolute path instead
396 398 libdir = self.install_lib
397 399 else:
398 400 common = os.path.commonprefix((self.install_dir, self.install_lib))
399 401 rest = self.install_dir[len(common):]
400 402 uplevel = len([n for n in os.path.split(rest) if n])
401 403
402 404 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
403 405
404 406 for outfile in self.outfiles:
405 407 fp = open(outfile, 'rb')
406 408 data = fp.read()
407 409 fp.close()
408 410
409 411 # skip binary files
410 412 if b('\0') in data:
411 413 continue
412 414
413 415 data = data.replace(b('@LIBDIR@'), libdir.encode(libdir_escape))
414 416 fp = open(outfile, 'wb')
415 417 fp.write(data)
416 418 fp.close()
417 419
418 420 cmdclass = {'build': hgbuild,
419 421 'build_mo': hgbuildmo,
420 422 'build_ext': hgbuildext,
421 423 'build_py': hgbuildpy,
422 424 'build_hgextindex': buildhgextindex,
423 425 'install_scripts': hginstallscripts,
424 426 'build_hgexe': buildhgexe,
425 427 }
426 428
427 429 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
428 430 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
429 431 'hgext.largefiles']
430 432
431 433 pymodules = []
432 434
433 435 common_depends = ['mercurial/util.h']
434 436
435 437 extmodules = [
436 438 Extension('mercurial.base85', ['mercurial/base85.c'],
437 439 depends=common_depends),
438 440 Extension('mercurial.bdiff', ['mercurial/bdiff.c'],
439 441 depends=common_depends),
440 442 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
441 443 depends=common_depends),
442 444 Extension('mercurial.mpatch', ['mercurial/mpatch.c'],
443 445 depends=common_depends),
444 446 Extension('mercurial.parsers', ['mercurial/dirs.c',
445 447 'mercurial/parsers.c',
446 448 'mercurial/pathencode.c'],
447 449 depends=common_depends),
448 450 ]
449 451
450 452 osutil_ldflags = []
451 453
452 454 if sys.platform == 'darwin':
453 455 osutil_ldflags += ['-framework', 'ApplicationServices']
454 456
455 457 # disable osutil.c under windows + python 2.4 (issue1364)
456 458 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
457 459 pymodules.append('mercurial.pure.osutil')
458 460 else:
459 461 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
460 462 extra_link_args=osutil_ldflags,
461 463 depends=common_depends))
462 464
463 465 # the -mno-cygwin option has been deprecated for years
464 466 Mingw32CCompiler = cygwinccompiler.Mingw32CCompiler
465 467
466 468 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
467 469 def __init__(self, *args, **kwargs):
468 470 Mingw32CCompiler.__init__(self, *args, **kwargs)
469 471 for i in 'compiler compiler_so linker_exe linker_so'.split():
470 472 try:
471 473 getattr(self, i).remove('-mno-cygwin')
472 474 except ValueError:
473 475 pass
474 476
475 477 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
476 478
477 479 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
478 480 'help/*.txt']}
479 481
480 482 def ordinarypath(p):
481 483 return p and p[0] != '.' and p[-1] != '~'
482 484
483 485 for root in ('templates',):
484 486 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
485 487 curdir = curdir.split(os.sep, 1)[1]
486 488 dirs[:] = filter(ordinarypath, dirs)
487 489 for f in filter(ordinarypath, files):
488 490 f = os.path.join(curdir, f)
489 491 packagedata['mercurial'].append(f)
490 492
491 493 datafiles = []
492 494 setupversion = version
493 495 extra = {}
494 496
495 497 if py2exeloaded:
496 498 extra['console'] = [
497 499 {'script':'hg',
498 500 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
499 501 'product_version':version}]
500 502 # sub command of 'build' because 'py2exe' does not handle sub_commands
501 503 build.sub_commands.insert(0, ('build_hgextindex', None))
502 504
503 505 if os.name == 'nt':
504 506 # Windows binary file versions for exe/dll files must have the
505 507 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
506 508 setupversion = version.split('+', 1)[0]
507 509
508 510 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
509 511 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
510 512 if version:
511 513 version = version[0]
512 514 xcode4 = (version.startswith('Xcode') and
513 515 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
514 516 xcode51 = re.match(r'^Xcode\s+5\.1\.', version) is not None
515 517 else:
516 518 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
517 519 # installed, but instead with only command-line tools. Assume
518 520 # that only happens on >= Lion, thus no PPC support.
519 521 xcode4 = True
520 522 xcode51 = False
521 523
522 524 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
523 525 # distutils.sysconfig
524 526 if xcode4:
525 527 os.environ['ARCHFLAGS'] = ''
526 528
527 529 # XCode 5.1 changes clang such that it now fails to compile if the
528 530 # -mno-fused-madd flag is passed, but the version of Python shipped with
529 531 # OS X 10.9 Mavericks includes this flag. This causes problems in all
530 532 # C extension modules, and a bug has been filed upstream at
531 533 # http://bugs.python.org/issue21244. We also need to patch this here
532 534 # so Mercurial can continue to compile in the meantime.
533 535 if xcode51:
534 536 cflags = get_config_var('CFLAGS')
535 537 if re.search(r'-mno-fused-madd\b', cflags) is not None:
536 538 os.environ['CFLAGS'] = (
537 539 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
538 540
539 541 setup(name='mercurial',
540 542 version=setupversion,
541 543 author='Matt Mackall and many others',
542 544 author_email='mercurial@selenic.com',
543 545 url='http://mercurial.selenic.com/',
544 546 download_url='http://mercurial.selenic.com/release/',
545 547 description=('Fast scalable distributed SCM (revision control, version '
546 548 'control) system'),
547 549 long_description=('Mercurial is a distributed SCM tool written in Python.'
548 550 ' It is used by a number of large projects that require'
549 551 ' fast, reliable distributed revision control, such as '
550 552 'Mozilla.'),
551 553 license='GNU GPLv2 or any later version',
552 554 classifiers=[
553 555 'Development Status :: 6 - Mature',
554 556 'Environment :: Console',
555 557 'Intended Audience :: Developers',
556 558 'Intended Audience :: System Administrators',
557 559 'License :: OSI Approved :: GNU General Public License (GPL)',
558 560 'Natural Language :: Danish',
559 561 'Natural Language :: English',
560 562 'Natural Language :: German',
561 563 'Natural Language :: Italian',
562 564 'Natural Language :: Japanese',
563 565 'Natural Language :: Portuguese (Brazilian)',
564 566 'Operating System :: Microsoft :: Windows',
565 567 'Operating System :: OS Independent',
566 568 'Operating System :: POSIX',
567 569 'Programming Language :: C',
568 570 'Programming Language :: Python',
569 571 'Topic :: Software Development :: Version Control',
570 572 ],
571 573 scripts=scripts,
572 574 packages=packages,
573 575 py_modules=pymodules,
574 576 ext_modules=extmodules,
575 577 data_files=datafiles,
576 578 package_data=packagedata,
577 579 cmdclass=cmdclass,
578 580 distclass=hgdist,
579 581 options={'py2exe': {'packages': ['hgext', 'email']},
580 582 'bdist_mpkg': {'zipdist': True,
581 583 'license': 'COPYING',
582 584 'readme': 'contrib/macosx/Readme.html',
583 585 'welcome': 'contrib/macosx/Welcome.html',
584 586 },
585 587 },
586 588 **extra)
General Comments 0
You need to be logged in to leave comments. Login now