##// END OF EJS Templates
setup: user-friendly error message if Python headers are missing
Nicolas Dumazet -
r12649:6c0e1aee default
parent child Browse files
Show More
@@ -1,343 +1,347
1 1 #!/usr/bin/env python
2 2 #
3 3 # This is the mercurial setup script.
4 4 #
5 5 # 'python setup.py install', or
6 6 # 'python setup.py --help' for more options
7 7
8 8 import sys
9 9 if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
10 10 raise SystemExit("Mercurial requires Python 2.4 or later.")
11 11
12 12 if sys.version_info[0] >= 3:
13 13 def b(s):
14 14 '''A helper function to emulate 2.6+ bytes literals using string
15 15 literals.'''
16 16 return s.encode('latin1')
17 17 else:
18 18 def b(s):
19 19 '''A helper function to emulate 2.6+ bytes literals using string
20 20 literals.'''
21 21 return s
22 22
23 23 # Solaris Python packaging brain damage
24 24 try:
25 25 import hashlib
26 26 sha = hashlib.sha1()
27 27 except:
28 28 try:
29 29 import sha
30 30 except:
31 31 raise SystemExit(
32 32 "Couldn't import standard hashlib (incomplete Python install).")
33 33
34 34 try:
35 35 import zlib
36 36 except:
37 37 raise SystemExit(
38 38 "Couldn't import standard zlib (incomplete Python install).")
39 39
40 40 try:
41 41 import bz2
42 42 except:
43 43 raise SystemExit(
44 44 "Couldn't import standard bz2 (incomplete Python install).")
45 45
46 46 import os, subprocess, time
47 47 import shutil
48 48 import tempfile
49 49 from distutils import log
50 50 from distutils.core import setup, Extension
51 51 from distutils.dist import Distribution
52 52 from distutils.command.build import build
53 53 from distutils.command.build_ext import build_ext
54 54 from distutils.command.build_py import build_py
55 55 from distutils.spawn import spawn, find_executable
56 56 from distutils.ccompiler import new_compiler
57 57 from distutils.errors import CCompilerError
58 from distutils.sysconfig import get_python_inc
58 59
59 60 scripts = ['hg']
60 61 if os.name == 'nt':
61 62 scripts.append('contrib/win32/hg.bat')
62 63
63 64 # simplified version of distutils.ccompiler.CCompiler.has_function
64 65 # that actually removes its temporary files.
65 66 def hasfunction(cc, funcname):
66 67 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
67 68 devnull = oldstderr = None
68 69 try:
69 70 try:
70 71 fname = os.path.join(tmpdir, 'funcname.c')
71 72 f = open(fname, 'w')
72 73 f.write('int main(void) {\n')
73 74 f.write(' %s();\n' % funcname)
74 75 f.write('}\n')
75 76 f.close()
76 77 # Redirect stderr to /dev/null to hide any error messages
77 78 # from the compiler.
78 79 # This will have to be changed if we ever have to check
79 80 # for a function on Windows.
80 81 devnull = open('/dev/null', 'w')
81 82 oldstderr = os.dup(sys.stderr.fileno())
82 83 os.dup2(devnull.fileno(), sys.stderr.fileno())
83 84 objects = cc.compile([fname], output_dir=tmpdir)
84 85 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
85 86 except:
86 87 return False
87 88 return True
88 89 finally:
89 90 if oldstderr is not None:
90 91 os.dup2(oldstderr, sys.stderr.fileno())
91 92 if devnull is not None:
92 93 devnull.close()
93 94 shutil.rmtree(tmpdir)
94 95
95 96 # py2exe needs to be installed to work
96 97 try:
97 98 import py2exe
98 99 py2exeloaded = True
99 100
100 101 # Help py2exe to find win32com.shell
101 102 try:
102 103 import modulefinder
103 104 import win32com
104 105 for p in win32com.__path__[1:]: # Take the path to win32comext
105 106 modulefinder.AddPackagePath("win32com", p)
106 107 pn = "win32com.shell"
107 108 __import__(pn)
108 109 m = sys.modules[pn]
109 110 for p in m.__path__[1:]:
110 111 modulefinder.AddPackagePath(pn, p)
111 112 except ImportError:
112 113 pass
113 114
114 115 except ImportError:
115 116 py2exeloaded = False
116 117 pass
117 118
118 119 def runcmd(cmd, env):
119 120 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
120 121 stderr=subprocess.PIPE, env=env)
121 122 out, err = p.communicate()
122 123 # If root is executing setup.py, but the repository is owned by
123 124 # another user (as in "sudo python setup.py install") we will get
124 125 # trust warnings since the .hg/hgrc file is untrusted. That is
125 126 # fine, we don't want to load it anyway. Python may warn about
126 127 # a missing __init__.py in mercurial/locale, we also ignore that.
127 128 err = [e for e in err.splitlines()
128 129 if not e.startswith(b('Not trusting file')) \
129 130 and not e.startswith(b('warning: Not importing'))]
130 131 if err:
131 132 return ''
132 133 return out
133 134
134 135 version = ''
135 136
136 137 if os.path.isdir('.hg'):
137 138 # Execute hg out of this directory with a custom environment which
138 139 # includes the pure Python modules in mercurial/pure. We also take
139 140 # care to not use any hgrc files and do no localization.
140 141 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
141 142 env = {'PYTHONPATH': os.pathsep.join(pypath),
142 143 'HGRCPATH': '',
143 144 'LANGUAGE': 'C'}
144 145 if 'LD_LIBRARY_PATH' in os.environ:
145 146 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
146 147 if 'SystemRoot' in os.environ:
147 148 # Copy SystemRoot into the custom environment for Python 2.6
148 149 # under Windows. Otherwise, the subprocess will fail with
149 150 # error 0xc0150004. See: http://bugs.python.org/issue3440
150 151 env['SystemRoot'] = os.environ['SystemRoot']
151 152 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
152 153 l = runcmd(cmd, env).split()
153 154 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
154 155 l.pop()
155 156 if len(l) > 1: # tag found
156 157 version = l[-1]
157 158 if l[0].endswith('+'): # propagate the dirty status to the tag
158 159 version += '+'
159 160 elif len(l) == 1: # no tag found
160 161 cmd = [sys.executable, 'hg', 'parents', '--template',
161 162 '{latesttag}+{latesttagdistance}-']
162 163 version = runcmd(cmd, env) + l[0]
163 164 if version.endswith('+'):
164 165 version += time.strftime('%Y%m%d')
165 166 elif os.path.exists('.hg_archival.txt'):
166 167 kw = dict([[t.strip() for t in l.split(':', 1)]
167 168 for l in open('.hg_archival.txt')])
168 169 if 'tag' in kw:
169 170 version = kw['tag']
170 171 elif 'latesttag' in kw:
171 172 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
172 173 else:
173 174 version = kw.get('node', '')[:12]
174 175
175 176 if version:
176 177 f = open("mercurial/__version__.py", "w")
177 178 f.write('# this file is autogenerated by setup.py\n')
178 179 f.write('version = "%s"\n' % version)
179 180 f.close()
180 181
181 182
182 183 try:
183 184 from mercurial import __version__
184 185 version = __version__.version
185 186 except ImportError:
186 187 version = 'unknown'
187 188
188 189 class hgbuildmo(build):
189 190
190 191 description = "build translations (.mo files)"
191 192
192 193 def run(self):
193 194 if not find_executable('msgfmt'):
194 195 self.warn("could not find msgfmt executable, no translations "
195 196 "will be built")
196 197 return
197 198
198 199 podir = 'i18n'
199 200 if not os.path.isdir(podir):
200 201 self.warn("could not find %s/ directory" % podir)
201 202 return
202 203
203 204 join = os.path.join
204 205 for po in os.listdir(podir):
205 206 if not po.endswith('.po'):
206 207 continue
207 208 pofile = join(podir, po)
208 209 modir = join('locale', po[:-3], 'LC_MESSAGES')
209 210 mofile = join(modir, 'hg.mo')
210 211 mobuildfile = join('mercurial', mofile)
211 212 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
212 213 if sys.platform != 'sunos5':
213 214 # msgfmt on Solaris does not know about -c
214 215 cmd.append('-c')
215 216 self.mkpath(join('mercurial', modir))
216 217 self.make_file([pofile], mobuildfile, spawn, (cmd,))
217 218
218 219 # Insert hgbuildmo first so that files in mercurial/locale/ are found
219 220 # when build_py is run next.
220 221 build.sub_commands.insert(0, ('build_mo', None))
221 222
222 223 Distribution.pure = 0
223 224 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
224 225 "code instead of C extensions"))
225 226
226 227 class hgbuildext(build_ext):
227 228
228 229 def build_extension(self, ext):
229 230 try:
230 231 build_ext.build_extension(self, ext)
231 232 except CCompilerError:
232 233 if not getattr(ext, 'optional', False):
233 234 raise
234 235 log.warn("Failed to build optional extension '%s' (skipping)",
235 236 ext.name)
236 237
237 238 class hgbuildpy(build_py):
238 239
239 240 def finalize_options(self):
240 241 build_py.finalize_options(self)
241 242
242 243 if self.distribution.pure:
243 244 if self.py_modules is None:
244 245 self.py_modules = []
245 246 for ext in self.distribution.ext_modules:
246 247 if ext.name.startswith("mercurial."):
247 248 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
248 249 self.distribution.ext_modules = []
250 else:
251 if not os.path.exists(os.path.join(get_python_inc(), 'Python.h')):
252 raise SystemExit("Python headers are required to build Mercurial")
249 253
250 254 def find_modules(self):
251 255 modules = build_py.find_modules(self)
252 256 for module in modules:
253 257 if module[0] == "mercurial.pure":
254 258 if module[1] != "__init__":
255 259 yield ("mercurial", module[1], module[2])
256 260 else:
257 261 yield module
258 262
259 263 cmdclass = {'build_mo': hgbuildmo,
260 264 'build_ext': hgbuildext,
261 265 'build_py': hgbuildpy}
262 266
263 267 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
264 268 'hgext.highlight', 'hgext.zeroconf']
265 269
266 270 pymodules = []
267 271
268 272 extmodules = [
269 273 Extension('mercurial.base85', ['mercurial/base85.c']),
270 274 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
271 275 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
272 276 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
273 277 Extension('mercurial.parsers', ['mercurial/parsers.c']),
274 278 ]
275 279
276 280 # disable osutil.c under windows + python 2.4 (issue1364)
277 281 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
278 282 pymodules.append('mercurial.pure.osutil')
279 283 else:
280 284 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
281 285
282 286 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
283 287 # The inotify extension is only usable with Linux 2.6 kernels.
284 288 # You also need a reasonably recent C library.
285 289 # In any case, if it fails to build the error will be skipped ('optional').
286 290 cc = new_compiler()
287 291 if hasfunction(cc, 'inotify_add_watch'):
288 292 inotify = Extension('hgext.inotify.linux._inotify',
289 293 ['hgext/inotify/linux/_inotify.c'],
290 294 ['mercurial'])
291 295 inotify.optional = True
292 296 extmodules.append(inotify)
293 297 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
294 298
295 299 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
296 300 'help/*.txt']}
297 301
298 302 def ordinarypath(p):
299 303 return p and p[0] != '.' and p[-1] != '~'
300 304
301 305 for root in ('templates',):
302 306 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
303 307 curdir = curdir.split(os.sep, 1)[1]
304 308 dirs[:] = filter(ordinarypath, dirs)
305 309 for f in filter(ordinarypath, files):
306 310 f = os.path.join(curdir, f)
307 311 packagedata['mercurial'].append(f)
308 312
309 313 datafiles = []
310 314 setupversion = version
311 315 extra = {}
312 316
313 317 if py2exeloaded:
314 318 extra['console'] = [
315 319 {'script':'hg',
316 320 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
317 321 'product_version':version}]
318 322
319 323 if os.name == 'nt':
320 324 # Windows binary file versions for exe/dll files must have the
321 325 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
322 326 setupversion = version.split('+', 1)[0]
323 327
324 328 setup(name='mercurial',
325 329 version=setupversion,
326 330 author='Matt Mackall',
327 331 author_email='mpm@selenic.com',
328 332 url='http://mercurial.selenic.com/',
329 333 description='Scalable distributed SCM',
330 334 license='GNU GPLv2+',
331 335 scripts=scripts,
332 336 packages=packages,
333 337 py_modules=pymodules,
334 338 ext_modules=extmodules,
335 339 data_files=datafiles,
336 340 package_data=packagedata,
337 341 cmdclass=cmdclass,
338 342 options=dict(py2exe=dict(packages=['hgext', 'email']),
339 343 bdist_mpkg=dict(zipdist=True,
340 344 license='COPYING',
341 345 readme='contrib/macosx/Readme.html',
342 346 welcome='contrib/macosx/Welcome.html')),
343 347 **extra)
General Comments 0
You need to be logged in to leave comments. Login now