##// END OF EJS Templates
setup: integrate osutil C extension into extmodules initialization
Adrian Buehlmann -
r25074:0021ad4c default
parent child Browse files
Show More
@@ -1,648 +1,647 b''
1 #
1 #
2 # This is the mercurial setup script.
2 # This is the mercurial setup script.
3 #
3 #
4 # 'python setup.py install', or
4 # 'python setup.py install', or
5 # 'python setup.py --help' for more options
5 # 'python setup.py --help' for more options
6
6
7 import sys, platform
7 import sys, platform
8 if getattr(sys, 'version_info', (0, 0, 0)) < (2, 6, 0, 'final'):
8 if getattr(sys, 'version_info', (0, 0, 0)) < (2, 6, 0, 'final'):
9 raise SystemExit("Mercurial requires Python 2.6 or later.")
9 raise SystemExit("Mercurial requires Python 2.6 or later.")
10
10
11 if sys.version_info[0] >= 3:
11 if sys.version_info[0] >= 3:
12 def b(s):
12 def b(s):
13 '''A helper function to emulate 2.6+ bytes literals using string
13 '''A helper function to emulate 2.6+ bytes literals using string
14 literals.'''
14 literals.'''
15 return s.encode('latin1')
15 return s.encode('latin1')
16 printf = eval('print')
16 printf = eval('print')
17 libdir_escape = 'unicode_escape'
17 libdir_escape = 'unicode_escape'
18 else:
18 else:
19 libdir_escape = 'string_escape'
19 libdir_escape = 'string_escape'
20 def b(s):
20 def b(s):
21 '''A helper function to emulate 2.6+ bytes literals using string
21 '''A helper function to emulate 2.6+ bytes literals using string
22 literals.'''
22 literals.'''
23 return s
23 return s
24 def printf(*args, **kwargs):
24 def printf(*args, **kwargs):
25 f = kwargs.get('file', sys.stdout)
25 f = kwargs.get('file', sys.stdout)
26 end = kwargs.get('end', '\n')
26 end = kwargs.get('end', '\n')
27 f.write(b(' ').join(args) + end)
27 f.write(b(' ').join(args) + end)
28
28
29 # Solaris Python packaging brain damage
29 # Solaris Python packaging brain damage
30 try:
30 try:
31 import hashlib
31 import hashlib
32 sha = hashlib.sha1()
32 sha = hashlib.sha1()
33 except ImportError:
33 except ImportError:
34 try:
34 try:
35 import sha
35 import sha
36 sha.sha # silence unused import warning
36 sha.sha # silence unused import warning
37 except ImportError:
37 except ImportError:
38 raise SystemExit(
38 raise SystemExit(
39 "Couldn't import standard hashlib (incomplete Python install).")
39 "Couldn't import standard hashlib (incomplete Python install).")
40
40
41 try:
41 try:
42 import zlib
42 import zlib
43 zlib.compressobj # silence unused import warning
43 zlib.compressobj # silence unused import warning
44 except ImportError:
44 except ImportError:
45 raise SystemExit(
45 raise SystemExit(
46 "Couldn't import standard zlib (incomplete Python install).")
46 "Couldn't import standard zlib (incomplete Python install).")
47
47
48 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
48 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
49 isironpython = False
49 isironpython = False
50 try:
50 try:
51 isironpython = (platform.python_implementation()
51 isironpython = (platform.python_implementation()
52 .lower().find("ironpython") != -1)
52 .lower().find("ironpython") != -1)
53 except AttributeError:
53 except AttributeError:
54 pass
54 pass
55
55
56 if isironpython:
56 if isironpython:
57 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
57 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
58 else:
58 else:
59 try:
59 try:
60 import bz2
60 import bz2
61 bz2.BZ2Compressor # silence unused import warning
61 bz2.BZ2Compressor # silence unused import warning
62 except ImportError:
62 except ImportError:
63 raise SystemExit(
63 raise SystemExit(
64 "Couldn't import standard bz2 (incomplete Python install).")
64 "Couldn't import standard bz2 (incomplete Python install).")
65
65
66 ispypy = "PyPy" in sys.version
66 ispypy = "PyPy" in sys.version
67
67
68 import os, stat, subprocess, time
68 import os, stat, subprocess, time
69 import re
69 import re
70 import shutil
70 import shutil
71 import tempfile
71 import tempfile
72 from distutils import log
72 from distutils import log
73 from distutils.core import setup, Command, Extension
73 from distutils.core import setup, Command, Extension
74 from distutils.dist import Distribution
74 from distutils.dist import Distribution
75 from distutils.command.build import build
75 from distutils.command.build import build
76 from distutils.command.build_ext import build_ext
76 from distutils.command.build_ext import build_ext
77 from distutils.command.build_py import build_py
77 from distutils.command.build_py import build_py
78 from distutils.command.install_lib import install_lib
78 from distutils.command.install_lib import install_lib
79 from distutils.command.install_scripts import install_scripts
79 from distutils.command.install_scripts import install_scripts
80 from distutils.spawn import spawn, find_executable
80 from distutils.spawn import spawn, find_executable
81 from distutils import file_util
81 from distutils import file_util
82 from distutils.errors import CCompilerError, DistutilsExecError
82 from distutils.errors import CCompilerError, DistutilsExecError
83 from distutils.sysconfig import get_python_inc, get_config_var
83 from distutils.sysconfig import get_python_inc, get_config_var
84 from distutils.version import StrictVersion
84 from distutils.version import StrictVersion
85
85
86 convert2to3 = '--c2to3' in sys.argv
86 convert2to3 = '--c2to3' in sys.argv
87 if convert2to3:
87 if convert2to3:
88 try:
88 try:
89 from distutils.command.build_py import build_py_2to3 as build_py
89 from distutils.command.build_py import build_py_2to3 as build_py
90 from lib2to3.refactor import get_fixers_from_package as getfixers
90 from lib2to3.refactor import get_fixers_from_package as getfixers
91 except ImportError:
91 except ImportError:
92 if sys.version_info[0] < 3:
92 if sys.version_info[0] < 3:
93 raise SystemExit("--c2to3 is only compatible with python3.")
93 raise SystemExit("--c2to3 is only compatible with python3.")
94 raise
94 raise
95 sys.path.append('contrib')
95 sys.path.append('contrib')
96 elif sys.version_info[0] >= 3:
96 elif sys.version_info[0] >= 3:
97 raise SystemExit("setup.py with python3 needs --c2to3 (experimental)")
97 raise SystemExit("setup.py with python3 needs --c2to3 (experimental)")
98
98
99 scripts = ['hg']
99 scripts = ['hg']
100 if os.name == 'nt':
100 if os.name == 'nt':
101 scripts.append('contrib/win32/hg.bat')
101 scripts.append('contrib/win32/hg.bat')
102
102
103 # simplified version of distutils.ccompiler.CCompiler.has_function
103 # simplified version of distutils.ccompiler.CCompiler.has_function
104 # that actually removes its temporary files.
104 # that actually removes its temporary files.
105 def hasfunction(cc, funcname):
105 def hasfunction(cc, funcname):
106 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
106 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
107 devnull = oldstderr = None
107 devnull = oldstderr = None
108 try:
108 try:
109 try:
109 try:
110 fname = os.path.join(tmpdir, 'funcname.c')
110 fname = os.path.join(tmpdir, 'funcname.c')
111 f = open(fname, 'w')
111 f = open(fname, 'w')
112 f.write('int main(void) {\n')
112 f.write('int main(void) {\n')
113 f.write(' %s();\n' % funcname)
113 f.write(' %s();\n' % funcname)
114 f.write('}\n')
114 f.write('}\n')
115 f.close()
115 f.close()
116 # Redirect stderr to /dev/null to hide any error messages
116 # Redirect stderr to /dev/null to hide any error messages
117 # from the compiler.
117 # from the compiler.
118 # This will have to be changed if we ever have to check
118 # This will have to be changed if we ever have to check
119 # for a function on Windows.
119 # for a function on Windows.
120 devnull = open('/dev/null', 'w')
120 devnull = open('/dev/null', 'w')
121 oldstderr = os.dup(sys.stderr.fileno())
121 oldstderr = os.dup(sys.stderr.fileno())
122 os.dup2(devnull.fileno(), sys.stderr.fileno())
122 os.dup2(devnull.fileno(), sys.stderr.fileno())
123 objects = cc.compile([fname], output_dir=tmpdir)
123 objects = cc.compile([fname], output_dir=tmpdir)
124 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
124 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
125 except Exception:
125 except Exception:
126 return False
126 return False
127 return True
127 return True
128 finally:
128 finally:
129 if oldstderr is not None:
129 if oldstderr is not None:
130 os.dup2(oldstderr, sys.stderr.fileno())
130 os.dup2(oldstderr, sys.stderr.fileno())
131 if devnull is not None:
131 if devnull is not None:
132 devnull.close()
132 devnull.close()
133 shutil.rmtree(tmpdir)
133 shutil.rmtree(tmpdir)
134
134
135 # py2exe needs to be installed to work
135 # py2exe needs to be installed to work
136 try:
136 try:
137 import py2exe
137 import py2exe
138 py2exe.Distribution # silence unused import warning
138 py2exe.Distribution # silence unused import warning
139 py2exeloaded = True
139 py2exeloaded = True
140 # import py2exe's patched Distribution class
140 # import py2exe's patched Distribution class
141 from distutils.core import Distribution
141 from distutils.core import Distribution
142 except ImportError:
142 except ImportError:
143 py2exeloaded = False
143 py2exeloaded = False
144
144
145 def runcmd(cmd, env):
145 def runcmd(cmd, env):
146 if (sys.platform == 'plan9'
146 if (sys.platform == 'plan9'
147 and (sys.version_info[0] == 2 and sys.version_info[1] < 7)):
147 and (sys.version_info[0] == 2 and sys.version_info[1] < 7)):
148 # subprocess kludge to work around issues in half-baked Python
148 # subprocess kludge to work around issues in half-baked Python
149 # ports, notably bichued/python:
149 # ports, notably bichued/python:
150 _, out, err = os.popen3(cmd)
150 _, out, err = os.popen3(cmd)
151 return str(out), str(err)
151 return str(out), str(err)
152 else:
152 else:
153 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
153 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
154 stderr=subprocess.PIPE, env=env)
154 stderr=subprocess.PIPE, env=env)
155 out, err = p.communicate()
155 out, err = p.communicate()
156 return out, err
156 return out, err
157
157
158 def runhg(cmd, env):
158 def runhg(cmd, env):
159 out, err = runcmd(cmd, env)
159 out, err = runcmd(cmd, env)
160 # If root is executing setup.py, but the repository is owned by
160 # If root is executing setup.py, but the repository is owned by
161 # another user (as in "sudo python setup.py install") we will get
161 # another user (as in "sudo python setup.py install") we will get
162 # trust warnings since the .hg/hgrc file is untrusted. That is
162 # trust warnings since the .hg/hgrc file is untrusted. That is
163 # fine, we don't want to load it anyway. Python may warn about
163 # fine, we don't want to load it anyway. Python may warn about
164 # a missing __init__.py in mercurial/locale, we also ignore that.
164 # a missing __init__.py in mercurial/locale, we also ignore that.
165 err = [e for e in err.splitlines()
165 err = [e for e in err.splitlines()
166 if not e.startswith(b('not trusting file')) \
166 if not e.startswith(b('not trusting file')) \
167 and not e.startswith(b('warning: Not importing')) \
167 and not e.startswith(b('warning: Not importing')) \
168 and not e.startswith(b('obsolete feature not enabled'))]
168 and not e.startswith(b('obsolete feature not enabled'))]
169 if err:
169 if err:
170 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
170 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
171 printf(b('\n').join([b(' ') + e for e in err]), file=sys.stderr)
171 printf(b('\n').join([b(' ') + e for e in err]), file=sys.stderr)
172 return ''
172 return ''
173 return out
173 return out
174
174
175 version = ''
175 version = ''
176
176
177 # Execute hg out of this directory with a custom environment which
177 # Execute hg out of this directory with a custom environment which
178 # includes the pure Python modules in mercurial/pure. We also take
178 # includes the pure Python modules in mercurial/pure. We also take
179 # care to not use any hgrc files and do no localization.
179 # care to not use any hgrc files and do no localization.
180 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
180 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
181 env = {'PYTHONPATH': os.pathsep.join(pypath),
181 env = {'PYTHONPATH': os.pathsep.join(pypath),
182 'HGRCPATH': '',
182 'HGRCPATH': '',
183 'LANGUAGE': 'C'}
183 'LANGUAGE': 'C'}
184 if 'LD_LIBRARY_PATH' in os.environ:
184 if 'LD_LIBRARY_PATH' in os.environ:
185 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
185 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
186 if 'SystemRoot' in os.environ:
186 if 'SystemRoot' in os.environ:
187 # Copy SystemRoot into the custom environment for Python 2.6
187 # Copy SystemRoot into the custom environment for Python 2.6
188 # under Windows. Otherwise, the subprocess will fail with
188 # under Windows. Otherwise, the subprocess will fail with
189 # error 0xc0150004. See: http://bugs.python.org/issue3440
189 # error 0xc0150004. See: http://bugs.python.org/issue3440
190 env['SystemRoot'] = os.environ['SystemRoot']
190 env['SystemRoot'] = os.environ['SystemRoot']
191
191
192 if os.path.isdir('.hg'):
192 if os.path.isdir('.hg'):
193 cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
193 cmd = [sys.executable, 'hg', 'log', '-r', '.', '--template', '{tags}\n']
194 numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
194 numerictags = [t for t in runhg(cmd, env).split() if t[0].isdigit()]
195 hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()
195 hgid = runhg([sys.executable, 'hg', 'id', '-i'], env).strip()
196 if numerictags: # tag(s) found
196 if numerictags: # tag(s) found
197 version = numerictags[-1]
197 version = numerictags[-1]
198 if hgid.endswith('+'): # propagate the dirty status to the tag
198 if hgid.endswith('+'): # propagate the dirty status to the tag
199 version += '+'
199 version += '+'
200 else: # no tag found
200 else: # no tag found
201 ltagcmd = [sys.executable, 'hg', 'parents', '--template',
201 ltagcmd = [sys.executable, 'hg', 'parents', '--template',
202 '{latesttag}']
202 '{latesttag}']
203 ltag = runhg(ltagcmd, env)
203 ltag = runhg(ltagcmd, env)
204 changessincecmd = [sys.executable, 'hg', 'log', '-T', 'x\n', '-r',
204 changessincecmd = [sys.executable, 'hg', 'log', '-T', 'x\n', '-r',
205 "only(.,'%s')" % ltag]
205 "only(.,'%s')" % ltag]
206 changessince = len(runhg(changessincecmd, env).splitlines())
206 changessince = len(runhg(changessincecmd, env).splitlines())
207 version = '%s+%s-%s' % (ltag, changessince, hgid)
207 version = '%s+%s-%s' % (ltag, changessince, hgid)
208 if version.endswith('+'):
208 if version.endswith('+'):
209 version += time.strftime('%Y%m%d')
209 version += time.strftime('%Y%m%d')
210 elif os.path.exists('.hg_archival.txt'):
210 elif os.path.exists('.hg_archival.txt'):
211 kw = dict([[t.strip() for t in l.split(':', 1)]
211 kw = dict([[t.strip() for t in l.split(':', 1)]
212 for l in open('.hg_archival.txt')])
212 for l in open('.hg_archival.txt')])
213 if 'tag' in kw:
213 if 'tag' in kw:
214 version = kw['tag']
214 version = kw['tag']
215 elif 'latesttag' in kw:
215 elif 'latesttag' in kw:
216 if 'changessincelatesttag' in kw:
216 if 'changessincelatesttag' in kw:
217 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
217 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
218 else:
218 else:
219 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
219 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
220 else:
220 else:
221 version = kw.get('node', '')[:12]
221 version = kw.get('node', '')[:12]
222
222
223 if version:
223 if version:
224 f = open("mercurial/__version__.py", "w")
224 f = open("mercurial/__version__.py", "w")
225 f.write('# this file is autogenerated by setup.py\n')
225 f.write('# this file is autogenerated by setup.py\n')
226 f.write('version = "%s"\n' % version)
226 f.write('version = "%s"\n' % version)
227 f.close()
227 f.close()
228
228
229
229
230 try:
230 try:
231 from mercurial import __version__
231 from mercurial import __version__
232 version = __version__.version
232 version = __version__.version
233 except ImportError:
233 except ImportError:
234 version = 'unknown'
234 version = 'unknown'
235
235
236 class hgbuild(build):
236 class hgbuild(build):
237 # Insert hgbuildmo first so that files in mercurial/locale/ are found
237 # Insert hgbuildmo first so that files in mercurial/locale/ are found
238 # when build_py is run next.
238 # when build_py is run next.
239 sub_commands = [('build_mo', None),
239 sub_commands = [('build_mo', None),
240
240
241 # We also need build_ext before build_py. Otherwise, when 2to3 is
241 # We also need build_ext before build_py. Otherwise, when 2to3 is
242 # called (in build_py), it will not find osutil & friends,
242 # called (in build_py), it will not find osutil & friends,
243 # thinking that those modules are global and, consequently, making
243 # thinking that those modules are global and, consequently, making
244 # a mess, now that all module imports are global.
244 # a mess, now that all module imports are global.
245
245
246 ('build_ext', build.has_ext_modules),
246 ('build_ext', build.has_ext_modules),
247 ] + build.sub_commands
247 ] + build.sub_commands
248
248
249 class hgbuildmo(build):
249 class hgbuildmo(build):
250
250
251 description = "build translations (.mo files)"
251 description = "build translations (.mo files)"
252
252
253 def run(self):
253 def run(self):
254 if not find_executable('msgfmt'):
254 if not find_executable('msgfmt'):
255 self.warn("could not find msgfmt executable, no translations "
255 self.warn("could not find msgfmt executable, no translations "
256 "will be built")
256 "will be built")
257 return
257 return
258
258
259 podir = 'i18n'
259 podir = 'i18n'
260 if not os.path.isdir(podir):
260 if not os.path.isdir(podir):
261 self.warn("could not find %s/ directory" % podir)
261 self.warn("could not find %s/ directory" % podir)
262 return
262 return
263
263
264 join = os.path.join
264 join = os.path.join
265 for po in os.listdir(podir):
265 for po in os.listdir(podir):
266 if not po.endswith('.po'):
266 if not po.endswith('.po'):
267 continue
267 continue
268 pofile = join(podir, po)
268 pofile = join(podir, po)
269 modir = join('locale', po[:-3], 'LC_MESSAGES')
269 modir = join('locale', po[:-3], 'LC_MESSAGES')
270 mofile = join(modir, 'hg.mo')
270 mofile = join(modir, 'hg.mo')
271 mobuildfile = join('mercurial', mofile)
271 mobuildfile = join('mercurial', mofile)
272 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
272 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
273 if sys.platform != 'sunos5':
273 if sys.platform != 'sunos5':
274 # msgfmt on Solaris does not know about -c
274 # msgfmt on Solaris does not know about -c
275 cmd.append('-c')
275 cmd.append('-c')
276 self.mkpath(join('mercurial', modir))
276 self.mkpath(join('mercurial', modir))
277 self.make_file([pofile], mobuildfile, spawn, (cmd,))
277 self.make_file([pofile], mobuildfile, spawn, (cmd,))
278
278
279
279
280 class hgdist(Distribution):
280 class hgdist(Distribution):
281 pure = ispypy
281 pure = ispypy
282
282
283 global_options = Distribution.global_options + \
283 global_options = Distribution.global_options + \
284 [('pure', None, "use pure (slow) Python "
284 [('pure', None, "use pure (slow) Python "
285 "code instead of C extensions"),
285 "code instead of C extensions"),
286 ('c2to3', None, "(experimental!) convert "
286 ('c2to3', None, "(experimental!) convert "
287 "code with 2to3"),
287 "code with 2to3"),
288 ]
288 ]
289
289
290 def has_ext_modules(self):
290 def has_ext_modules(self):
291 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
291 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
292 # too late for some cases
292 # too late for some cases
293 return not self.pure and Distribution.has_ext_modules(self)
293 return not self.pure and Distribution.has_ext_modules(self)
294
294
295 class hgbuildext(build_ext):
295 class hgbuildext(build_ext):
296
296
297 def build_extension(self, ext):
297 def build_extension(self, ext):
298 try:
298 try:
299 build_ext.build_extension(self, ext)
299 build_ext.build_extension(self, ext)
300 except CCompilerError:
300 except CCompilerError:
301 if not getattr(ext, 'optional', False):
301 if not getattr(ext, 'optional', False):
302 raise
302 raise
303 log.warn("Failed to build optional extension '%s' (skipping)",
303 log.warn("Failed to build optional extension '%s' (skipping)",
304 ext.name)
304 ext.name)
305
305
306 class hgbuildpy(build_py):
306 class hgbuildpy(build_py):
307 if convert2to3:
307 if convert2to3:
308 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
308 fixer_names = sorted(set(getfixers("lib2to3.fixes") +
309 getfixers("hgfixes")))
309 getfixers("hgfixes")))
310
310
311 def finalize_options(self):
311 def finalize_options(self):
312 build_py.finalize_options(self)
312 build_py.finalize_options(self)
313
313
314 if self.distribution.pure:
314 if self.distribution.pure:
315 if self.py_modules is None:
315 if self.py_modules is None:
316 self.py_modules = []
316 self.py_modules = []
317 for ext in self.distribution.ext_modules:
317 for ext in self.distribution.ext_modules:
318 if ext.name.startswith("mercurial."):
318 if ext.name.startswith("mercurial."):
319 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
319 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
320 self.distribution.ext_modules = []
320 self.distribution.ext_modules = []
321 else:
321 else:
322 h = os.path.join(get_python_inc(), 'Python.h')
322 h = os.path.join(get_python_inc(), 'Python.h')
323 if not os.path.exists(h):
323 if not os.path.exists(h):
324 raise SystemExit('Python headers are required to build '
324 raise SystemExit('Python headers are required to build '
325 'Mercurial but weren\'t found in %s' % h)
325 'Mercurial but weren\'t found in %s' % h)
326
326
327 def find_modules(self):
327 def find_modules(self):
328 modules = build_py.find_modules(self)
328 modules = build_py.find_modules(self)
329 for module in modules:
329 for module in modules:
330 if module[0] == "mercurial.pure":
330 if module[0] == "mercurial.pure":
331 if module[1] != "__init__":
331 if module[1] != "__init__":
332 yield ("mercurial", module[1], module[2])
332 yield ("mercurial", module[1], module[2])
333 else:
333 else:
334 yield module
334 yield module
335
335
336 class buildhgextindex(Command):
336 class buildhgextindex(Command):
337 description = 'generate prebuilt index of hgext (for frozen package)'
337 description = 'generate prebuilt index of hgext (for frozen package)'
338 user_options = []
338 user_options = []
339 _indexfilename = 'hgext/__index__.py'
339 _indexfilename = 'hgext/__index__.py'
340
340
341 def initialize_options(self):
341 def initialize_options(self):
342 pass
342 pass
343
343
344 def finalize_options(self):
344 def finalize_options(self):
345 pass
345 pass
346
346
347 def run(self):
347 def run(self):
348 if os.path.exists(self._indexfilename):
348 if os.path.exists(self._indexfilename):
349 f = open(self._indexfilename, 'w')
349 f = open(self._indexfilename, 'w')
350 f.write('# empty\n')
350 f.write('# empty\n')
351 f.close()
351 f.close()
352
352
353 # here no extension enabled, disabled() lists up everything
353 # here no extension enabled, disabled() lists up everything
354 code = ('import pprint; from mercurial import extensions; '
354 code = ('import pprint; from mercurial import extensions; '
355 'pprint.pprint(extensions.disabled())')
355 'pprint.pprint(extensions.disabled())')
356 out, err = runcmd([sys.executable, '-c', code], env)
356 out, err = runcmd([sys.executable, '-c', code], env)
357 if err:
357 if err:
358 raise DistutilsExecError(err)
358 raise DistutilsExecError(err)
359
359
360 f = open(self._indexfilename, 'w')
360 f = open(self._indexfilename, 'w')
361 f.write('# this file is autogenerated by setup.py\n')
361 f.write('# this file is autogenerated by setup.py\n')
362 f.write('docs = ')
362 f.write('docs = ')
363 f.write(out)
363 f.write(out)
364 f.close()
364 f.close()
365
365
366 class buildhgexe(build_ext):
366 class buildhgexe(build_ext):
367 description = 'compile hg.exe from mercurial/exewrapper.c'
367 description = 'compile hg.exe from mercurial/exewrapper.c'
368
368
369 def build_extensions(self):
369 def build_extensions(self):
370 if os.name != 'nt':
370 if os.name != 'nt':
371 return
371 return
372 if isinstance(self.compiler, HackedMingw32CCompiler):
372 if isinstance(self.compiler, HackedMingw32CCompiler):
373 self.compiler.compiler_so = self.compiler.compiler # no -mdll
373 self.compiler.compiler_so = self.compiler.compiler # no -mdll
374 self.compiler.dll_libraries = [] # no -lmsrvc90
374 self.compiler.dll_libraries = [] # no -lmsrvc90
375 hv = sys.hexversion
375 hv = sys.hexversion
376 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
376 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
377 f = open('mercurial/hgpythonlib.h', 'wb')
377 f = open('mercurial/hgpythonlib.h', 'wb')
378 f.write('/* this file is autogenerated by setup.py */\n')
378 f.write('/* this file is autogenerated by setup.py */\n')
379 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
379 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
380 f.close()
380 f.close()
381 objects = self.compiler.compile(['mercurial/exewrapper.c'],
381 objects = self.compiler.compile(['mercurial/exewrapper.c'],
382 output_dir=self.build_temp)
382 output_dir=self.build_temp)
383 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
383 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
384 target = os.path.join(dir, 'hg')
384 target = os.path.join(dir, 'hg')
385 self.compiler.link_executable(objects, target,
385 self.compiler.link_executable(objects, target,
386 libraries=[],
386 libraries=[],
387 output_dir=self.build_temp)
387 output_dir=self.build_temp)
388
388
389 class hginstalllib(install_lib):
389 class hginstalllib(install_lib):
390 '''
390 '''
391 This is a specialization of install_lib that replaces the copy_file used
391 This is a specialization of install_lib that replaces the copy_file used
392 there so that it supports setting the mode of files after copying them,
392 there so that it supports setting the mode of files after copying them,
393 instead of just preserving the mode that the files originally had. If your
393 instead of just preserving the mode that the files originally had. If your
394 system has a umask of something like 027, preserving the permissions when
394 system has a umask of something like 027, preserving the permissions when
395 copying will lead to a broken install.
395 copying will lead to a broken install.
396
396
397 Note that just passing keep_permissions=False to copy_file would be
397 Note that just passing keep_permissions=False to copy_file would be
398 insufficient, as it might still be applying a umask.
398 insufficient, as it might still be applying a umask.
399 '''
399 '''
400
400
401 def run(self):
401 def run(self):
402 realcopyfile = file_util.copy_file
402 realcopyfile = file_util.copy_file
403 def copyfileandsetmode(*args, **kwargs):
403 def copyfileandsetmode(*args, **kwargs):
404 src, dst = args[0], args[1]
404 src, dst = args[0], args[1]
405 dst, copied = realcopyfile(*args, **kwargs)
405 dst, copied = realcopyfile(*args, **kwargs)
406 if copied:
406 if copied:
407 st = os.stat(src)
407 st = os.stat(src)
408 # Persist executable bit (apply it to group and other if user
408 # Persist executable bit (apply it to group and other if user
409 # has it)
409 # has it)
410 if st[stat.ST_MODE] & stat.S_IXUSR:
410 if st[stat.ST_MODE] & stat.S_IXUSR:
411 setmode = int('0755', 8)
411 setmode = int('0755', 8)
412 else:
412 else:
413 setmode = int('0644', 8)
413 setmode = int('0644', 8)
414 m = stat.S_IMODE(st[stat.ST_MODE])
414 m = stat.S_IMODE(st[stat.ST_MODE])
415 m = (m & ~int('0777', 8)) | setmode
415 m = (m & ~int('0777', 8)) | setmode
416 os.chmod(dst, m)
416 os.chmod(dst, m)
417 file_util.copy_file = copyfileandsetmode
417 file_util.copy_file = copyfileandsetmode
418 try:
418 try:
419 install_lib.run(self)
419 install_lib.run(self)
420 finally:
420 finally:
421 file_util.copy_file = realcopyfile
421 file_util.copy_file = realcopyfile
422
422
423 class hginstallscripts(install_scripts):
423 class hginstallscripts(install_scripts):
424 '''
424 '''
425 This is a specialization of install_scripts that replaces the @LIBDIR@ with
425 This is a specialization of install_scripts that replaces the @LIBDIR@ with
426 the configured directory for modules. If possible, the path is made relative
426 the configured directory for modules. If possible, the path is made relative
427 to the directory for scripts.
427 to the directory for scripts.
428 '''
428 '''
429
429
430 def initialize_options(self):
430 def initialize_options(self):
431 install_scripts.initialize_options(self)
431 install_scripts.initialize_options(self)
432
432
433 self.install_lib = None
433 self.install_lib = None
434
434
435 def finalize_options(self):
435 def finalize_options(self):
436 install_scripts.finalize_options(self)
436 install_scripts.finalize_options(self)
437 self.set_undefined_options('install',
437 self.set_undefined_options('install',
438 ('install_lib', 'install_lib'))
438 ('install_lib', 'install_lib'))
439
439
440 def run(self):
440 def run(self):
441 install_scripts.run(self)
441 install_scripts.run(self)
442
442
443 if (os.path.splitdrive(self.install_dir)[0] !=
443 if (os.path.splitdrive(self.install_dir)[0] !=
444 os.path.splitdrive(self.install_lib)[0]):
444 os.path.splitdrive(self.install_lib)[0]):
445 # can't make relative paths from one drive to another, so use an
445 # can't make relative paths from one drive to another, so use an
446 # absolute path instead
446 # absolute path instead
447 libdir = self.install_lib
447 libdir = self.install_lib
448 else:
448 else:
449 common = os.path.commonprefix((self.install_dir, self.install_lib))
449 common = os.path.commonprefix((self.install_dir, self.install_lib))
450 rest = self.install_dir[len(common):]
450 rest = self.install_dir[len(common):]
451 uplevel = len([n for n in os.path.split(rest) if n])
451 uplevel = len([n for n in os.path.split(rest) if n])
452
452
453 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
453 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
454
454
455 for outfile in self.outfiles:
455 for outfile in self.outfiles:
456 fp = open(outfile, 'rb')
456 fp = open(outfile, 'rb')
457 data = fp.read()
457 data = fp.read()
458 fp.close()
458 fp.close()
459
459
460 # skip binary files
460 # skip binary files
461 if b('\0') in data:
461 if b('\0') in data:
462 continue
462 continue
463
463
464 data = data.replace(b('@LIBDIR@'), libdir.encode(libdir_escape))
464 data = data.replace(b('@LIBDIR@'), libdir.encode(libdir_escape))
465 fp = open(outfile, 'wb')
465 fp = open(outfile, 'wb')
466 fp.write(data)
466 fp.write(data)
467 fp.close()
467 fp.close()
468
468
469 cmdclass = {'build': hgbuild,
469 cmdclass = {'build': hgbuild,
470 'build_mo': hgbuildmo,
470 'build_mo': hgbuildmo,
471 'build_ext': hgbuildext,
471 'build_ext': hgbuildext,
472 'build_py': hgbuildpy,
472 'build_py': hgbuildpy,
473 'build_hgextindex': buildhgextindex,
473 'build_hgextindex': buildhgextindex,
474 'install_lib': hginstalllib,
474 'install_lib': hginstalllib,
475 'install_scripts': hginstallscripts,
475 'install_scripts': hginstallscripts,
476 'build_hgexe': buildhgexe,
476 'build_hgexe': buildhgexe,
477 }
477 }
478
478
479 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
479 packages = ['mercurial', 'mercurial.hgweb', 'mercurial.httpclient',
480 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
480 'hgext', 'hgext.convert', 'hgext.highlight', 'hgext.zeroconf',
481 'hgext.largefiles']
481 'hgext.largefiles']
482
482
483 pymodules = []
483 pymodules = []
484
484
485 common_depends = ['mercurial/util.h']
485 common_depends = ['mercurial/util.h']
486
486
487 osutil_ldflags = []
487 osutil_ldflags = []
488
488
489 if sys.platform == 'darwin':
489 if sys.platform == 'darwin':
490 osutil_ldflags += ['-framework', 'ApplicationServices']
490 osutil_ldflags += ['-framework', 'ApplicationServices']
491
491
492 extmodules = [
492 extmodules = [
493 Extension('mercurial.base85', ['mercurial/base85.c'],
493 Extension('mercurial.base85', ['mercurial/base85.c'],
494 depends=common_depends),
494 depends=common_depends),
495 Extension('mercurial.bdiff', ['mercurial/bdiff.c'],
495 Extension('mercurial.bdiff', ['mercurial/bdiff.c'],
496 depends=common_depends),
496 depends=common_depends),
497 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
497 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'],
498 depends=common_depends),
498 depends=common_depends),
499 Extension('mercurial.mpatch', ['mercurial/mpatch.c'],
499 Extension('mercurial.mpatch', ['mercurial/mpatch.c'],
500 depends=common_depends),
500 depends=common_depends),
501 Extension('mercurial.parsers', ['mercurial/dirs.c',
501 Extension('mercurial.parsers', ['mercurial/dirs.c',
502 'mercurial/manifest.c',
502 'mercurial/manifest.c',
503 'mercurial/parsers.c',
503 'mercurial/parsers.c',
504 'mercurial/pathencode.c'],
504 'mercurial/pathencode.c'],
505 depends=common_depends),
505 depends=common_depends),
506 Extension('mercurial.osutil', ['mercurial/osutil.c'],
507 extra_link_args=osutil_ldflags,
508 depends=common_depends),
506 ]
509 ]
507
510
508 extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
509 extra_link_args=osutil_ldflags,
510 depends=common_depends))
511
512 try:
511 try:
513 from distutils import cygwinccompiler
512 from distutils import cygwinccompiler
514
513
515 # the -mno-cygwin option has been deprecated for years
514 # the -mno-cygwin option has been deprecated for years
516 compiler = cygwinccompiler.Mingw32CCompiler
515 compiler = cygwinccompiler.Mingw32CCompiler
517
516
518 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
517 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
519 def __init__(self, *args, **kwargs):
518 def __init__(self, *args, **kwargs):
520 compiler.__init__(self, *args, **kwargs)
519 compiler.__init__(self, *args, **kwargs)
521 for i in 'compiler compiler_so linker_exe linker_so'.split():
520 for i in 'compiler compiler_so linker_exe linker_so'.split():
522 try:
521 try:
523 getattr(self, i).remove('-mno-cygwin')
522 getattr(self, i).remove('-mno-cygwin')
524 except ValueError:
523 except ValueError:
525 pass
524 pass
526
525
527 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
526 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
528 except ImportError:
527 except ImportError:
529 # the cygwinccompiler package is not available on some Python
528 # the cygwinccompiler package is not available on some Python
530 # distributions like the ones from the optware project for Synology
529 # distributions like the ones from the optware project for Synology
531 # DiskStation boxes
530 # DiskStation boxes
532 class HackedMingw32CCompiler(object):
531 class HackedMingw32CCompiler(object):
533 pass
532 pass
534
533
535 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
534 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
536 'help/*.txt',
535 'help/*.txt',
537 'default.d/*.rc',
536 'default.d/*.rc',
538 'dummycert.pem']}
537 'dummycert.pem']}
539
538
540 def ordinarypath(p):
539 def ordinarypath(p):
541 return p and p[0] != '.' and p[-1] != '~'
540 return p and p[0] != '.' and p[-1] != '~'
542
541
543 for root in ('templates',):
542 for root in ('templates',):
544 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
543 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
545 curdir = curdir.split(os.sep, 1)[1]
544 curdir = curdir.split(os.sep, 1)[1]
546 dirs[:] = filter(ordinarypath, dirs)
545 dirs[:] = filter(ordinarypath, dirs)
547 for f in filter(ordinarypath, files):
546 for f in filter(ordinarypath, files):
548 f = os.path.join(curdir, f)
547 f = os.path.join(curdir, f)
549 packagedata['mercurial'].append(f)
548 packagedata['mercurial'].append(f)
550
549
551 datafiles = []
550 datafiles = []
552 setupversion = version
551 setupversion = version
553 extra = {}
552 extra = {}
554
553
555 if py2exeloaded:
554 if py2exeloaded:
556 extra['console'] = [
555 extra['console'] = [
557 {'script':'hg',
556 {'script':'hg',
558 'copyright':'Copyright (C) 2005-2015 Matt Mackall and others',
557 'copyright':'Copyright (C) 2005-2015 Matt Mackall and others',
559 'product_version':version}]
558 'product_version':version}]
560 # sub command of 'build' because 'py2exe' does not handle sub_commands
559 # sub command of 'build' because 'py2exe' does not handle sub_commands
561 build.sub_commands.insert(0, ('build_hgextindex', None))
560 build.sub_commands.insert(0, ('build_hgextindex', None))
562
561
563 if os.name == 'nt':
562 if os.name == 'nt':
564 # Windows binary file versions for exe/dll files must have the
563 # Windows binary file versions for exe/dll files must have the
565 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
564 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
566 setupversion = version.split('+', 1)[0]
565 setupversion = version.split('+', 1)[0]
567
566
568 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
567 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
569 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
568 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[0].splitlines()
570 if version:
569 if version:
571 version = version[0]
570 version = version[0]
572 if sys.version_info[0] == 3:
571 if sys.version_info[0] == 3:
573 version = version.decode('utf-8')
572 version = version.decode('utf-8')
574 xcode4 = (version.startswith('Xcode') and
573 xcode4 = (version.startswith('Xcode') and
575 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
574 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
576 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
575 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
577 else:
576 else:
578 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
577 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
579 # installed, but instead with only command-line tools. Assume
578 # installed, but instead with only command-line tools. Assume
580 # that only happens on >= Lion, thus no PPC support.
579 # that only happens on >= Lion, thus no PPC support.
581 xcode4 = True
580 xcode4 = True
582 xcode51 = False
581 xcode51 = False
583
582
584 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
583 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
585 # distutils.sysconfig
584 # distutils.sysconfig
586 if xcode4:
585 if xcode4:
587 os.environ['ARCHFLAGS'] = ''
586 os.environ['ARCHFLAGS'] = ''
588
587
589 # XCode 5.1 changes clang such that it now fails to compile if the
588 # XCode 5.1 changes clang such that it now fails to compile if the
590 # -mno-fused-madd flag is passed, but the version of Python shipped with
589 # -mno-fused-madd flag is passed, but the version of Python shipped with
591 # OS X 10.9 Mavericks includes this flag. This causes problems in all
590 # OS X 10.9 Mavericks includes this flag. This causes problems in all
592 # C extension modules, and a bug has been filed upstream at
591 # C extension modules, and a bug has been filed upstream at
593 # http://bugs.python.org/issue21244. We also need to patch this here
592 # http://bugs.python.org/issue21244. We also need to patch this here
594 # so Mercurial can continue to compile in the meantime.
593 # so Mercurial can continue to compile in the meantime.
595 if xcode51:
594 if xcode51:
596 cflags = get_config_var('CFLAGS')
595 cflags = get_config_var('CFLAGS')
597 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
596 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
598 os.environ['CFLAGS'] = (
597 os.environ['CFLAGS'] = (
599 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
598 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
600
599
601 setup(name='mercurial',
600 setup(name='mercurial',
602 version=setupversion,
601 version=setupversion,
603 author='Matt Mackall and many others',
602 author='Matt Mackall and many others',
604 author_email='mercurial@selenic.com',
603 author_email='mercurial@selenic.com',
605 url='http://mercurial.selenic.com/',
604 url='http://mercurial.selenic.com/',
606 download_url='http://mercurial.selenic.com/release/',
605 download_url='http://mercurial.selenic.com/release/',
607 description=('Fast scalable distributed SCM (revision control, version '
606 description=('Fast scalable distributed SCM (revision control, version '
608 'control) system'),
607 'control) system'),
609 long_description=('Mercurial is a distributed SCM tool written in Python.'
608 long_description=('Mercurial is a distributed SCM tool written in Python.'
610 ' It is used by a number of large projects that require'
609 ' It is used by a number of large projects that require'
611 ' fast, reliable distributed revision control, such as '
610 ' fast, reliable distributed revision control, such as '
612 'Mozilla.'),
611 'Mozilla.'),
613 license='GNU GPLv2 or any later version',
612 license='GNU GPLv2 or any later version',
614 classifiers=[
613 classifiers=[
615 'Development Status :: 6 - Mature',
614 'Development Status :: 6 - Mature',
616 'Environment :: Console',
615 'Environment :: Console',
617 'Intended Audience :: Developers',
616 'Intended Audience :: Developers',
618 'Intended Audience :: System Administrators',
617 'Intended Audience :: System Administrators',
619 'License :: OSI Approved :: GNU General Public License (GPL)',
618 'License :: OSI Approved :: GNU General Public License (GPL)',
620 'Natural Language :: Danish',
619 'Natural Language :: Danish',
621 'Natural Language :: English',
620 'Natural Language :: English',
622 'Natural Language :: German',
621 'Natural Language :: German',
623 'Natural Language :: Italian',
622 'Natural Language :: Italian',
624 'Natural Language :: Japanese',
623 'Natural Language :: Japanese',
625 'Natural Language :: Portuguese (Brazilian)',
624 'Natural Language :: Portuguese (Brazilian)',
626 'Operating System :: Microsoft :: Windows',
625 'Operating System :: Microsoft :: Windows',
627 'Operating System :: OS Independent',
626 'Operating System :: OS Independent',
628 'Operating System :: POSIX',
627 'Operating System :: POSIX',
629 'Programming Language :: C',
628 'Programming Language :: C',
630 'Programming Language :: Python',
629 'Programming Language :: Python',
631 'Topic :: Software Development :: Version Control',
630 'Topic :: Software Development :: Version Control',
632 ],
631 ],
633 scripts=scripts,
632 scripts=scripts,
634 packages=packages,
633 packages=packages,
635 py_modules=pymodules,
634 py_modules=pymodules,
636 ext_modules=extmodules,
635 ext_modules=extmodules,
637 data_files=datafiles,
636 data_files=datafiles,
638 package_data=packagedata,
637 package_data=packagedata,
639 cmdclass=cmdclass,
638 cmdclass=cmdclass,
640 distclass=hgdist,
639 distclass=hgdist,
641 options={'py2exe': {'packages': ['hgext', 'email']},
640 options={'py2exe': {'packages': ['hgext', 'email']},
642 'bdist_mpkg': {'zipdist': False,
641 'bdist_mpkg': {'zipdist': False,
643 'license': 'COPYING',
642 'license': 'COPYING',
644 'readme': 'contrib/macosx/Readme.html',
643 'readme': 'contrib/macosx/Readme.html',
645 'welcome': 'contrib/macosx/Welcome.html',
644 'welcome': 'contrib/macosx/Welcome.html',
646 },
645 },
647 },
646 },
648 **extra)
647 **extra)
General Comments 0
You need to be logged in to leave comments. Login now