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