##// END OF EJS Templates
setup.py: fixing version info for Windows hg.exe (py2exe)...
Adrian Buehlmann -
r10400:fb203201 default
parent child Browse files
Show More
@@ -1,288 +1,300 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # This is the mercurial setup script.
3 # This is the mercurial setup script.
4 #
4 #
5 # 'python setup.py install', or
5 # 'python setup.py install', or
6 # 'python setup.py --help' for more options
6 # 'python setup.py --help' for more options
7
7
8 import sys
8 import sys
9 if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
9 if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
10 raise SystemExit("Mercurial requires Python 2.4 or later.")
10 raise SystemExit("Mercurial requires Python 2.4 or later.")
11
11
12 # Solaris Python packaging brain damage
12 # Solaris Python packaging brain damage
13 try:
13 try:
14 import hashlib
14 import hashlib
15 sha = hashlib.sha1()
15 sha = hashlib.sha1()
16 except:
16 except:
17 try:
17 try:
18 import sha
18 import sha
19 except:
19 except:
20 raise SystemExit(
20 raise SystemExit(
21 "Couldn't import standard hashlib (incomplete Python install).")
21 "Couldn't import standard hashlib (incomplete Python install).")
22
22
23 try:
23 try:
24 import zlib
24 import zlib
25 except:
25 except:
26 raise SystemExit(
26 raise SystemExit(
27 "Couldn't import standard zlib (incomplete Python install).")
27 "Couldn't import standard zlib (incomplete Python install).")
28
28
29 import os, subprocess, time
29 import os, subprocess, time
30 import shutil
30 import shutil
31 import tempfile
31 import tempfile
32 from distutils.core import setup, Extension
32 from distutils.core import setup, Extension
33 from distutils.dist import Distribution
33 from distutils.dist import Distribution
34 from distutils.command.install_data import install_data
34 from distutils.command.install_data import install_data
35 from distutils.command.build import build
35 from distutils.command.build import build
36 from distutils.command.build_py import build_py
36 from distutils.command.build_py import build_py
37 from distutils.spawn import spawn, find_executable
37 from distutils.spawn import spawn, find_executable
38 from distutils.ccompiler import new_compiler
38 from distutils.ccompiler import new_compiler
39
39
40 extra = {}
41 scripts = ['hg']
40 scripts = ['hg']
42 if os.name == 'nt':
41 if os.name == 'nt':
43 scripts.append('contrib/win32/hg.bat')
42 scripts.append('contrib/win32/hg.bat')
44
43
45 # simplified version of distutils.ccompiler.CCompiler.has_function
44 # simplified version of distutils.ccompiler.CCompiler.has_function
46 # that actually removes its temporary files.
45 # that actually removes its temporary files.
47 def hasfunction(cc, funcname):
46 def hasfunction(cc, funcname):
48 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
47 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
49 devnull = oldstderr = None
48 devnull = oldstderr = None
50 try:
49 try:
51 try:
50 try:
52 fname = os.path.join(tmpdir, 'funcname.c')
51 fname = os.path.join(tmpdir, 'funcname.c')
53 f = open(fname, 'w')
52 f = open(fname, 'w')
54 f.write('int main(void) {\n')
53 f.write('int main(void) {\n')
55 f.write(' %s();\n' % funcname)
54 f.write(' %s();\n' % funcname)
56 f.write('}\n')
55 f.write('}\n')
57 f.close()
56 f.close()
58 # Redirect stderr to /dev/null to hide any error messages
57 # Redirect stderr to /dev/null to hide any error messages
59 # from the compiler.
58 # from the compiler.
60 # This will have to be changed if we ever have to check
59 # This will have to be changed if we ever have to check
61 # for a function on Windows.
60 # for a function on Windows.
62 devnull = open('/dev/null', 'w')
61 devnull = open('/dev/null', 'w')
63 oldstderr = os.dup(sys.stderr.fileno())
62 oldstderr = os.dup(sys.stderr.fileno())
64 os.dup2(devnull.fileno(), sys.stderr.fileno())
63 os.dup2(devnull.fileno(), sys.stderr.fileno())
65 objects = cc.compile([fname], output_dir=tmpdir)
64 objects = cc.compile([fname], output_dir=tmpdir)
66 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
65 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
67 except:
66 except:
68 return False
67 return False
69 return True
68 return True
70 finally:
69 finally:
71 if oldstderr is not None:
70 if oldstderr is not None:
72 os.dup2(oldstderr, sys.stderr.fileno())
71 os.dup2(oldstderr, sys.stderr.fileno())
73 if devnull is not None:
72 if devnull is not None:
74 devnull.close()
73 devnull.close()
75 shutil.rmtree(tmpdir)
74 shutil.rmtree(tmpdir)
76
75
77 # py2exe needs to be installed to work
76 # py2exe needs to be installed to work
78 try:
77 try:
79 import py2exe
78 import py2exe
79 py2exeloaded = True
80
80
81 # Help py2exe to find win32com.shell
81 # Help py2exe to find win32com.shell
82 try:
82 try:
83 import modulefinder
83 import modulefinder
84 import win32com
84 import win32com
85 for p in win32com.__path__[1:]: # Take the path to win32comext
85 for p in win32com.__path__[1:]: # Take the path to win32comext
86 modulefinder.AddPackagePath("win32com", p)
86 modulefinder.AddPackagePath("win32com", p)
87 pn = "win32com.shell"
87 pn = "win32com.shell"
88 __import__(pn)
88 __import__(pn)
89 m = sys.modules[pn]
89 m = sys.modules[pn]
90 for p in m.__path__[1:]:
90 for p in m.__path__[1:]:
91 modulefinder.AddPackagePath(pn, p)
91 modulefinder.AddPackagePath(pn, p)
92 except ImportError:
92 except ImportError:
93 pass
93 pass
94
94
95 extra['console'] = ['hg']
96
97 except ImportError:
95 except ImportError:
96 py2exeloaded = False
98 pass
97 pass
99
98
100 def runcmd(cmd, env):
99 def runcmd(cmd, env):
101 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
100 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
102 stderr=subprocess.PIPE, env=env)
101 stderr=subprocess.PIPE, env=env)
103 out, err = p.communicate()
102 out, err = p.communicate()
104 # If root is executing setup.py, but the repository is owned by
103 # If root is executing setup.py, but the repository is owned by
105 # another user (as in "sudo python setup.py install") we will get
104 # another user (as in "sudo python setup.py install") we will get
106 # trust warnings since the .hg/hgrc file is untrusted. That is
105 # trust warnings since the .hg/hgrc file is untrusted. That is
107 # fine, we don't want to load it anyway. Python may warn about
106 # fine, we don't want to load it anyway. Python may warn about
108 # a missing __init__.py in mercurial/locale, we also ignore that.
107 # a missing __init__.py in mercurial/locale, we also ignore that.
109 err = [e for e in err.splitlines()
108 err = [e for e in err.splitlines()
110 if not e.startswith('Not trusting file') \
109 if not e.startswith('Not trusting file') \
111 and not e.startswith('warning: Not importing')]
110 and not e.startswith('warning: Not importing')]
112 if err:
111 if err:
113 return ''
112 return ''
114 return out
113 return out
115
114
116 version = ''
115 version = ''
117
116
118 if os.path.isdir('.hg'):
117 if os.path.isdir('.hg'):
119 # Execute hg out of this directory with a custom environment which
118 # Execute hg out of this directory with a custom environment which
120 # includes the pure Python modules in mercurial/pure. We also take
119 # includes the pure Python modules in mercurial/pure. We also take
121 # care to not use any hgrc files and do no localization.
120 # care to not use any hgrc files and do no localization.
122 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
121 pypath = ['mercurial', os.path.join('mercurial', 'pure')]
123 env = {'PYTHONPATH': os.pathsep.join(pypath),
122 env = {'PYTHONPATH': os.pathsep.join(pypath),
124 'HGRCPATH': '',
123 'HGRCPATH': '',
125 'LANGUAGE': 'C'}
124 'LANGUAGE': 'C'}
126 if 'LD_LIBRARY_PATH' in os.environ:
125 if 'LD_LIBRARY_PATH' in os.environ:
127 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
126 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
128 if 'SystemRoot' in os.environ:
127 if 'SystemRoot' in os.environ:
129 # Copy SystemRoot into the custom environment for Python 2.6
128 # Copy SystemRoot into the custom environment for Python 2.6
130 # under Windows. Otherwise, the subprocess will fail with
129 # under Windows. Otherwise, the subprocess will fail with
131 # error 0xc0150004. See: http://bugs.python.org/issue3440
130 # error 0xc0150004. See: http://bugs.python.org/issue3440
132 env['SystemRoot'] = os.environ['SystemRoot']
131 env['SystemRoot'] = os.environ['SystemRoot']
133 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
132 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
134 l = runcmd(cmd, env).split()
133 l = runcmd(cmd, env).split()
135 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
134 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
136 l.pop()
135 l.pop()
137 if len(l) > 1: # tag found
136 if len(l) > 1: # tag found
138 version = l[-1]
137 version = l[-1]
139 if l[0].endswith('+'): # propagate the dirty status to the tag
138 if l[0].endswith('+'): # propagate the dirty status to the tag
140 version += '+'
139 version += '+'
141 elif len(l) == 1: # no tag found
140 elif len(l) == 1: # no tag found
142 cmd = [sys.executable, 'hg', 'parents', '--template',
141 cmd = [sys.executable, 'hg', 'parents', '--template',
143 '{latesttag}+{latesttagdistance}-']
142 '{latesttag}+{latesttagdistance}-']
144 version = runcmd(cmd, env) + l[0]
143 version = runcmd(cmd, env) + l[0]
145 if version.endswith('+'):
144 if version.endswith('+'):
146 version += time.strftime('%Y%m%d')
145 version += time.strftime('%Y%m%d')
147 elif os.path.exists('.hg_archival.txt'):
146 elif os.path.exists('.hg_archival.txt'):
148 kw = dict([[t.strip() for t in l.split(':', 1)]
147 kw = dict([[t.strip() for t in l.split(':', 1)]
149 for l in open('.hg_archival.txt')])
148 for l in open('.hg_archival.txt')])
150 if 'tag' in kw:
149 if 'tag' in kw:
151 version = kw['tag']
150 version = kw['tag']
152 elif 'latesttag' in kw:
151 elif 'latesttag' in kw:
153 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
152 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
154 else:
153 else:
155 version = kw.get('node', '')[:12]
154 version = kw.get('node', '')[:12]
156
155
157 if version:
156 if version:
158 f = open("mercurial/__version__.py", "w")
157 f = open("mercurial/__version__.py", "w")
159 f.write('# this file is autogenerated by setup.py\n')
158 f.write('# this file is autogenerated by setup.py\n')
160 f.write('version = "%s"\n' % version)
159 f.write('version = "%s"\n' % version)
161 f.close()
160 f.close()
162
161
163
162
164 try:
163 try:
165 from mercurial import __version__
164 from mercurial import __version__
166 version = __version__.version
165 version = __version__.version
167 except ImportError:
166 except ImportError:
168 version = 'unknown'
167 version = 'unknown'
169
168
170 class hgbuildmo(build):
169 class hgbuildmo(build):
171
170
172 description = "build translations (.mo files)"
171 description = "build translations (.mo files)"
173
172
174 def run(self):
173 def run(self):
175 if not find_executable('msgfmt'):
174 if not find_executable('msgfmt'):
176 self.warn("could not find msgfmt executable, no translations "
175 self.warn("could not find msgfmt executable, no translations "
177 "will be built")
176 "will be built")
178 return
177 return
179
178
180 podir = 'i18n'
179 podir = 'i18n'
181 if not os.path.isdir(podir):
180 if not os.path.isdir(podir):
182 self.warn("could not find %s/ directory" % podir)
181 self.warn("could not find %s/ directory" % podir)
183 return
182 return
184
183
185 join = os.path.join
184 join = os.path.join
186 for po in os.listdir(podir):
185 for po in os.listdir(podir):
187 if not po.endswith('.po'):
186 if not po.endswith('.po'):
188 continue
187 continue
189 pofile = join(podir, po)
188 pofile = join(podir, po)
190 modir = join('locale', po[:-3], 'LC_MESSAGES')
189 modir = join('locale', po[:-3], 'LC_MESSAGES')
191 mofile = join(modir, 'hg.mo')
190 mofile = join(modir, 'hg.mo')
192 mobuildfile = join('mercurial', mofile)
191 mobuildfile = join('mercurial', mofile)
193 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
192 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
194 if sys.platform != 'sunos5':
193 if sys.platform != 'sunos5':
195 # msgfmt on Solaris does not know about -c
194 # msgfmt on Solaris does not know about -c
196 cmd.append('-c')
195 cmd.append('-c')
197 self.mkpath(join('mercurial', modir))
196 self.mkpath(join('mercurial', modir))
198 self.make_file([pofile], mobuildfile, spawn, (cmd,))
197 self.make_file([pofile], mobuildfile, spawn, (cmd,))
199
198
200 # Insert hgbuildmo first so that files in mercurial/locale/ are found
199 # Insert hgbuildmo first so that files in mercurial/locale/ are found
201 # when build_py is run next.
200 # when build_py is run next.
202 build.sub_commands.insert(0, ('build_mo', None))
201 build.sub_commands.insert(0, ('build_mo', None))
203
202
204 Distribution.pure = 0
203 Distribution.pure = 0
205 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
204 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
206 "code instead of C extensions"))
205 "code instead of C extensions"))
207
206
208 class hgbuildpy(build_py):
207 class hgbuildpy(build_py):
209
208
210 def finalize_options(self):
209 def finalize_options(self):
211 build_py.finalize_options(self)
210 build_py.finalize_options(self)
212
211
213 if self.distribution.pure:
212 if self.distribution.pure:
214 if self.py_modules is None:
213 if self.py_modules is None:
215 self.py_modules = []
214 self.py_modules = []
216 for ext in self.distribution.ext_modules:
215 for ext in self.distribution.ext_modules:
217 if ext.name.startswith("mercurial."):
216 if ext.name.startswith("mercurial."):
218 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
217 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
219 self.distribution.ext_modules = []
218 self.distribution.ext_modules = []
220
219
221 def find_modules(self):
220 def find_modules(self):
222 modules = build_py.find_modules(self)
221 modules = build_py.find_modules(self)
223 for module in modules:
222 for module in modules:
224 if module[0] == "mercurial.pure":
223 if module[0] == "mercurial.pure":
225 if module[1] != "__init__":
224 if module[1] != "__init__":
226 yield ("mercurial", module[1], module[2])
225 yield ("mercurial", module[1], module[2])
227 else:
226 else:
228 yield module
227 yield module
229
228
230 cmdclass = {'build_mo': hgbuildmo,
229 cmdclass = {'build_mo': hgbuildmo,
231 'build_py': hgbuildpy}
230 'build_py': hgbuildpy}
232
231
233 extmodules = [
232 extmodules = [
234 Extension('mercurial.base85', ['mercurial/base85.c']),
233 Extension('mercurial.base85', ['mercurial/base85.c']),
235 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
234 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
236 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
235 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
237 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
236 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
238 Extension('mercurial.parsers', ['mercurial/parsers.c']),
237 Extension('mercurial.parsers', ['mercurial/parsers.c']),
239 Extension('mercurial.osutil', ['mercurial/osutil.c']),
238 Extension('mercurial.osutil', ['mercurial/osutil.c']),
240 ]
239 ]
241
240
242 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
241 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
243 'hgext.highlight', 'hgext.zeroconf']
242 'hgext.highlight', 'hgext.zeroconf']
244
243
245 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
244 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
246 # The inotify extension is only usable with Linux 2.6 kernels.
245 # The inotify extension is only usable with Linux 2.6 kernels.
247 # You also need a reasonably recent C library.
246 # You also need a reasonably recent C library.
248 cc = new_compiler()
247 cc = new_compiler()
249 if hasfunction(cc, 'inotify_add_watch'):
248 if hasfunction(cc, 'inotify_add_watch'):
250 extmodules.append(Extension('hgext.inotify.linux._inotify',
249 extmodules.append(Extension('hgext.inotify.linux._inotify',
251 ['hgext/inotify/linux/_inotify.c']))
250 ['hgext/inotify/linux/_inotify.c']))
252 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
251 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
253
252
254 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
253 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
255 'help/*.txt']}
254 'help/*.txt']}
256
255
257 def ordinarypath(p):
256 def ordinarypath(p):
258 return p and p[0] != '.' and p[-1] != '~'
257 return p and p[0] != '.' and p[-1] != '~'
259
258
260 for root in ('templates',):
259 for root in ('templates',):
261 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
260 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
262 curdir = curdir.split(os.sep, 1)[1]
261 curdir = curdir.split(os.sep, 1)[1]
263 dirs[:] = filter(ordinarypath, dirs)
262 dirs[:] = filter(ordinarypath, dirs)
264 for f in filter(ordinarypath, files):
263 for f in filter(ordinarypath, files):
265 f = os.path.join(curdir, f)
264 f = os.path.join(curdir, f)
266 packagedata['mercurial'].append(f)
265 packagedata['mercurial'].append(f)
267
266
268 datafiles = []
267 datafiles = []
268 setupversion = version
269 extra = {}
270
271 if py2exeloaded:
272 extra['console'] = [
273 {'script':'hg',
274 'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
275 'product_version':version}]
276
277 if os.name == 'nt':
278 # Windows binary file versions for exe/dll files must have the
279 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
280 setupversion = version.split('+', 1)[0]
269
281
270 setup(name='mercurial',
282 setup(name='mercurial',
271 version=version,
283 version=setupversion,
272 author='Matt Mackall',
284 author='Matt Mackall',
273 author_email='mpm@selenic.com',
285 author_email='mpm@selenic.com',
274 url='http://mercurial.selenic.com/',
286 url='http://mercurial.selenic.com/',
275 description='Scalable distributed SCM',
287 description='Scalable distributed SCM',
276 license='GNU GPLv2+',
288 license='GNU GPLv2+',
277 scripts=scripts,
289 scripts=scripts,
278 packages=packages,
290 packages=packages,
279 ext_modules=extmodules,
291 ext_modules=extmodules,
280 data_files=datafiles,
292 data_files=datafiles,
281 package_data=packagedata,
293 package_data=packagedata,
282 cmdclass=cmdclass,
294 cmdclass=cmdclass,
283 options=dict(py2exe=dict(packages=['hgext', 'email']),
295 options=dict(py2exe=dict(packages=['hgext', 'email']),
284 bdist_mpkg=dict(zipdist=True,
296 bdist_mpkg=dict(zipdist=True,
285 license='COPYING',
297 license='COPYING',
286 readme='contrib/macosx/Readme.html',
298 readme='contrib/macosx/Readme.html',
287 welcome='contrib/macosx/Welcome.html')),
299 welcome='contrib/macosx/Welcome.html')),
288 **extra)
300 **extra)
General Comments 0
You need to be logged in to leave comments. Login now