##// END OF EJS Templates
mercurial: implement import hook for handling C/Python modules...
Gregory Szorc -
r27220:4374d819 default
parent child Browse files
Show More
@@ -0,0 +1,109
1 # __init__.py - Startup and module loading logic for Mercurial.
2 #
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 from __future__ import absolute_import
9
10 import imp
11 import os
12 import sys
13
14 __all__ = []
15
16 # Rules for how modules can be loaded. Values are:
17 #
18 # c - require C extensions
19 # allow - allow pure Python implementation when C loading fails
20 # py - only load pure Python modules
21 modulepolicy = '@MODULELOADPOLICY@'
22
23 # By default, require the C extensions for performance reasons.
24 if modulepolicy == '@' 'MODULELOADPOLICY' '@':
25 # TODO change to 'c' once installer is changed.
26 modulepolicy = 'allow'
27
28 # Environment variable can always force settings.
29 modulepolicy = os.environ.get('HGMODULEPOLICY', modulepolicy)
30
31 # Modules that have both Python and C implementations. See also the
32 # set of .py files under mercurial/pure/.
33 _dualmodules = set([
34 'mercurial.base85',
35 'mercurial.bdiff',
36 'mercurial.diffhelpers',
37 'mercurial.mpatch',
38 'mercurial.osutil',
39 'mercurial.parsers',
40 ])
41
42 class hgimporter(object):
43 """Object that conforms to import hook interface defined in PEP-302."""
44 def find_module(self, name, path=None):
45 # We only care about modules that have both C and pure implementations.
46 if name in _dualmodules:
47 return self
48 return None
49
50 def load_module(self, name):
51 mod = sys.modules.get(name, None)
52 if mod:
53 return mod
54
55 mercurial = sys.modules['mercurial']
56
57 # Unlike the default importer which searches special locations and
58 # sys.path, we only look in the directory where "mercurial" was
59 # imported from.
60
61 # imp.find_module doesn't support submodules (modules with ".").
62 # Instead you have to pass the parent package's __path__ attribute
63 # as the path argument.
64 stem = name.split('.')[-1]
65
66 try:
67 if modulepolicy == 'py':
68 raise ImportError()
69
70 modinfo = imp.find_module(stem, mercurial.__path__)
71
72 # The Mercurial installer used to copy files from
73 # mercurial/pure/*.py to mercurial/*.py. Therefore, it's possible
74 # for some installations to have .py files under mercurial/*.
75 # Loading Python modules when we expected C versions could result
76 # in a) poor performance b) loading a version from a previous
77 # Mercurial version, potentially leading to incompatibility. Either
78 # scenario is bad. So we verify that modules loaded from
79 # mercurial/* are C extensions. If the current policy allows the
80 # loading of .py modules, the module will be re-imported from
81 # mercurial/pure/* below.
82 # TODO uncomment once setup.py is updated to actually install
83 # into mercurial/pure.
84 #if modinfo[2][2] != imp.C_EXTENSION:
85 # raise ImportError('.py version of %s found where C '
86 # 'version should exist' % name)
87
88 except ImportError:
89 if modulepolicy == 'c':
90 raise
91
92 # Could not load the C extension and pure Python is allowed. So
93 # try to load them.
94 from . import pure
95 modinfo = imp.find_module(stem, pure.__path__)
96 if not modinfo:
97 raise ImportError('could not find mercurial module %s' %
98 name)
99
100 mod = imp.load_module(name, *modinfo)
101 sys.modules[name] = mod
102 return mod
103
104 # We automagically register our custom importer as a side-effect of loading.
105 # This is necessary to ensure that any entry points are able to import
106 # mercurial.* modules without having to perform this registration themselves.
107 if not any(isinstance(x, hgimporter) for x in sys.meta_path):
108 # meta_path is used before any implicit finders and before sys.path.
109 sys.meta_path.insert(0, hgimporter())
@@ -1,649 +1,647
1 1 #
2 2 # This is the mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6
7 7 import sys, platform
8 8 if getattr(sys, 'version_info', (0, 0, 0)) < (2, 6, 0, 'final'):
9 9 raise SystemExit("Mercurial requires Python 2.6 or later.")
10 10
11 11 if sys.version_info[0] >= 3:
12 12 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 sha.sha # silence unused import warning
37 37 except ImportError:
38 38 raise SystemExit(
39 39 "Couldn't import standard hashlib (incomplete Python install).")
40 40
41 41 try:
42 42 import zlib
43 43 zlib.compressobj # silence unused import warning
44 44 except ImportError:
45 45 raise SystemExit(
46 46 "Couldn't import standard zlib (incomplete Python install).")
47 47
48 48 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
49 49 isironpython = False
50 50 try:
51 51 isironpython = (platform.python_implementation()
52 52 .lower().find("ironpython") != -1)
53 53 except AttributeError:
54 54 pass
55 55
56 56 if isironpython:
57 57 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
58 58 else:
59 59 try:
60 60 import bz2
61 61 bz2.BZ2Compressor # silence unused import warning
62 62 except ImportError:
63 63 raise SystemExit(
64 64 "Couldn't import standard bz2 (incomplete Python install).")
65 65
66 66 ispypy = "PyPy" in sys.version
67 67
68 68 import os, stat, subprocess, time
69 69 import re
70 70 import shutil
71 71 import tempfile
72 72 from distutils import log
73 73 if 'FORCE_SETUPTOOLS' in os.environ:
74 74 from setuptools import setup
75 75 else:
76 76 from distutils.core import setup
77 77 from distutils.core import Command, Extension
78 78 from distutils.dist import Distribution
79 79 from distutils.command.build import build
80 80 from distutils.command.build_ext import build_ext
81 81 from distutils.command.build_py import build_py
82 82 from distutils.command.install_lib import install_lib
83 83 from distutils.command.install_scripts import install_scripts
84 84 from distutils.spawn import spawn, find_executable
85 85 from distutils import file_util
86 86 from distutils.errors import CCompilerError, DistutilsExecError
87 87 from distutils.sysconfig import get_python_inc, get_config_var
88 88 from distutils.version import StrictVersion
89 89
90 90 convert2to3 = '--c2to3' in sys.argv
91 91 if convert2to3:
92 92 try:
93 93 from distutils.command.build_py import build_py_2to3 as build_py
94 94 from lib2to3.refactor import get_fixers_from_package as getfixers
95 95 except ImportError:
96 96 if sys.version_info[0] < 3:
97 97 raise SystemExit("--c2to3 is only compatible with python3.")
98 98 raise
99 99 sys.path.append('contrib')
100 100 elif sys.version_info[0] >= 3:
101 101 raise SystemExit("setup.py with python3 needs --c2to3 (experimental)")
102 102
103 103 scripts = ['hg']
104 104 if os.name == 'nt':
105 105 scripts.append('contrib/win32/hg.bat')
106 106
107 107 # simplified version of distutils.ccompiler.CCompiler.has_function
108 108 # that actually removes its temporary files.
109 109 def hasfunction(cc, funcname):
110 110 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
111 111 devnull = oldstderr = None
112 112 try:
113 113 fname = os.path.join(tmpdir, 'funcname.c')
114 114 f = open(fname, 'w')
115 115 f.write('int main(void) {\n')
116 116 f.write(' %s();\n' % funcname)
117 117 f.write('}\n')
118 118 f.close()
119 119 # Redirect stderr to /dev/null to hide any error messages
120 120 # from the compiler.
121 121 # This will have to be changed if we ever have to check
122 122 # for a function on Windows.
123 123 devnull = open('/dev/null', 'w')
124 124 oldstderr = os.dup(sys.stderr.fileno())
125 125 os.dup2(devnull.fileno(), sys.stderr.fileno())
126 126 objects = cc.compile([fname], output_dir=tmpdir)
127 127 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
128 128 return True
129 129 except Exception:
130 130 return False
131 131 finally:
132 132 if oldstderr is not None:
133 133 os.dup2(oldstderr, sys.stderr.fileno())
134 134 if devnull is not None:
135 135 devnull.close()
136 136 shutil.rmtree(tmpdir)
137 137
138 138 # py2exe needs to be installed to work
139 139 try:
140 140 import py2exe
141 141 py2exe.Distribution # silence unused import warning
142 142 py2exeloaded = True
143 143 # import py2exe's patched Distribution class
144 144 from distutils.core import Distribution
145 145 except ImportError:
146 146 py2exeloaded = False
147 147
148 148 def runcmd(cmd, env):
149 149 if (sys.platform == 'plan9'
150 150 and (sys.version_info[0] == 2 and sys.version_info[1] < 7)):
151 151 # subprocess kludge to work around issues in half-baked Python
152 152 # ports, notably bichued/python:
153 153 _, out, err = os.popen3(cmd)
154 154 return str(out), str(err)
155 155 else:
156 156 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
157 157 stderr=subprocess.PIPE, env=env)
158 158 out, err = p.communicate()
159 159 return out, err
160 160
161 161 def runhg(cmd, env):
162 162 out, err = runcmd(cmd, env)
163 163 # If root is executing setup.py, but the repository is owned by
164 164 # another user (as in "sudo python setup.py install") we will get
165 165 # trust warnings since the .hg/hgrc file is untrusted. That is
166 166 # fine, we don't want to load it anyway. Python may warn about
167 167 # a missing __init__.py in mercurial/locale, we also ignore that.
168 168 err = [e for e in err.splitlines()
169 169 if not e.startswith(b('not trusting file')) \
170 170 and not e.startswith(b('warning: Not importing')) \
171 171 and not e.startswith(b('obsolete feature not enabled'))]
172 172 if err:
173 173 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
174 174 printf(b('\n').join([b(' ') + e for e in err]), file=sys.stderr)
175 175 return ''
176 176 return out
177 177
178 178 version = ''
179 179
180 # Execute hg out of this directory with a custom environment which
181 # includes the pure Python modules in mercurial/pure. We also take
182 # care to not use any hgrc files and do no localization.
183 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
184 env = {'PYTHONPATH': os.pathsep.join(pypath),
180 # Execute hg out of this directory with a custom environment which takes care
181 # to not use any hgrc files and do no localization.
182 env = {'HGMODULEPOLICY': 'py',
185 183 'HGRCPATH': '',
186 184 'LANGUAGE': 'C'}
187 185 if 'LD_LIBRARY_PATH' in os.environ:
188 186 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
189 187 if 'SystemRoot' in os.environ:
190 188 # Copy SystemRoot into the custom environment for Python 2.6
191 189 # under Windows. Otherwise, the subprocess will fail with
192 190 # error 0xc0150004. See: http://bugs.python.org/issue3440
193 191 env['SystemRoot'] = os.environ['SystemRoot']
194 192
195 193 if os.path.isdir('.hg'):
196 194 cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
197 195 numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
198 196 hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()
199 197 if numerictags: # tag(s) found
200 198 version = numerictags[-1]
201 199 if hgid.endswith('+'): # propagate the dirty status to the tag
202 200 version += '+'
203 201 else: # no tag found
204 202 ltagcmd = [sys.executable, 'hg', 'parents', '--template',
205 203 '{latesttag}']
206 204 ltag = runhg(ltagcmd, env)
207 205 changessincecmd = [sys.executable, 'hg', 'log', '-T', 'x\n', '-r',
208 206 "only(.,'%s')" % ltag]
209 207 changessince = len(runhg(changessincecmd, env).splitlines())
210 208 version = '%s+%s-%s' % (ltag, changessince, hgid)
211 209 if version.endswith('+'):
212 210 version += time.strftime('%Y%m%d')
213 211 elif os.path.exists('.hg_archival.txt'):
214 212 kw = dict([[t.strip() for t in l.split(':', 1)]
215 213 for l in open('.hg_archival.txt')])
216 214 if 'tag' in kw:
217 215 version = kw['tag']
218 216 elif 'latesttag' in kw:
219 217 if 'changessincelatesttag' in kw:
220 218 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
221 219 else:
222 220 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
223 221 else:
224 222 version = kw.get('node', '')[:12]
225 223
226 224 if version:
227 225 f = open("mercurial/__version__.py", "w")
228 226 f.write('# this file is autogenerated by setup.py\n')
229 227 f.write('version = "%s"\n' % version)
230 228 f.close()
231 229
232 230
233 231 try:
234 232 from mercurial import __version__
235 233 version = __version__.version
236 234 except ImportError:
237 235 version = 'unknown'
238 236
239 237 class hgbuild(build):
240 238 # Insert hgbuildmo first so that files in mercurial/locale/ are found
241 239 # when build_py is run next.
242 240 sub_commands = [('build_mo', None),
243 241
244 242 # We also need build_ext before build_py. Otherwise, when 2to3 is
245 243 # called (in build_py), it will not find osutil & friends,
246 244 # thinking that those modules are global and, consequently, making
247 245 # a mess, now that all module imports are global.
248 246
249 247 ('build_ext', build.has_ext_modules),
250 248 ] + build.sub_commands
251 249
252 250 class hgbuildmo(build):
253 251
254 252 description = "build translations (.mo files)"
255 253
256 254 def run(self):
257 255 if not find_executable('msgfmt'):
258 256 self.warn("could not find msgfmt executable, no translations "
259 257 "will be built")
260 258 return
261 259
262 260 podir = 'i18n'
263 261 if not os.path.isdir(podir):
264 262 self.warn("could not find %s/ directory" % podir)
265 263 return
266 264
267 265 join = os.path.join
268 266 for po in os.listdir(podir):
269 267 if not po.endswith('.po'):
270 268 continue
271 269 pofile = join(podir, po)
272 270 modir = join('locale', po[:-3], 'LC_MESSAGES')
273 271 mofile = join(modir, 'hg.mo')
274 272 mobuildfile = join('mercurial', mofile)
275 273 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
276 274 if sys.platform != 'sunos5':
277 275 # msgfmt on Solaris does not know about -c
278 276 cmd.append('-c')
279 277 self.mkpath(join('mercurial', modir))
280 278 self.make_file([pofile], mobuildfile, spawn, (cmd,))
281 279
282 280
283 281 class hgdist(Distribution):
284 282 pure = ispypy
285 283
286 284 global_options = Distribution.global_options + \
287 285 [('pure', None, "use pure (slow) Python "
288 286 "code instead of C extensions"),
289 287 ('c2to3', None, "(experimental!) convert "
290 288 "code with 2to3"),
291 289 ]
292 290
293 291 def has_ext_modules(self):
294 292 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
295 293 # too late for some cases
296 294 return not self.pure and Distribution.has_ext_modules(self)
297 295
298 296 class hgbuildext(build_ext):
299 297
300 298 def build_extension(self, ext):
301 299 try:
302 300 build_ext.build_extension(self, ext)
303 301 except CCompilerError:
304 302 if not getattr(ext, 'optional', False):
305 303 raise
306 304 log.warn("Failed to build optional extension '%s' (skipping)",
307 305 ext.name)
308 306
309 307 class hgbuildpy(build_py):
310 308 if convert2to3:
311 309 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
312 310 getfixers("hgfixes")))
313 311
314 312 def finalize_options(self):
315 313 build_py.finalize_options(self)
316 314
317 315 if self.distribution.pure:
318 316 if self.py_modules is None:
319 317 self.py_modules = []
320 318 for ext in self.distribution.ext_modules:
321 319 if ext.name.startswith("mercurial."):
322 320 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
323 321 self.distribution.ext_modules = []
324 322 else:
325 323 h = os.path.join(get_python_inc(), 'Python.h')
326 324 if not os.path.exists(h):
327 325 raise SystemExit('Python headers are required to build '
328 326 'Mercurial but weren\'t found in %s' % h)
329 327
330 328 def find_modules(self):
331 329 modules = build_py.find_modules(self)
332 330 for module in modules:
333 331 if module[0] == "mercurial.pure":
334 332 if module[1] != "__init__":
335 333 yield ("mercurial", module[1], module[2])
336 334 else:
337 335 yield module
338 336
339 337 class buildhgextindex(Command):
340 338 description = 'generate prebuilt index of hgext (for frozen package)'
341 339 user_options = []
342 340 _indexfilename = 'hgext/__index__.py'
343 341
344 342 def initialize_options(self):
345 343 pass
346 344
347 345 def finalize_options(self):
348 346 pass
349 347
350 348 def run(self):
351 349 if os.path.exists(self._indexfilename):
352 350 f = open(self._indexfilename, 'w')
353 351 f.write('# empty\n')
354 352 f.close()
355 353
356 354 # here no extension enabled, disabled() lists up everything
357 355 code = ('import pprint; from mercurial import extensions; '
358 356 'pprint.pprint(extensions.disabled())')
359 357 out, err = runcmd([sys.executable, '-c', code], env)
360 358 if err:
361 359 raise DistutilsExecError(err)
362 360
363 361 f = open(self._indexfilename, 'w')
364 362 f.write('# this file is autogenerated by setup.py\n')
365 363 f.write('docs = ')
366 364 f.write(out)
367 365 f.close()
368 366
369 367 class buildhgexe(build_ext):
370 368 description = 'compile hg.exe from mercurial/exewrapper.c'
371 369
372 370 def build_extensions(self):
373 371 if os.name != 'nt':
374 372 return
375 373 if isinstance(self.compiler, HackedMingw32CCompiler):
376 374 self.compiler.compiler_so = self.compiler.compiler # no -mdll
377 375 self.compiler.dll_libraries = [] # no -lmsrvc90
378 376 hv = sys.hexversion
379 377 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
380 378 f = open('mercurial/hgpythonlib.h', 'wb')
381 379 f.write('/* this file is autogenerated by setup.py */\n')
382 380 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
383 381 f.close()
384 382 objects = self.compiler.compile(['mercurial/exewrapper.c'],
385 383 output_dir=self.build_temp)
386 384 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
387 385 target = os.path.join(dir, 'hg')
388 386 self.compiler.link_executable(objects, target,
389 387 libraries=[],
390 388 output_dir=self.build_temp)
391 389
392 390 class hginstalllib(install_lib):
393 391 '''
394 392 This is a specialization of install_lib that replaces the copy_file used
395 393 there so that it supports setting the mode of files after copying them,
396 394 instead of just preserving the mode that the files originally had. If your
397 395 system has a umask of something like 027, preserving the permissions when
398 396 copying will lead to a broken install.
399 397
400 398 Note that just passing keep_permissions=False to copy_file would be
401 399 insufficient, as it might still be applying a umask.
402 400 '''
403 401
404 402 def run(self):
405 403 realcopyfile = file_util.copy_file
406 404 def copyfileandsetmode(*args, **kwargs):
407 405 src, dst = args[0], args[1]
408 406 dst, copied = realcopyfile(*args, **kwargs)
409 407 if copied:
410 408 st = os.stat(src)
411 409 # Persist executable bit (apply it to group and other if user
412 410 # has it)
413 411 if st[stat.ST_MODE] & stat.S_IXUSR:
414 412 setmode = int('0755', 8)
415 413 else:
416 414 setmode = int('0644', 8)
417 415 m = stat.S_IMODE(st[stat.ST_MODE])
418 416 m = (m & ~int('0777', 8)) | setmode
419 417 os.chmod(dst, m)
420 418 file_util.copy_file = copyfileandsetmode
421 419 try:
422 420 install_lib.run(self)
423 421 finally:
424 422 file_util.copy_file = realcopyfile
425 423
426 424 class hginstallscripts(install_scripts):
427 425 '''
428 426 This is a specialization of install_scripts that replaces the @LIBDIR@ with
429 427 the configured directory for modules. If possible, the path is made relative
430 428 to the directory for scripts.
431 429 '''
432 430
433 431 def initialize_options(self):
434 432 install_scripts.initialize_options(self)
435 433
436 434 self.install_lib = None
437 435
438 436 def finalize_options(self):
439 437 install_scripts.finalize_options(self)
440 438 self.set_undefined_options('install',
441 439 ('install_lib', 'install_lib'))
442 440
443 441 def run(self):
444 442 install_scripts.run(self)
445 443
446 444 if (os.path.splitdrive(self.install_dir)[0] !=
447 445 os.path.splitdrive(self.install_lib)[0]):
448 446 # can't make relative paths from one drive to another, so use an
449 447 # absolute path instead
450 448 libdir = self.install_lib
451 449 else:
452 450 common = os.path.commonprefix((self.install_dir, self.install_lib))
453 451 rest = self.install_dir[len(common):]
454 452 uplevel = len([n for n in os.path.split(rest) if n])
455 453
456 454 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
457 455
458 456 for outfile in self.outfiles:
459 457 fp = open(outfile, 'rb')
460 458 data = fp.read()
461 459 fp.close()
462 460
463 461 # skip binary files
464 462 if b('\0') in data:
465 463 continue
466 464
467 465 data = data.replace(b('@LIBDIR@'), libdir.encode(libdir_escape))
468 466 fp = open(outfile, 'wb')
469 467 fp.write(data)
470 468 fp.close()
471 469
472 470 cmdclass = {'build': hgbuild,
473 471 'build_mo': hgbuildmo,
474 472 'build_ext': hgbuildext,
475 473 'build_py': hgbuildpy,
476 474 'build_hgextindex': buildhgextindex,
477 475 'install_lib': hginstalllib,
478 476 'install_scripts': hginstallscripts,
479 477 'build_hgexe': buildhgexe,
480 478 }
481 479
482 480 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
483 481 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
484 482 'hgext.largefiles']
485 483
486 484 common_depends = ['mercurial/util.h']
487 485
488 486 osutil_ldflags = []
489 487
490 488 if sys.platform == 'darwin':
491 489 osutil_ldflags += ['-framework', 'ApplicationServices']
492 490
493 491 extmodules = [
494 492 Extension('mercurial.base85', ['mercurial/base85.c'],
495 493 depends=common_depends),
496 494 Extension('mercurial.bdiff', ['mercurial/bdiff.c'],
497 495 depends=common_depends),
498 496 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
499 497 depends=common_depends),
500 498 Extension('mercurial.mpatch', ['mercurial/mpatch.c'],
501 499 depends=common_depends),
502 500 Extension('mercurial.parsers', ['mercurial/dirs.c',
503 501 'mercurial/manifest.c',
504 502 'mercurial/parsers.c',
505 503 'mercurial/pathencode.c'],
506 504 depends=common_depends),
507 505 Extension('mercurial.osutil', ['mercurial/osutil.c'],
508 506 extra_link_args=osutil_ldflags,
509 507 depends=common_depends),
510 508 ]
511 509
512 510 try:
513 511 from distutils import cygwinccompiler
514 512
515 513 # the -mno-cygwin option has been deprecated for years
516 514 compiler = cygwinccompiler.Mingw32CCompiler
517 515
518 516 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
519 517 def __init__(self, *args, **kwargs):
520 518 compiler.__init__(self, *args, **kwargs)
521 519 for i in 'compiler compiler_so linker_exe linker_so'.split():
522 520 try:
523 521 getattr(self, i).remove('-mno-cygwin')
524 522 except ValueError:
525 523 pass
526 524
527 525 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
528 526 except ImportError:
529 527 # the cygwinccompiler package is not available on some Python
530 528 # distributions like the ones from the optware project for Synology
531 529 # DiskStation boxes
532 530 class HackedMingw32CCompiler(object):
533 531 pass
534 532
535 533 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
536 534 'help/*.txt',
537 535 'default.d/*.rc',
538 536 'dummycert.pem']}
539 537
540 538 def ordinarypath(p):
541 539 return p and p[0] != '.' and p[-1] != '~'
542 540
543 541 for root in ('templates',):
544 542 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
545 543 curdir = curdir.split(os.sep, 1)[1]
546 544 dirs[:] = filter(ordinarypath, dirs)
547 545 for f in filter(ordinarypath, files):
548 546 f = os.path.join(curdir, f)
549 547 packagedata['mercurial'].append(f)
550 548
551 549 datafiles = []
552 550 setupversion = version
553 551 extra = {}
554 552
555 553 if py2exeloaded:
556 554 extra['console'] = [
557 555 {'script':'hg',
558 556 'copyright':'Copyright (C) 2005-2015 Matt Mackall and others',
559 557 'product_version':version}]
560 558 # sub command of 'build' because 'py2exe' does not handle sub_commands
561 559 build.sub_commands.insert(0, ('build_hgextindex', None))
562 560 # put dlls in sub directory so that they won't pollute PATH
563 561 extra['zipfile'] = 'lib/library.zip'
564 562
565 563 if os.name == 'nt':
566 564 # Windows binary file versions for exe/dll files must have the
567 565 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
568 566 setupversion = version.split('+', 1)[0]
569 567
570 568 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
571 569 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
572 570 if version:
573 571 version = version[0]
574 572 if sys.version_info[0] == 3:
575 573 version = version.decode('utf-8')
576 574 xcode4 = (version.startswith('Xcode') and
577 575 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
578 576 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
579 577 else:
580 578 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
581 579 # installed, but instead with only command-line tools. Assume
582 580 # that only happens on >= Lion, thus no PPC support.
583 581 xcode4 = True
584 582 xcode51 = False
585 583
586 584 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
587 585 # distutils.sysconfig
588 586 if xcode4:
589 587 os.environ['ARCHFLAGS'] = ''
590 588
591 589 # XCode 5.1 changes clang such that it now fails to compile if the
592 590 # -mno-fused-madd flag is passed, but the version of Python shipped with
593 591 # OS X 10.9 Mavericks includes this flag. This causes problems in all
594 592 # C extension modules, and a bug has been filed upstream at
595 593 # http://bugs.python.org/issue21244. We also need to patch this here
596 594 # so Mercurial can continue to compile in the meantime.
597 595 if xcode51:
598 596 cflags = get_config_var('CFLAGS')
599 597 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
600 598 os.environ['CFLAGS'] = (
601 599 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
602 600
603 601 setup(name='mercurial',
604 602 version=setupversion,
605 603 author='Matt Mackall and many others',
606 604 author_email='mercurial@selenic.com',
607 605 url='https://mercurial-scm.org/',
608 606 download_url='https://mercurial-scm.org/release/',
609 607 description=('Fast scalable distributed SCM (revision control, version '
610 608 'control) system'),
611 609 long_description=('Mercurial is a distributed SCM tool written in Python.'
612 610 ' It is used by a number of large projects that require'
613 611 ' fast, reliable distributed revision control, such as '
614 612 'Mozilla.'),
615 613 license='GNU GPLv2 or any later version',
616 614 classifiers=[
617 615 'Development Status :: 6 - Mature',
618 616 'Environment :: Console',
619 617 'Intended Audience :: Developers',
620 618 'Intended Audience :: System Administrators',
621 619 'License :: OSI Approved :: GNU General Public License (GPL)',
622 620 'Natural Language :: Danish',
623 621 'Natural Language :: English',
624 622 'Natural Language :: German',
625 623 'Natural Language :: Italian',
626 624 'Natural Language :: Japanese',
627 625 'Natural Language :: Portuguese (Brazilian)',
628 626 'Operating System :: Microsoft :: Windows',
629 627 'Operating System :: OS Independent',
630 628 'Operating System :: POSIX',
631 629 'Programming Language :: C',
632 630 'Programming Language :: Python',
633 631 'Topic :: Software Development :: Version Control',
634 632 ],
635 633 scripts=scripts,
636 634 packages=packages,
637 635 ext_modules=extmodules,
638 636 data_files=datafiles,
639 637 package_data=packagedata,
640 638 cmdclass=cmdclass,
641 639 distclass=hgdist,
642 640 options={'py2exe': {'packages': ['hgext', 'email']},
643 641 'bdist_mpkg': {'zipdist': False,
644 642 'license': 'COPYING',
645 643 'readme': 'contrib/macosx/Readme.html',
646 644 'welcome': 'contrib/macosx/Welcome.html',
647 645 },
648 646 },
649 647 **extra)
General Comments 0
You need to be logged in to leave comments. Login now