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