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