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