##// END OF EJS Templates
setup.py: subprocess instead of os.popen, sys.stderr.write instead of print...
Christian Ebert -
r8547:548fd7a0 default
parent child Browse files
Show More
@@ -1,249 +1,251
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, 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 = {}
40 extra = {}
41 scripts = ['hg']
41 scripts = ['hg']
42 if os.name == 'nt':
42 if os.name == 'nt':
43 scripts.append('contrib/win32/hg.bat')
43 scripts.append('contrib/win32/hg.bat')
44
44
45 # simplified version of distutils.ccompiler.CCompiler.has_function
45 # simplified version of distutils.ccompiler.CCompiler.has_function
46 # that actually removes its temporary files.
46 # that actually removes its temporary files.
47 def has_function(cc, funcname):
47 def has_function(cc, funcname):
48 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
48 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
49 devnull = oldstderr = None
49 devnull = oldstderr = None
50 try:
50 try:
51 try:
51 try:
52 fname = os.path.join(tmpdir, 'funcname.c')
52 fname = os.path.join(tmpdir, 'funcname.c')
53 f = open(fname, 'w')
53 f = open(fname, 'w')
54 f.write('int main(void) {\n')
54 f.write('int main(void) {\n')
55 f.write(' %s();\n' % funcname)
55 f.write(' %s();\n' % funcname)
56 f.write('}\n')
56 f.write('}\n')
57 f.close()
57 f.close()
58 # Redirect stderr to /dev/null to hide any error messages
58 # Redirect stderr to /dev/null to hide any error messages
59 # from the compiler.
59 # from the compiler.
60 # This will have to be changed if we ever have to check
60 # This will have to be changed if we ever have to check
61 # for a function on Windows.
61 # for a function on Windows.
62 devnull = open('/dev/null', 'w')
62 devnull = open('/dev/null', 'w')
63 oldstderr = os.dup(sys.stderr.fileno())
63 oldstderr = os.dup(sys.stderr.fileno())
64 os.dup2(devnull.fileno(), sys.stderr.fileno())
64 os.dup2(devnull.fileno(), sys.stderr.fileno())
65 objects = cc.compile([fname])
65 objects = cc.compile([fname])
66 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
66 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
67 except:
67 except:
68 return False
68 return False
69 return True
69 return True
70 finally:
70 finally:
71 if oldstderr is not None:
71 if oldstderr is not None:
72 os.dup2(oldstderr, sys.stderr.fileno())
72 os.dup2(oldstderr, sys.stderr.fileno())
73 if devnull is not None:
73 if devnull is not None:
74 devnull.close()
74 devnull.close()
75 shutil.rmtree(tmpdir)
75 shutil.rmtree(tmpdir)
76
76
77 # py2exe needs to be installed to work
77 # py2exe needs to be installed to work
78 try:
78 try:
79 import py2exe
79 import py2exe
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']
95 extra['console'] = ['hg']
96
96
97 except ImportError:
97 except ImportError:
98 pass
98 pass
99
99
100 if os.path.exists('.hg'):
100 if os.path.isdir('.hg'):
101 # execute hg out of this directory with a custom environment which
101 # execute hg out of this directory with a custom environment which
102 # includes the pure Python modules in mercurial/pure
102 # includes the pure Python modules in mercurial/pure
103 pypath = os.environ.get('PYTHONPATH', '')
103 pypath = os.environ.get('PYTHONPATH', '')
104 purepath = os.path.join('mercurial', 'pure')
104 purepath = os.path.join('mercurial', 'pure')
105 os.environ['PYTHONPATH'] = os.pathsep.join(['mercurial', purepath, pypath])
105 os.environ['PYTHONPATH'] = os.pathsep.join(['mercurial', purepath, pypath])
106 os.environ['HGRCPATH'] = '' # do not read any config file
106 os.environ['HGRCPATH'] = '' # do not read any config file
107 cmd = '%s hg id -it' % sys.executable
107 cmd = [sys.executable, 'hg', 'id', '-i', '-t']
108 version = None
108 version = None
109
109
110 try:
110 l, e = subprocess.Popen(cmd, stdout=subprocess.PIPE,
111 l = os.popen(cmd).read().split()
111 stderr=subprocess.PIPE).communicate()
112 except OSError, e:
113 print "warning: could not establish Mercurial version: %s" % e
114
115 os.environ['PYTHONPATH'] = pypath
112 os.environ['PYTHONPATH'] = pypath
116
113
114 if e:
115 sys.stderr.write('warning: could not establish Mercurial version: %s'
116 % e)
117 else:
118 l = l.split()
117 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
119 while len(l) > 1 and l[-1][0].isalpha(): # remove non-numbered tags
118 l.pop()
120 l.pop()
119 if l:
121 if l:
120 version = l[-1] # latest tag or revision number
122 version = l[-1] # latest tag or revision number
121 if version.endswith('+'):
123 if version.endswith('+'):
122 version += time.strftime('%Y%m%d')
124 version += time.strftime('%Y%m%d')
123
125
124 if version:
126 if version:
125 f = file("mercurial/__version__.py", "w")
127 f = file("mercurial/__version__.py", "w")
126 f.write('# this file is autogenerated by setup.py\n')
128 f.write('# this file is autogenerated by setup.py\n')
127 f.write('version = "%s"\n' % version)
129 f.write('version = "%s"\n' % version)
128 f.close()
130 f.close()
129
131
130 try:
132 try:
131 from mercurial import __version__
133 from mercurial import __version__
132 version = __version__.version
134 version = __version__.version
133 except ImportError:
135 except ImportError:
134 version = 'unknown'
136 version = 'unknown'
135
137
136 class install_package_data(install_data):
138 class install_package_data(install_data):
137 def finalize_options(self):
139 def finalize_options(self):
138 self.set_undefined_options('install',
140 self.set_undefined_options('install',
139 ('install_lib', 'install_dir'))
141 ('install_lib', 'install_dir'))
140 install_data.finalize_options(self)
142 install_data.finalize_options(self)
141
143
142 class build_mo(build):
144 class build_mo(build):
143
145
144 description = "build translations (.mo files)"
146 description = "build translations (.mo files)"
145
147
146 def run(self):
148 def run(self):
147 if not find_executable('msgfmt'):
149 if not find_executable('msgfmt'):
148 self.warn("could not find msgfmt executable, no translations "
150 self.warn("could not find msgfmt executable, no translations "
149 "will be built")
151 "will be built")
150 return
152 return
151
153
152 podir = 'i18n'
154 podir = 'i18n'
153 if not os.path.isdir(podir):
155 if not os.path.isdir(podir):
154 self.warn("could not find %s/ directory" % podir)
156 self.warn("could not find %s/ directory" % podir)
155 return
157 return
156
158
157 join = os.path.join
159 join = os.path.join
158 for po in os.listdir(podir):
160 for po in os.listdir(podir):
159 if not po.endswith('.po'):
161 if not po.endswith('.po'):
160 continue
162 continue
161 pofile = join(podir, po)
163 pofile = join(podir, po)
162 modir = join('locale', po[:-3], 'LC_MESSAGES')
164 modir = join('locale', po[:-3], 'LC_MESSAGES')
163 mofile = join(modir, 'hg.mo')
165 mofile = join(modir, 'hg.mo')
164 cmd = ['msgfmt', '-v', '-o', mofile, pofile]
166 cmd = ['msgfmt', '-v', '-o', mofile, pofile]
165 if sys.platform != 'sunos5':
167 if sys.platform != 'sunos5':
166 # msgfmt on Solaris does not know about -c
168 # msgfmt on Solaris does not know about -c
167 cmd.append('-c')
169 cmd.append('-c')
168 self.mkpath(modir)
170 self.mkpath(modir)
169 self.make_file([pofile], mofile, spawn, (cmd,))
171 self.make_file([pofile], mofile, spawn, (cmd,))
170 self.distribution.data_files.append((join('mercurial', modir),
172 self.distribution.data_files.append((join('mercurial', modir),
171 [mofile]))
173 [mofile]))
172
174
173 build.sub_commands.append(('build_mo', None))
175 build.sub_commands.append(('build_mo', None))
174
176
175 Distribution.pure = 0
177 Distribution.pure = 0
176 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
178 Distribution.global_options.append(('pure', None, "use pure (slow) Python "
177 "code instead of C extensions"))
179 "code instead of C extensions"))
178
180
179 class hg_build_py(build_py):
181 class hg_build_py(build_py):
180
182
181 def finalize_options(self):
183 def finalize_options(self):
182 build_py.finalize_options(self)
184 build_py.finalize_options(self)
183
185
184 if self.distribution.pure:
186 if self.distribution.pure:
185 if self.py_modules is None:
187 if self.py_modules is None:
186 self.py_modules = []
188 self.py_modules = []
187 for ext in self.distribution.ext_modules:
189 for ext in self.distribution.ext_modules:
188 if ext.name.startswith("mercurial."):
190 if ext.name.startswith("mercurial."):
189 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
191 self.py_modules.append("mercurial.pure.%s" % ext.name[10:])
190 self.distribution.ext_modules = []
192 self.distribution.ext_modules = []
191
193
192 def find_modules(self):
194 def find_modules(self):
193 modules = build_py.find_modules(self)
195 modules = build_py.find_modules(self)
194 for module in modules:
196 for module in modules:
195 if module[0] == "mercurial.pure":
197 if module[0] == "mercurial.pure":
196 if module[1] != "__init__":
198 if module[1] != "__init__":
197 yield ("mercurial", module[1], module[2])
199 yield ("mercurial", module[1], module[2])
198 else:
200 else:
199 yield module
201 yield module
200
202
201 cmdclass = {'install_data': install_package_data,
203 cmdclass = {'install_data': install_package_data,
202 'build_mo': build_mo,
204 'build_mo': build_mo,
203 'build_py': hg_build_py}
205 'build_py': hg_build_py}
204
206
205 ext_modules=[
207 ext_modules=[
206 Extension('mercurial.base85', ['mercurial/base85.c']),
208 Extension('mercurial.base85', ['mercurial/base85.c']),
207 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
209 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
208 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
210 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c']),
209 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
211 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
210 Extension('mercurial.parsers', ['mercurial/parsers.c']),
212 Extension('mercurial.parsers', ['mercurial/parsers.c']),
211 Extension('mercurial.osutil', ['mercurial/osutil.c']),
213 Extension('mercurial.osutil', ['mercurial/osutil.c']),
212 ]
214 ]
213
215
214 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
216 packages = ['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert',
215 'hgext.highlight', 'hgext.zeroconf', ]
217 'hgext.highlight', 'hgext.zeroconf', ]
216
218
217 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
219 if sys.platform == 'linux2' and os.uname()[2] > '2.6':
218 # The inotify extension is only usable with Linux 2.6 kernels.
220 # The inotify extension is only usable with Linux 2.6 kernels.
219 # You also need a reasonably recent C library.
221 # You also need a reasonably recent C library.
220 cc = new_compiler()
222 cc = new_compiler()
221 if has_function(cc, 'inotify_add_watch'):
223 if has_function(cc, 'inotify_add_watch'):
222 ext_modules.append(Extension('hgext.inotify.linux._inotify',
224 ext_modules.append(Extension('hgext.inotify.linux._inotify',
223 ['hgext/inotify/linux/_inotify.c']))
225 ['hgext/inotify/linux/_inotify.c']))
224 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
226 packages.extend(['hgext.inotify', 'hgext.inotify.linux'])
225
227
226 datafiles = []
228 datafiles = []
227 for root in ('templates', 'i18n'):
229 for root in ('templates', 'i18n'):
228 for dir, dirs, files in os.walk(root):
230 for dir, dirs, files in os.walk(root):
229 datafiles.append((os.path.join('mercurial', dir),
231 datafiles.append((os.path.join('mercurial', dir),
230 [os.path.join(dir, file_) for file_ in files]))
232 [os.path.join(dir, file_) for file_ in files]))
231
233
232 setup(name='mercurial',
234 setup(name='mercurial',
233 version=version,
235 version=version,
234 author='Matt Mackall',
236 author='Matt Mackall',
235 author_email='mpm@selenic.com',
237 author_email='mpm@selenic.com',
236 url='http://selenic.com/mercurial',
238 url='http://selenic.com/mercurial',
237 description='Scalable distributed SCM',
239 description='Scalable distributed SCM',
238 license='GNU GPL',
240 license='GNU GPL',
239 scripts=scripts,
241 scripts=scripts,
240 packages=packages,
242 packages=packages,
241 ext_modules=ext_modules,
243 ext_modules=ext_modules,
242 data_files=datafiles,
244 data_files=datafiles,
243 cmdclass=cmdclass,
245 cmdclass=cmdclass,
244 options=dict(py2exe=dict(packages=['hgext', 'email']),
246 options=dict(py2exe=dict(packages=['hgext', 'email']),
245 bdist_mpkg=dict(zipdist=True,
247 bdist_mpkg=dict(zipdist=True,
246 license='COPYING',
248 license='COPYING',
247 readme='contrib/macosx/Readme.html',
249 readme='contrib/macosx/Readme.html',
248 welcome='contrib/macosx/Welcome.html')),
250 welcome='contrib/macosx/Welcome.html')),
249 **extra)
251 **extra)
General Comments 0
You need to be logged in to leave comments. Login now