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