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