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