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