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