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