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