##// END OF EJS Templates
setup: set whole env for running hg even if .hg doesn't exist (issue3073)...
Yuya Nishihara -
r15388:eb6c0d47 stable
parent child Browse files
Show More
@@ -1,442 +1,443 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, platform
8 8 if getattr(sys, 'version_info', (0, 0, 0)) < (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 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
40 40 isironpython = False
41 41 try:
42 42 isironpython = platform.python_implementation().lower().find("ironpython") != -1
43 43 except:
44 44 pass
45 45
46 46 if isironpython:
47 47 print "warning: IronPython detected (no bz2 support)"
48 48 else:
49 49 try:
50 50 import bz2
51 51 except:
52 52 raise SystemExit(
53 53 "Couldn't import standard bz2 (incomplete Python install).")
54 54
55 55 import os, subprocess, time
56 56 import shutil
57 57 import tempfile
58 58 from distutils import log
59 59 from distutils.core import setup, Command, Extension
60 60 from distutils.dist import Distribution
61 61 from distutils.command.build import build
62 62 from distutils.command.build_ext import build_ext
63 63 from distutils.command.build_py import build_py
64 64 from distutils.command.install_scripts import install_scripts
65 65 from distutils.spawn import spawn, find_executable
66 66 from distutils.ccompiler import new_compiler
67 67 from distutils.errors import CCompilerError, DistutilsExecError
68 68 from distutils.sysconfig import get_python_inc
69 69 from distutils.version import StrictVersion
70 70
71 71 scripts = ['hg']
72 72 if os.name == 'nt':
73 73 scripts.append('contrib/win32/hg.bat')
74 74
75 75 # simplified version of distutils.ccompiler.CCompiler.has_function
76 76 # that actually removes its temporary files.
77 77 def hasfunction(cc, funcname):
78 78 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
79 79 devnull = oldstderr = None
80 80 try:
81 81 try:
82 82 fname = os.path.join(tmpdir, 'funcname.c')
83 83 f = open(fname, 'w')
84 84 f.write('int main(void) {\n')
85 85 f.write(' %s();\n' % funcname)
86 86 f.write('}\n')
87 87 f.close()
88 88 # Redirect stderr to /dev/null to hide any error messages
89 89 # from the compiler.
90 90 # This will have to be changed if we ever have to check
91 91 # for a function on Windows.
92 92 devnull = open('/dev/null', 'w')
93 93 oldstderr = os.dup(sys.stderr.fileno())
94 94 os.dup2(devnull.fileno(), sys.stderr.fileno())
95 95 objects = cc.compile([fname], output_dir=tmpdir)
96 96 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
97 97 except:
98 98 return False
99 99 return True
100 100 finally:
101 101 if oldstderr is not None:
102 102 os.dup2(oldstderr, sys.stderr.fileno())
103 103 if devnull is not None:
104 104 devnull.close()
105 105 shutil.rmtree(tmpdir)
106 106
107 107 # py2exe needs to be installed to work
108 108 try:
109 109 import py2exe
110 110 py2exeloaded = True
111 111 except ImportError:
112 112 py2exeloaded = False
113 113
114 114 def runcmd(cmd, env):
115 115 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
116 116 stderr=subprocess.PIPE, env=env)
117 117 out, err = p.communicate()
118 118 return out, err
119 119
120 120 def runhg(cmd, env):
121 121 out, err = runcmd(cmd, env)
122 122 # If root is executing setup.py, but the repository is owned by
123 123 # another user (as in "sudo python setup.py install") we will get
124 124 # trust warnings since the .hg/hgrc file is untrusted. That is
125 125 # fine, we don't want to load it anyway. Python may warn about
126 126 # a missing __init__.py in mercurial/locale, we also ignore that.
127 127 err = [e for e in err.splitlines()
128 128 if not e.startswith(b('Not trusting file')) \
129 129 and not e.startswith(b('warning: Not importing'))]
130 130 if err:
131 131 return ''
132 132 return out
133 133
134 134 version = ''
135 135
136 env = {'HGRCPATH': '', 'LANGUAGE': 'C'}
136 # Execute hg out of this directory with a custom environment which
137 # includes the pure Python modules in mercurial/pure. We also take
138 # care to not use any hgrc files and do no localization.
139 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
140 env = {'PYTHONPATH': os.pathsep.join(pypath),
141 'HGRCPATH': '',
142 'LANGUAGE': 'C'}
143 if 'LD_LIBRARY_PATH' in os.environ:
144 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
145 if 'SystemRoot' in os.environ:
146 # Copy SystemRoot into the custom environment for Python 2.6
147 # under Windows. Otherwise, the subprocess will fail with
148 # error 0xc0150004. See: http://bugs.python.org/issue3440
149 env['SystemRoot'] = os.environ['SystemRoot']
137 150
138 151 if os.path.isdir('.hg'):
139 # Execute hg out of this directory with a custom environment which
140 # includes the pure Python modules in mercurial/pure. We also take
141 # care to not use any hgrc files and do no localization.
142 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
143 env['PYTHONPATH'] = os.pathsep.join(pypath)
144 if 'LD_LIBRARY_PATH' in os.environ:
145 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
146 if 'SystemRoot' in os.environ:
147 # Copy SystemRoot into the custom environment for Python 2.6
148 # under Windows. Otherwise, the subprocess will fail with
149 # error 0xc0150004. See: http://bugs.python.org/issue3440
150 env['SystemRoot'] = os.environ['SystemRoot']
151 152 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
152 153 l = runhg(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 = runhg(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
219 220 # Insert hgbuildmo first so that files in mercurial/locale/ are found
220 221 # when build_py is run next.
221 222 build.sub_commands.insert(0, ('build_mo', None))
222 223
223 224 Distribution.pure = 0
224 225 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
225 226 "code instead of C extensions"))
226 227
227 228 class hgbuildext(build_ext):
228 229
229 230 def build_extension(self, ext):
230 231 try:
231 232 build_ext.build_extension(self, ext)
232 233 except CCompilerError:
233 234 if not getattr(ext, 'optional', False):
234 235 raise
235 236 log.warn("Failed to build optional extension '%s' (skipping)",
236 237 ext.name)
237 238
238 239 class hgbuildpy(build_py):
239 240
240 241 def finalize_options(self):
241 242 build_py.finalize_options(self)
242 243
243 244 if self.distribution.pure:
244 245 if self.py_modules is None:
245 246 self.py_modules = []
246 247 for ext in self.distribution.ext_modules:
247 248 if ext.name.startswith("mercurial."):
248 249 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
249 250 self.distribution.ext_modules = []
250 251 else:
251 252 if not os.path.exists(os.path.join(get_python_inc(), 'Python.h')):
252 253 raise SystemExit("Python headers are required to build Mercurial")
253 254
254 255 def find_modules(self):
255 256 modules = build_py.find_modules(self)
256 257 for module in modules:
257 258 if module[0] == "mercurial.pure":
258 259 if module[1] != "__init__":
259 260 yield ("mercurial", module[1], module[2])
260 261 else:
261 262 yield module
262 263
263 264 class buildhgextindex(Command):
264 265 description = 'generate prebuilt index of hgext (for frozen package)'
265 266 user_options = []
266 267 _indexfilename = 'hgext/__index__.py'
267 268
268 269 def initialize_options(self):
269 270 pass
270 271
271 272 def finalize_options(self):
272 273 pass
273 274
274 275 def run(self):
275 276 if os.path.exists(self._indexfilename):
276 277 os.unlink(self._indexfilename)
277 278
278 279 # here no extension enabled, disabled() lists up everything
279 280 code = ('import pprint; from mercurial import extensions; '
280 281 'pprint.pprint(extensions.disabled())')
281 282 out, err = runcmd([sys.executable, '-c', code], env)
282 283 if err:
283 284 raise DistutilsExecError(err)
284 285
285 286 f = open(self._indexfilename, 'w')
286 287 f.write('# this file is autogenerated by setup.py\n')
287 288 f.write('docs = ')
288 289 f.write(out)
289 290 f.close()
290 291
291 292 class hginstallscripts(install_scripts):
292 293 '''
293 294 This is a specialization of install_scripts that replaces the @LIBDIR@ with
294 295 the configured directory for modules. If possible, the path is made relative
295 296 to the directory for scripts.
296 297 '''
297 298
298 299 def initialize_options(self):
299 300 install_scripts.initialize_options(self)
300 301
301 302 self.install_lib = None
302 303
303 304 def finalize_options(self):
304 305 install_scripts.finalize_options(self)
305 306 self.set_undefined_options('install',
306 307 ('install_lib', 'install_lib'))
307 308
308 309 def run(self):
309 310 install_scripts.run(self)
310 311
311 312 if (os.path.splitdrive(self.install_dir)[0] !=
312 313 os.path.splitdrive(self.install_lib)[0]):
313 314 # can't make relative paths from one drive to another, so use an
314 315 # absolute path instead
315 316 libdir = self.install_lib
316 317 else:
317 318 common = os.path.commonprefix((self.install_dir, self.install_lib))
318 319 rest = self.install_dir[len(common):]
319 320 uplevel = len([n for n in os.path.split(rest) if n])
320 321
321 322 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
322 323
323 324 for outfile in self.outfiles:
324 325 fp = open(outfile, 'rb')
325 326 data = fp.read()
326 327 fp.close()
327 328
328 329 # skip binary files
329 330 if '\0' in data:
330 331 continue
331 332
332 333 data = data.replace('@LIBDIR@', libdir.encode('string_escape'))
333 334 fp = open(outfile, 'wb')
334 335 fp.write(data)
335 336 fp.close()
336 337
337 338 cmdclass = {'build_mo': hgbuildmo,
338 339 'build_ext': hgbuildext,
339 340 'build_py': hgbuildpy,
340 341 'build_hgextindex': buildhgextindex,
341 342 'install_scripts': hginstallscripts}
342 343
343 344 packages = ['mercurial', 'mercurial.hgweb',
344 345 'mercurial.httpclient', 'mercurial.httpclient.tests',
345 346 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
346 347 'hgext.largefiles']
347 348
348 349 pymodules = []
349 350
350 351 extmodules = [
351 352 Extension('mercurial.base85', ['mercurial/base85.c']),
352 353 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
353 354 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
354 355 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
355 356 Extension('mercurial.parsers', ['mercurial/parsers.c']),
356 357 ]
357 358
358 359 osutil_ldflags = []
359 360
360 361 if sys.platform == 'darwin':
361 362 osutil_ldflags += ['-framework', 'ApplicationServices']
362 363
363 364 # disable osutil.c under windows + python 2.4 (issue1364)
364 365 if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
365 366 pymodules.append('mercurial.pure.osutil')
366 367 else:
367 368 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
368 369 extra_link_args=osutil_ldflags))
369 370
370 371 if sys.platform.startswith('linux') and os.uname()[2] > '2.6':
371 372 # The inotify extension is only usable with Linux 2.6 kernels.
372 373 # You also need a reasonably recent C library.
373 374 # In any case, if it fails to build the error will be skipped ('optional').
374 375 cc = new_compiler()
375 376 if hasfunction(cc, 'inotify_add_watch'):
376 377 inotify = Extension('hgext.inotify.linux._inotify',
377 378 ['hgext/inotify/linux/_inotify.c'],
378 379 ['mercurial'])
379 380 inotify.optional = True
380 381 extmodules.append(inotify)
381 382 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
382 383
383 384 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
384 385 'help/*.txt']}
385 386
386 387 def ordinarypath(p):
387 388 return p and p[0] != '.' and p[-1] != '~'
388 389
389 390 for root in ('templates',):
390 391 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
391 392 curdir = curdir.split(os.sep, 1)[1]
392 393 dirs[:] = filter(ordinarypath, dirs)
393 394 for f in filter(ordinarypath, files):
394 395 f = os.path.join(curdir, f)
395 396 packagedata['mercurial'].append(f)
396 397
397 398 datafiles = []
398 399 setupversion = version
399 400 extra = {}
400 401
401 402 if py2exeloaded:
402 403 extra['console'] = [
403 404 {'script':'hg',
404 405 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
405 406 'product_version':version}]
406 407 # sub command of 'build' because 'py2exe' does not handle sub_commands
407 408 build.sub_commands.insert(0, ('build_hgextindex', None))
408 409
409 410 if os.name == 'nt':
410 411 # Windows binary file versions for exe/dll files must have the
411 412 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
412 413 setupversion = version.split('+', 1)[0]
413 414
414 415 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
415 416 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
416 417 # distutils.sysconfig
417 418 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()[0]
418 419 # Also parse only first digit, because 3.2.1 can't be parsed nicely
419 420 if (version.startswith('Xcode') and
420 421 StrictVersion(version.split()[1]) >= StrictVersion('4.0')):
421 422 os.environ['ARCHFLAGS'] = ''
422 423
423 424 setup(name='mercurial',
424 425 version=setupversion,
425 426 author='Matt Mackall',
426 427 author_email='mpm@selenic.com',
427 428 url='http://mercurial.selenic.com/',
428 429 description='Scalable distributed SCM',
429 430 license='GNU GPLv2+',
430 431 scripts=scripts,
431 432 packages=packages,
432 433 py_modules=pymodules,
433 434 ext_modules=extmodules,
434 435 data_files=datafiles,
435 436 package_data=packagedata,
436 437 cmdclass=cmdclass,
437 438 options=dict(py2exe=dict(packages=['hgext', 'email']),
438 439 bdist_mpkg=dict(zipdist=True,
439 440 license='COPYING',
440 441 readme='contrib/macosx/Readme.html',
441 442 welcome='contrib/macosx/Welcome.html')),
442 443 **extra)
General Comments 0
You need to be logged in to leave comments. Login now