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