##// END OF EJS Templates
setup: only write some autogenerated files if they change...
Gregory Szorc -
r35229:61ff0d7d default
parent child Browse files
Show More
@@ -1,1009 +1,1030
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 os
7 import os
8
8
9 supportedpy = '~= 2.7'
9 supportedpy = '~= 2.7'
10 if os.environ.get('HGALLOWPYTHON3', ''):
10 if os.environ.get('HGALLOWPYTHON3', ''):
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 # due to a bug in % formatting in bytestrings.
13 # due to a bug in % formatting in bytestrings.
14 #
14 #
15 # TODO: when we actually work on Python 3, use this string as the
15 # TODO: when we actually work on Python 3, use this string as the
16 # actual supportedpy string.
16 # actual supportedpy string.
17 supportedpy = ','.join([
17 supportedpy = ','.join([
18 '>=2.7',
18 '>=2.7',
19 '!=3.0.*',
19 '!=3.0.*',
20 '!=3.1.*',
20 '!=3.1.*',
21 '!=3.2.*',
21 '!=3.2.*',
22 '!=3.3.*',
22 '!=3.3.*',
23 '!=3.4.*',
23 '!=3.4.*',
24 '!=3.6.0',
24 '!=3.6.0',
25 '!=3.6.1',
25 '!=3.6.1',
26 ])
26 ])
27
27
28 import sys, platform
28 import sys, platform
29 if sys.version_info[0] >= 3:
29 if sys.version_info[0] >= 3:
30 printf = eval('print')
30 printf = eval('print')
31 libdir_escape = 'unicode_escape'
31 libdir_escape = 'unicode_escape'
32 else:
32 else:
33 libdir_escape = 'string_escape'
33 libdir_escape = 'string_escape'
34 def printf(*args, **kwargs):
34 def printf(*args, **kwargs):
35 f = kwargs.get('file', sys.stdout)
35 f = kwargs.get('file', sys.stdout)
36 end = kwargs.get('end', '\n')
36 end = kwargs.get('end', '\n')
37 f.write(b' '.join(args) + end)
37 f.write(b' '.join(args) + end)
38
38
39 # Attempt to guide users to a modern pip - this means that 2.6 users
39 # Attempt to guide users to a modern pip - this means that 2.6 users
40 # should have a chance of getting a 4.2 release, and when we ratchet
40 # should have a chance of getting a 4.2 release, and when we ratchet
41 # the version requirement forward again hopefully everyone will get
41 # the version requirement forward again hopefully everyone will get
42 # something that works for them.
42 # something that works for them.
43 if sys.version_info < (2, 7, 0, 'final'):
43 if sys.version_info < (2, 7, 0, 'final'):
44 pip_message = ('This may be due to an out of date pip. '
44 pip_message = ('This may be due to an out of date pip. '
45 'Make sure you have pip >= 9.0.1.')
45 'Make sure you have pip >= 9.0.1.')
46 try:
46 try:
47 import pip
47 import pip
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
49 if pip_version < (9, 0, 1) :
49 if pip_version < (9, 0, 1) :
50 pip_message = (
50 pip_message = (
51 'Your pip version is out of date, please install '
51 'Your pip version is out of date, please install '
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
53 else:
53 else:
54 # pip is new enough - it must be something else
54 # pip is new enough - it must be something else
55 pip_message = ''
55 pip_message = ''
56 except Exception:
56 except Exception:
57 pass
57 pass
58 error = """
58 error = """
59 Mercurial does not support Python older than 2.7.
59 Mercurial does not support Python older than 2.7.
60 Python {py} detected.
60 Python {py} detected.
61 {pip}
61 {pip}
62 """.format(py=sys.version_info, pip=pip_message)
62 """.format(py=sys.version_info, pip=pip_message)
63 printf(error, file=sys.stderr)
63 printf(error, file=sys.stderr)
64 sys.exit(1)
64 sys.exit(1)
65
65
66 # Solaris Python packaging brain damage
66 # Solaris Python packaging brain damage
67 try:
67 try:
68 import hashlib
68 import hashlib
69 sha = hashlib.sha1()
69 sha = hashlib.sha1()
70 except ImportError:
70 except ImportError:
71 try:
71 try:
72 import sha
72 import sha
73 sha.sha # silence unused import warning
73 sha.sha # silence unused import warning
74 except ImportError:
74 except ImportError:
75 raise SystemExit(
75 raise SystemExit(
76 "Couldn't import standard hashlib (incomplete Python install).")
76 "Couldn't import standard hashlib (incomplete Python install).")
77
77
78 try:
78 try:
79 import zlib
79 import zlib
80 zlib.compressobj # silence unused import warning
80 zlib.compressobj # silence unused import warning
81 except ImportError:
81 except ImportError:
82 raise SystemExit(
82 raise SystemExit(
83 "Couldn't import standard zlib (incomplete Python install).")
83 "Couldn't import standard zlib (incomplete Python install).")
84
84
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
86 isironpython = False
86 isironpython = False
87 try:
87 try:
88 isironpython = (platform.python_implementation()
88 isironpython = (platform.python_implementation()
89 .lower().find("ironpython") != -1)
89 .lower().find("ironpython") != -1)
90 except AttributeError:
90 except AttributeError:
91 pass
91 pass
92
92
93 if isironpython:
93 if isironpython:
94 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
94 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
95 else:
95 else:
96 try:
96 try:
97 import bz2
97 import bz2
98 bz2.BZ2Compressor # silence unused import warning
98 bz2.BZ2Compressor # silence unused import warning
99 except ImportError:
99 except ImportError:
100 raise SystemExit(
100 raise SystemExit(
101 "Couldn't import standard bz2 (incomplete Python install).")
101 "Couldn't import standard bz2 (incomplete Python install).")
102
102
103 ispypy = "PyPy" in sys.version
103 ispypy = "PyPy" in sys.version
104
104
105 import ctypes
105 import ctypes
106 import stat, subprocess, time
106 import stat, subprocess, time
107 import re
107 import re
108 import shutil
108 import shutil
109 import tempfile
109 import tempfile
110 from distutils import log
110 from distutils import log
111 # We have issues with setuptools on some platforms and builders. Until
111 # We have issues with setuptools on some platforms and builders. Until
112 # those are resolved, setuptools is opt-in except for platforms where
112 # those are resolved, setuptools is opt-in except for platforms where
113 # we don't have issues.
113 # we don't have issues.
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
115 if issetuptools:
115 if issetuptools:
116 from setuptools import setup
116 from setuptools import setup
117 else:
117 else:
118 from distutils.core import setup
118 from distutils.core import setup
119 from distutils.ccompiler import new_compiler
119 from distutils.ccompiler import new_compiler
120 from distutils.core import Command, Extension
120 from distutils.core import Command, Extension
121 from distutils.dist import Distribution
121 from distutils.dist import Distribution
122 from distutils.command.build import build
122 from distutils.command.build import build
123 from distutils.command.build_ext import build_ext
123 from distutils.command.build_ext import build_ext
124 from distutils.command.build_py import build_py
124 from distutils.command.build_py import build_py
125 from distutils.command.build_scripts import build_scripts
125 from distutils.command.build_scripts import build_scripts
126 from distutils.command.install import install
126 from distutils.command.install import install
127 from distutils.command.install_lib import install_lib
127 from distutils.command.install_lib import install_lib
128 from distutils.command.install_scripts import install_scripts
128 from distutils.command.install_scripts import install_scripts
129 from distutils.spawn import spawn, find_executable
129 from distutils.spawn import spawn, find_executable
130 from distutils import file_util
130 from distutils import file_util
131 from distutils.errors import (
131 from distutils.errors import (
132 CCompilerError,
132 CCompilerError,
133 DistutilsError,
133 DistutilsError,
134 DistutilsExecError,
134 DistutilsExecError,
135 )
135 )
136 from distutils.sysconfig import get_python_inc, get_config_var
136 from distutils.sysconfig import get_python_inc, get_config_var
137 from distutils.version import StrictVersion
137 from distutils.version import StrictVersion
138
138
139 def write_if_changed(path, content):
140 """Write content to a file iff the content hasn't changed."""
141 if os.path.exists(path):
142 with open(path, 'rb') as fh:
143 current = fh.read()
144 else:
145 current = b''
146
147 if current != content:
148 with open(path, 'wb') as fh:
149 fh.write(content)
150
139 scripts = ['hg']
151 scripts = ['hg']
140 if os.name == 'nt':
152 if os.name == 'nt':
141 # We remove hg.bat if we are able to build hg.exe.
153 # We remove hg.bat if we are able to build hg.exe.
142 scripts.append('contrib/win32/hg.bat')
154 scripts.append('contrib/win32/hg.bat')
143
155
144 def cancompile(cc, code):
156 def cancompile(cc, code):
145 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
157 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
146 devnull = oldstderr = None
158 devnull = oldstderr = None
147 try:
159 try:
148 fname = os.path.join(tmpdir, 'testcomp.c')
160 fname = os.path.join(tmpdir, 'testcomp.c')
149 f = open(fname, 'w')
161 f = open(fname, 'w')
150 f.write(code)
162 f.write(code)
151 f.close()
163 f.close()
152 # Redirect stderr to /dev/null to hide any error messages
164 # Redirect stderr to /dev/null to hide any error messages
153 # from the compiler.
165 # from the compiler.
154 # This will have to be changed if we ever have to check
166 # This will have to be changed if we ever have to check
155 # for a function on Windows.
167 # for a function on Windows.
156 devnull = open('/dev/null', 'w')
168 devnull = open('/dev/null', 'w')
157 oldstderr = os.dup(sys.stderr.fileno())
169 oldstderr = os.dup(sys.stderr.fileno())
158 os.dup2(devnull.fileno(), sys.stderr.fileno())
170 os.dup2(devnull.fileno(), sys.stderr.fileno())
159 objects = cc.compile([fname], output_dir=tmpdir)
171 objects = cc.compile([fname], output_dir=tmpdir)
160 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
172 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
161 return True
173 return True
162 except Exception:
174 except Exception:
163 return False
175 return False
164 finally:
176 finally:
165 if oldstderr is not None:
177 if oldstderr is not None:
166 os.dup2(oldstderr, sys.stderr.fileno())
178 os.dup2(oldstderr, sys.stderr.fileno())
167 if devnull is not None:
179 if devnull is not None:
168 devnull.close()
180 devnull.close()
169 shutil.rmtree(tmpdir)
181 shutil.rmtree(tmpdir)
170
182
171 # simplified version of distutils.ccompiler.CCompiler.has_function
183 # simplified version of distutils.ccompiler.CCompiler.has_function
172 # that actually removes its temporary files.
184 # that actually removes its temporary files.
173 def hasfunction(cc, funcname):
185 def hasfunction(cc, funcname):
174 code = 'int main(void) { %s(); }\n' % funcname
186 code = 'int main(void) { %s(); }\n' % funcname
175 return cancompile(cc, code)
187 return cancompile(cc, code)
176
188
177 def hasheader(cc, headername):
189 def hasheader(cc, headername):
178 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
190 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
179 return cancompile(cc, code)
191 return cancompile(cc, code)
180
192
181 # py2exe needs to be installed to work
193 # py2exe needs to be installed to work
182 try:
194 try:
183 import py2exe
195 import py2exe
184 py2exe.Distribution # silence unused import warning
196 py2exe.Distribution # silence unused import warning
185 py2exeloaded = True
197 py2exeloaded = True
186 # import py2exe's patched Distribution class
198 # import py2exe's patched Distribution class
187 from distutils.core import Distribution
199 from distutils.core import Distribution
188 except ImportError:
200 except ImportError:
189 py2exeloaded = False
201 py2exeloaded = False
190
202
191 def runcmd(cmd, env):
203 def runcmd(cmd, env):
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
204 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
193 stderr=subprocess.PIPE, env=env)
205 stderr=subprocess.PIPE, env=env)
194 out, err = p.communicate()
206 out, err = p.communicate()
195 return p.returncode, out, err
207 return p.returncode, out, err
196
208
197 class hgcommand(object):
209 class hgcommand(object):
198 def __init__(self, cmd, env):
210 def __init__(self, cmd, env):
199 self.cmd = cmd
211 self.cmd = cmd
200 self.env = env
212 self.env = env
201
213
202 def run(self, args):
214 def run(self, args):
203 cmd = self.cmd + args
215 cmd = self.cmd + args
204 returncode, out, err = runcmd(cmd, self.env)
216 returncode, out, err = runcmd(cmd, self.env)
205 err = filterhgerr(err)
217 err = filterhgerr(err)
206 if err or returncode != 0:
218 if err or returncode != 0:
207 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
219 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
208 printf(err, file=sys.stderr)
220 printf(err, file=sys.stderr)
209 return ''
221 return ''
210 return out
222 return out
211
223
212 def filterhgerr(err):
224 def filterhgerr(err):
213 # If root is executing setup.py, but the repository is owned by
225 # If root is executing setup.py, but the repository is owned by
214 # another user (as in "sudo python setup.py install") we will get
226 # another user (as in "sudo python setup.py install") we will get
215 # trust warnings since the .hg/hgrc file is untrusted. That is
227 # trust warnings since the .hg/hgrc file is untrusted. That is
216 # fine, we don't want to load it anyway. Python may warn about
228 # fine, we don't want to load it anyway. Python may warn about
217 # a missing __init__.py in mercurial/locale, we also ignore that.
229 # a missing __init__.py in mercurial/locale, we also ignore that.
218 err = [e for e in err.splitlines()
230 err = [e for e in err.splitlines()
219 if (not e.startswith(b'not trusting file')
231 if (not e.startswith(b'not trusting file')
220 and not e.startswith(b'warning: Not importing')
232 and not e.startswith(b'warning: Not importing')
221 and not e.startswith(b'obsolete feature not enabled')
233 and not e.startswith(b'obsolete feature not enabled')
222 and not e.startswith(b'devel-warn:'))]
234 and not e.startswith(b'devel-warn:'))]
223 return b'\n'.join(b' ' + e for e in err)
235 return b'\n'.join(b' ' + e for e in err)
224
236
225 def findhg():
237 def findhg():
226 """Try to figure out how we should invoke hg for examining the local
238 """Try to figure out how we should invoke hg for examining the local
227 repository contents.
239 repository contents.
228
240
229 Returns an hgcommand object."""
241 Returns an hgcommand object."""
230 # By default, prefer the "hg" command in the user's path. This was
242 # By default, prefer the "hg" command in the user's path. This was
231 # presumably the hg command that the user used to create this repository.
243 # presumably the hg command that the user used to create this repository.
232 #
244 #
233 # This repository may require extensions or other settings that would not
245 # This repository may require extensions or other settings that would not
234 # be enabled by running the hg script directly from this local repository.
246 # be enabled by running the hg script directly from this local repository.
235 hgenv = os.environ.copy()
247 hgenv = os.environ.copy()
236 # Use HGPLAIN to disable hgrc settings that would change output formatting,
248 # Use HGPLAIN to disable hgrc settings that would change output formatting,
237 # and disable localization for the same reasons.
249 # and disable localization for the same reasons.
238 hgenv['HGPLAIN'] = '1'
250 hgenv['HGPLAIN'] = '1'
239 hgenv['LANGUAGE'] = 'C'
251 hgenv['LANGUAGE'] = 'C'
240 hgcmd = ['hg']
252 hgcmd = ['hg']
241 # Run a simple "hg log" command just to see if using hg from the user's
253 # Run a simple "hg log" command just to see if using hg from the user's
242 # path works and can successfully interact with this repository.
254 # path works and can successfully interact with this repository.
243 check_cmd = ['log', '-r.', '-Ttest']
255 check_cmd = ['log', '-r.', '-Ttest']
244 try:
256 try:
245 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
257 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
246 except EnvironmentError:
258 except EnvironmentError:
247 retcode = -1
259 retcode = -1
248 if retcode == 0 and not filterhgerr(err):
260 if retcode == 0 and not filterhgerr(err):
249 return hgcommand(hgcmd, hgenv)
261 return hgcommand(hgcmd, hgenv)
250
262
251 # Fall back to trying the local hg installation.
263 # Fall back to trying the local hg installation.
252 hgenv = localhgenv()
264 hgenv = localhgenv()
253 hgcmd = [sys.executable, 'hg']
265 hgcmd = [sys.executable, 'hg']
254 try:
266 try:
255 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
267 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
256 except EnvironmentError:
268 except EnvironmentError:
257 retcode = -1
269 retcode = -1
258 if retcode == 0 and not filterhgerr(err):
270 if retcode == 0 and not filterhgerr(err):
259 return hgcommand(hgcmd, hgenv)
271 return hgcommand(hgcmd, hgenv)
260
272
261 raise SystemExit('Unable to find a working hg binary to extract the '
273 raise SystemExit('Unable to find a working hg binary to extract the '
262 'version from the repository tags')
274 'version from the repository tags')
263
275
264 def localhgenv():
276 def localhgenv():
265 """Get an environment dictionary to use for invoking or importing
277 """Get an environment dictionary to use for invoking or importing
266 mercurial from the local repository."""
278 mercurial from the local repository."""
267 # Execute hg out of this directory with a custom environment which takes
279 # Execute hg out of this directory with a custom environment which takes
268 # care to not use any hgrc files and do no localization.
280 # care to not use any hgrc files and do no localization.
269 env = {'HGMODULEPOLICY': 'py',
281 env = {'HGMODULEPOLICY': 'py',
270 'HGRCPATH': '',
282 'HGRCPATH': '',
271 'LANGUAGE': 'C',
283 'LANGUAGE': 'C',
272 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
284 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
273 if 'LD_LIBRARY_PATH' in os.environ:
285 if 'LD_LIBRARY_PATH' in os.environ:
274 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
286 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
275 if 'SystemRoot' in os.environ:
287 if 'SystemRoot' in os.environ:
276 # SystemRoot is required by Windows to load various DLLs. See:
288 # SystemRoot is required by Windows to load various DLLs. See:
277 # https://bugs.python.org/issue13524#msg148850
289 # https://bugs.python.org/issue13524#msg148850
278 env['SystemRoot'] = os.environ['SystemRoot']
290 env['SystemRoot'] = os.environ['SystemRoot']
279 return env
291 return env
280
292
281 version = ''
293 version = ''
282
294
283 if os.path.isdir('.hg'):
295 if os.path.isdir('.hg'):
284 hg = findhg()
296 hg = findhg()
285 cmd = ['log', '-r', '.', '--template', '{tags}\n']
297 cmd = ['log', '-r', '.', '--template', '{tags}\n']
286 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
298 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
287 hgid = hg.run(['id', '-i']).strip()
299 hgid = hg.run(['id', '-i']).strip()
288 if not hgid:
300 if not hgid:
289 # Bail out if hg is having problems interacting with this repository,
301 # Bail out if hg is having problems interacting with this repository,
290 # rather than falling through and producing a bogus version number.
302 # rather than falling through and producing a bogus version number.
291 # Continuing with an invalid version number will break extensions
303 # Continuing with an invalid version number will break extensions
292 # that define minimumhgversion.
304 # that define minimumhgversion.
293 raise SystemExit('Unable to determine hg version from local repository')
305 raise SystemExit('Unable to determine hg version from local repository')
294 if numerictags: # tag(s) found
306 if numerictags: # tag(s) found
295 version = numerictags[-1]
307 version = numerictags[-1]
296 if hgid.endswith('+'): # propagate the dirty status to the tag
308 if hgid.endswith('+'): # propagate the dirty status to the tag
297 version += '+'
309 version += '+'
298 else: # no tag found
310 else: # no tag found
299 ltagcmd = ['parents', '--template', '{latesttag}']
311 ltagcmd = ['parents', '--template', '{latesttag}']
300 ltag = hg.run(ltagcmd)
312 ltag = hg.run(ltagcmd)
301 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
313 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
302 changessince = len(hg.run(changessincecmd).splitlines())
314 changessince = len(hg.run(changessincecmd).splitlines())
303 version = '%s+%s-%s' % (ltag, changessince, hgid)
315 version = '%s+%s-%s' % (ltag, changessince, hgid)
304 if version.endswith('+'):
316 if version.endswith('+'):
305 version += time.strftime('%Y%m%d')
317 version += time.strftime('%Y%m%d')
306 elif os.path.exists('.hg_archival.txt'):
318 elif os.path.exists('.hg_archival.txt'):
307 kw = dict([[t.strip() for t in l.split(':', 1)]
319 kw = dict([[t.strip() for t in l.split(':', 1)]
308 for l in open('.hg_archival.txt')])
320 for l in open('.hg_archival.txt')])
309 if 'tag' in kw:
321 if 'tag' in kw:
310 version = kw['tag']
322 version = kw['tag']
311 elif 'latesttag' in kw:
323 elif 'latesttag' in kw:
312 if 'changessincelatesttag' in kw:
324 if 'changessincelatesttag' in kw:
313 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
325 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
314 else:
326 else:
315 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
327 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
316 else:
328 else:
317 version = kw.get('node', '')[:12]
329 version = kw.get('node', '')[:12]
318
330
319 if version:
331 if version:
320 with open("mercurial/__version__.py", "w") as f:
332 versionb = version
321 f.write('# this file is autogenerated by setup.py\n')
333 if not isinstance(versionb, bytes):
322 f.write('version = "%s"\n' % version)
334 versionb = versionb.encode('ascii')
335
336 write_if_changed('mercurial/__version__.py', b''.join([
337 b'# this file is autogenerated by setup.py\n'
338 b'version = "%s"\n' % versionb,
339 ]))
323
340
324 try:
341 try:
325 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
342 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
326 os.environ['HGMODULEPOLICY'] = 'py'
343 os.environ['HGMODULEPOLICY'] = 'py'
327 from mercurial import __version__
344 from mercurial import __version__
328 version = __version__.version
345 version = __version__.version
329 except ImportError:
346 except ImportError:
330 version = 'unknown'
347 version = 'unknown'
331 finally:
348 finally:
332 if oldpolicy is None:
349 if oldpolicy is None:
333 del os.environ['HGMODULEPOLICY']
350 del os.environ['HGMODULEPOLICY']
334 else:
351 else:
335 os.environ['HGMODULEPOLICY'] = oldpolicy
352 os.environ['HGMODULEPOLICY'] = oldpolicy
336
353
337 class hgbuild(build):
354 class hgbuild(build):
338 # Insert hgbuildmo first so that files in mercurial/locale/ are found
355 # Insert hgbuildmo first so that files in mercurial/locale/ are found
339 # when build_py is run next.
356 # when build_py is run next.
340 sub_commands = [('build_mo', None)] + build.sub_commands
357 sub_commands = [('build_mo', None)] + build.sub_commands
341
358
342 class hgbuildmo(build):
359 class hgbuildmo(build):
343
360
344 description = "build translations (.mo files)"
361 description = "build translations (.mo files)"
345
362
346 def run(self):
363 def run(self):
347 if not find_executable('msgfmt'):
364 if not find_executable('msgfmt'):
348 self.warn("could not find msgfmt executable, no translations "
365 self.warn("could not find msgfmt executable, no translations "
349 "will be built")
366 "will be built")
350 return
367 return
351
368
352 podir = 'i18n'
369 podir = 'i18n'
353 if not os.path.isdir(podir):
370 if not os.path.isdir(podir):
354 self.warn("could not find %s/ directory" % podir)
371 self.warn("could not find %s/ directory" % podir)
355 return
372 return
356
373
357 join = os.path.join
374 join = os.path.join
358 for po in os.listdir(podir):
375 for po in os.listdir(podir):
359 if not po.endswith('.po'):
376 if not po.endswith('.po'):
360 continue
377 continue
361 pofile = join(podir, po)
378 pofile = join(podir, po)
362 modir = join('locale', po[:-3], 'LC_MESSAGES')
379 modir = join('locale', po[:-3], 'LC_MESSAGES')
363 mofile = join(modir, 'hg.mo')
380 mofile = join(modir, 'hg.mo')
364 mobuildfile = join('mercurial', mofile)
381 mobuildfile = join('mercurial', mofile)
365 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
382 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
366 if sys.platform != 'sunos5':
383 if sys.platform != 'sunos5':
367 # msgfmt on Solaris does not know about -c
384 # msgfmt on Solaris does not know about -c
368 cmd.append('-c')
385 cmd.append('-c')
369 self.mkpath(join('mercurial', modir))
386 self.mkpath(join('mercurial', modir))
370 self.make_file([pofile], mobuildfile, spawn, (cmd,))
387 self.make_file([pofile], mobuildfile, spawn, (cmd,))
371
388
372
389
373 class hgdist(Distribution):
390 class hgdist(Distribution):
374 pure = False
391 pure = False
375 cffi = ispypy
392 cffi = ispypy
376
393
377 global_options = Distribution.global_options + \
394 global_options = Distribution.global_options + \
378 [('pure', None, "use pure (slow) Python "
395 [('pure', None, "use pure (slow) Python "
379 "code instead of C extensions"),
396 "code instead of C extensions"),
380 ]
397 ]
381
398
382 def has_ext_modules(self):
399 def has_ext_modules(self):
383 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
400 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
384 # too late for some cases
401 # too late for some cases
385 return not self.pure and Distribution.has_ext_modules(self)
402 return not self.pure and Distribution.has_ext_modules(self)
386
403
387 # This is ugly as a one-liner. So use a variable.
404 # This is ugly as a one-liner. So use a variable.
388 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
405 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
389 buildextnegops['no-zstd'] = 'zstd'
406 buildextnegops['no-zstd'] = 'zstd'
390
407
391 class hgbuildext(build_ext):
408 class hgbuildext(build_ext):
392 user_options = build_ext.user_options + [
409 user_options = build_ext.user_options + [
393 ('zstd', None, 'compile zstd bindings [default]'),
410 ('zstd', None, 'compile zstd bindings [default]'),
394 ('no-zstd', None, 'do not compile zstd bindings'),
411 ('no-zstd', None, 'do not compile zstd bindings'),
395 ]
412 ]
396
413
397 boolean_options = build_ext.boolean_options + ['zstd']
414 boolean_options = build_ext.boolean_options + ['zstd']
398 negative_opt = buildextnegops
415 negative_opt = buildextnegops
399
416
400 def initialize_options(self):
417 def initialize_options(self):
401 self.zstd = True
418 self.zstd = True
402 return build_ext.initialize_options(self)
419 return build_ext.initialize_options(self)
403
420
404 def build_extensions(self):
421 def build_extensions(self):
405 # Filter out zstd if disabled via argument.
422 # Filter out zstd if disabled via argument.
406 if not self.zstd:
423 if not self.zstd:
407 self.extensions = [e for e in self.extensions
424 self.extensions = [e for e in self.extensions
408 if e.name != 'mercurial.zstd']
425 if e.name != 'mercurial.zstd']
409
426
410 return build_ext.build_extensions(self)
427 return build_ext.build_extensions(self)
411
428
412 def build_extension(self, ext):
429 def build_extension(self, ext):
413 try:
430 try:
414 build_ext.build_extension(self, ext)
431 build_ext.build_extension(self, ext)
415 except CCompilerError:
432 except CCompilerError:
416 if not getattr(ext, 'optional', False):
433 if not getattr(ext, 'optional', False):
417 raise
434 raise
418 log.warn("Failed to build optional extension '%s' (skipping)",
435 log.warn("Failed to build optional extension '%s' (skipping)",
419 ext.name)
436 ext.name)
420
437
421 class hgbuildscripts(build_scripts):
438 class hgbuildscripts(build_scripts):
422 def run(self):
439 def run(self):
423 if os.name != 'nt' or self.distribution.pure:
440 if os.name != 'nt' or self.distribution.pure:
424 return build_scripts.run(self)
441 return build_scripts.run(self)
425
442
426 exebuilt = False
443 exebuilt = False
427 try:
444 try:
428 self.run_command('build_hgexe')
445 self.run_command('build_hgexe')
429 exebuilt = True
446 exebuilt = True
430 except (DistutilsError, CCompilerError):
447 except (DistutilsError, CCompilerError):
431 log.warn('failed to build optional hg.exe')
448 log.warn('failed to build optional hg.exe')
432
449
433 if exebuilt:
450 if exebuilt:
434 # Copying hg.exe to the scripts build directory ensures it is
451 # Copying hg.exe to the scripts build directory ensures it is
435 # installed by the install_scripts command.
452 # installed by the install_scripts command.
436 hgexecommand = self.get_finalized_command('build_hgexe')
453 hgexecommand = self.get_finalized_command('build_hgexe')
437 dest = os.path.join(self.build_dir, 'hg.exe')
454 dest = os.path.join(self.build_dir, 'hg.exe')
438 self.mkpath(self.build_dir)
455 self.mkpath(self.build_dir)
439 self.copy_file(hgexecommand.hgexepath, dest)
456 self.copy_file(hgexecommand.hgexepath, dest)
440
457
441 # Remove hg.bat because it is redundant with hg.exe.
458 # Remove hg.bat because it is redundant with hg.exe.
442 self.scripts.remove('contrib/win32/hg.bat')
459 self.scripts.remove('contrib/win32/hg.bat')
443
460
444 return build_scripts.run(self)
461 return build_scripts.run(self)
445
462
446 class hgbuildpy(build_py):
463 class hgbuildpy(build_py):
447 def finalize_options(self):
464 def finalize_options(self):
448 build_py.finalize_options(self)
465 build_py.finalize_options(self)
449
466
450 if self.distribution.pure:
467 if self.distribution.pure:
451 self.distribution.ext_modules = []
468 self.distribution.ext_modules = []
452 elif self.distribution.cffi:
469 elif self.distribution.cffi:
453 from mercurial.cffi import (
470 from mercurial.cffi import (
454 bdiffbuild,
471 bdiffbuild,
455 mpatchbuild,
472 mpatchbuild,
456 )
473 )
457 exts = [mpatchbuild.ffi.distutils_extension(),
474 exts = [mpatchbuild.ffi.distutils_extension(),
458 bdiffbuild.ffi.distutils_extension()]
475 bdiffbuild.ffi.distutils_extension()]
459 # cffi modules go here
476 # cffi modules go here
460 if sys.platform == 'darwin':
477 if sys.platform == 'darwin':
461 from mercurial.cffi import osutilbuild
478 from mercurial.cffi import osutilbuild
462 exts.append(osutilbuild.ffi.distutils_extension())
479 exts.append(osutilbuild.ffi.distutils_extension())
463 self.distribution.ext_modules = exts
480 self.distribution.ext_modules = exts
464 else:
481 else:
465 h = os.path.join(get_python_inc(), 'Python.h')
482 h = os.path.join(get_python_inc(), 'Python.h')
466 if not os.path.exists(h):
483 if not os.path.exists(h):
467 raise SystemExit('Python headers are required to build '
484 raise SystemExit('Python headers are required to build '
468 'Mercurial but weren\'t found in %s' % h)
485 'Mercurial but weren\'t found in %s' % h)
469
486
470 def run(self):
487 def run(self):
471 basepath = os.path.join(self.build_lib, 'mercurial')
488 basepath = os.path.join(self.build_lib, 'mercurial')
472 self.mkpath(basepath)
489 self.mkpath(basepath)
473
490
474 if self.distribution.pure:
491 if self.distribution.pure:
475 modulepolicy = 'py'
492 modulepolicy = 'py'
476 elif self.build_lib == '.':
493 elif self.build_lib == '.':
477 # in-place build should run without rebuilding C extensions
494 # in-place build should run without rebuilding C extensions
478 modulepolicy = 'allow'
495 modulepolicy = 'allow'
479 else:
496 else:
480 modulepolicy = 'c'
497 modulepolicy = 'c'
481 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
498
482 f.write('# this file is autogenerated by setup.py\n')
499 content = b''.join([
483 f.write('modulepolicy = b"%s"\n' % modulepolicy)
500 b'# this file is autogenerated by setup.py\n',
501 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
502 ])
503 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'),
504 content)
484
505
485 build_py.run(self)
506 build_py.run(self)
486
507
487 class buildhgextindex(Command):
508 class buildhgextindex(Command):
488 description = 'generate prebuilt index of hgext (for frozen package)'
509 description = 'generate prebuilt index of hgext (for frozen package)'
489 user_options = []
510 user_options = []
490 _indexfilename = 'hgext/__index__.py'
511 _indexfilename = 'hgext/__index__.py'
491
512
492 def initialize_options(self):
513 def initialize_options(self):
493 pass
514 pass
494
515
495 def finalize_options(self):
516 def finalize_options(self):
496 pass
517 pass
497
518
498 def run(self):
519 def run(self):
499 if os.path.exists(self._indexfilename):
520 if os.path.exists(self._indexfilename):
500 with open(self._indexfilename, 'w') as f:
521 with open(self._indexfilename, 'w') as f:
501 f.write('# empty\n')
522 f.write('# empty\n')
502
523
503 # here no extension enabled, disabled() lists up everything
524 # here no extension enabled, disabled() lists up everything
504 code = ('import pprint; from mercurial import extensions; '
525 code = ('import pprint; from mercurial import extensions; '
505 'pprint.pprint(extensions.disabled())')
526 'pprint.pprint(extensions.disabled())')
506 returncode, out, err = runcmd([sys.executable, '-c', code],
527 returncode, out, err = runcmd([sys.executable, '-c', code],
507 localhgenv())
528 localhgenv())
508 if err or returncode != 0:
529 if err or returncode != 0:
509 raise DistutilsExecError(err)
530 raise DistutilsExecError(err)
510
531
511 with open(self._indexfilename, 'w') as f:
532 with open(self._indexfilename, 'w') as f:
512 f.write('# this file is autogenerated by setup.py\n')
533 f.write('# this file is autogenerated by setup.py\n')
513 f.write('docs = ')
534 f.write('docs = ')
514 f.write(out)
535 f.write(out)
515
536
516 class buildhgexe(build_ext):
537 class buildhgexe(build_ext):
517 description = 'compile hg.exe from mercurial/exewrapper.c'
538 description = 'compile hg.exe from mercurial/exewrapper.c'
518 user_options = build_ext.user_options + [
539 user_options = build_ext.user_options + [
519 ('long-paths-support', None, 'enable support for long paths on '
540 ('long-paths-support', None, 'enable support for long paths on '
520 'Windows (off by default and '
541 'Windows (off by default and '
521 'experimental)'),
542 'experimental)'),
522 ]
543 ]
523
544
524 LONG_PATHS_MANIFEST = """
545 LONG_PATHS_MANIFEST = """
525 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
546 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
526 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
547 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
527 <application>
548 <application>
528 <windowsSettings
549 <windowsSettings
529 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
550 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
530 <ws2:longPathAware>true</ws2:longPathAware>
551 <ws2:longPathAware>true</ws2:longPathAware>
531 </windowsSettings>
552 </windowsSettings>
532 </application>
553 </application>
533 </assembly>"""
554 </assembly>"""
534
555
535 def initialize_options(self):
556 def initialize_options(self):
536 build_ext.initialize_options(self)
557 build_ext.initialize_options(self)
537 self.long_paths_support = False
558 self.long_paths_support = False
538
559
539 def build_extensions(self):
560 def build_extensions(self):
540 if os.name != 'nt':
561 if os.name != 'nt':
541 return
562 return
542 if isinstance(self.compiler, HackedMingw32CCompiler):
563 if isinstance(self.compiler, HackedMingw32CCompiler):
543 self.compiler.compiler_so = self.compiler.compiler # no -mdll
564 self.compiler.compiler_so = self.compiler.compiler # no -mdll
544 self.compiler.dll_libraries = [] # no -lmsrvc90
565 self.compiler.dll_libraries = [] # no -lmsrvc90
545
566
546 # Different Python installs can have different Python library
567 # Different Python installs can have different Python library
547 # names. e.g. the official CPython distribution uses pythonXY.dll
568 # names. e.g. the official CPython distribution uses pythonXY.dll
548 # and MinGW uses libpythonX.Y.dll.
569 # and MinGW uses libpythonX.Y.dll.
549 _kernel32 = ctypes.windll.kernel32
570 _kernel32 = ctypes.windll.kernel32
550 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
571 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
551 ctypes.c_void_p,
572 ctypes.c_void_p,
552 ctypes.c_ulong]
573 ctypes.c_ulong]
553 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
574 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
554 size = 1000
575 size = 1000
555 buf = ctypes.create_string_buffer(size + 1)
576 buf = ctypes.create_string_buffer(size + 1)
556 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
577 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
557 size)
578 size)
558
579
559 if filelen > 0 and filelen != size:
580 if filelen > 0 and filelen != size:
560 dllbasename = os.path.basename(buf.value)
581 dllbasename = os.path.basename(buf.value)
561 if not dllbasename.lower().endswith('.dll'):
582 if not dllbasename.lower().endswith('.dll'):
562 raise SystemExit('Python DLL does not end with .dll: %s' %
583 raise SystemExit('Python DLL does not end with .dll: %s' %
563 dllbasename)
584 dllbasename)
564 pythonlib = dllbasename[:-4]
585 pythonlib = dllbasename[:-4]
565 else:
586 else:
566 log.warn('could not determine Python DLL filename; '
587 log.warn('could not determine Python DLL filename; '
567 'assuming pythonXY')
588 'assuming pythonXY')
568
589
569 hv = sys.hexversion
590 hv = sys.hexversion
570 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
591 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
571
592
572 log.info('using %s as Python library name' % pythonlib)
593 log.info('using %s as Python library name' % pythonlib)
573 with open('mercurial/hgpythonlib.h', 'wb') as f:
594 with open('mercurial/hgpythonlib.h', 'wb') as f:
574 f.write('/* this file is autogenerated by setup.py */\n')
595 f.write('/* this file is autogenerated by setup.py */\n')
575 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
596 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
576 objects = self.compiler.compile(['mercurial/exewrapper.c'],
597 objects = self.compiler.compile(['mercurial/exewrapper.c'],
577 output_dir=self.build_temp)
598 output_dir=self.build_temp)
578 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
599 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
579 self.hgtarget = os.path.join(dir, 'hg')
600 self.hgtarget = os.path.join(dir, 'hg')
580 self.compiler.link_executable(objects, self.hgtarget,
601 self.compiler.link_executable(objects, self.hgtarget,
581 libraries=[],
602 libraries=[],
582 output_dir=self.build_temp)
603 output_dir=self.build_temp)
583 if self.long_paths_support:
604 if self.long_paths_support:
584 self.addlongpathsmanifest()
605 self.addlongpathsmanifest()
585
606
586 def addlongpathsmanifest(self):
607 def addlongpathsmanifest(self):
587 """Add manifest pieces so that hg.exe understands long paths
608 """Add manifest pieces so that hg.exe understands long paths
588
609
589 This is an EXPERIMENTAL feature, use with care.
610 This is an EXPERIMENTAL feature, use with care.
590 To enable long paths support, one needs to do two things:
611 To enable long paths support, one needs to do two things:
591 - build Mercurial with --long-paths-support option
612 - build Mercurial with --long-paths-support option
592 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
613 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
593 LongPathsEnabled to have value 1.
614 LongPathsEnabled to have value 1.
594
615
595 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
616 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
596 it happens because Mercurial uses mt.exe circa 2008, which is not
617 it happens because Mercurial uses mt.exe circa 2008, which is not
597 yet aware of long paths support in the manifest (I think so at least).
618 yet aware of long paths support in the manifest (I think so at least).
598 This does not stop mt.exe from embedding/merging the XML properly.
619 This does not stop mt.exe from embedding/merging the XML properly.
599
620
600 Why resource #1 should be used for .exe manifests? I don't know and
621 Why resource #1 should be used for .exe manifests? I don't know and
601 wasn't able to find an explanation for mortals. But it seems to work.
622 wasn't able to find an explanation for mortals. But it seems to work.
602 """
623 """
603 exefname = self.compiler.executable_filename(self.hgtarget)
624 exefname = self.compiler.executable_filename(self.hgtarget)
604 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
625 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
605 os.close(fdauto)
626 os.close(fdauto)
606 with open(manfname, 'w') as f:
627 with open(manfname, 'w') as f:
607 f.write(self.LONG_PATHS_MANIFEST)
628 f.write(self.LONG_PATHS_MANIFEST)
608 log.info("long paths manifest is written to '%s'" % manfname)
629 log.info("long paths manifest is written to '%s'" % manfname)
609 inputresource = '-inputresource:%s;#1' % exefname
630 inputresource = '-inputresource:%s;#1' % exefname
610 outputresource = '-outputresource:%s;#1' % exefname
631 outputresource = '-outputresource:%s;#1' % exefname
611 log.info("running mt.exe to update hg.exe's manifest in-place")
632 log.info("running mt.exe to update hg.exe's manifest in-place")
612 # supplying both -manifest and -inputresource to mt.exe makes
633 # supplying both -manifest and -inputresource to mt.exe makes
613 # it merge the embedded and supplied manifests in the -outputresource
634 # it merge the embedded and supplied manifests in the -outputresource
614 self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
635 self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
615 inputresource, outputresource])
636 inputresource, outputresource])
616 log.info("done updating hg.exe's manifest")
637 log.info("done updating hg.exe's manifest")
617 os.remove(manfname)
638 os.remove(manfname)
618
639
619 @property
640 @property
620 def hgexepath(self):
641 def hgexepath(self):
621 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
642 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
622 return os.path.join(self.build_temp, dir, 'hg.exe')
643 return os.path.join(self.build_temp, dir, 'hg.exe')
623
644
624 class hginstall(install):
645 class hginstall(install):
625
646
626 user_options = install.user_options + [
647 user_options = install.user_options + [
627 ('old-and-unmanageable', None,
648 ('old-and-unmanageable', None,
628 'noop, present for eggless setuptools compat'),
649 'noop, present for eggless setuptools compat'),
629 ('single-version-externally-managed', None,
650 ('single-version-externally-managed', None,
630 'noop, present for eggless setuptools compat'),
651 'noop, present for eggless setuptools compat'),
631 ]
652 ]
632
653
633 # Also helps setuptools not be sad while we refuse to create eggs.
654 # Also helps setuptools not be sad while we refuse to create eggs.
634 single_version_externally_managed = True
655 single_version_externally_managed = True
635
656
636 def get_sub_commands(self):
657 def get_sub_commands(self):
637 # Screen out egg related commands to prevent egg generation. But allow
658 # Screen out egg related commands to prevent egg generation. But allow
638 # mercurial.egg-info generation, since that is part of modern
659 # mercurial.egg-info generation, since that is part of modern
639 # packaging.
660 # packaging.
640 excl = set(['bdist_egg'])
661 excl = set(['bdist_egg'])
641 return filter(lambda x: x not in excl, install.get_sub_commands(self))
662 return filter(lambda x: x not in excl, install.get_sub_commands(self))
642
663
643 class hginstalllib(install_lib):
664 class hginstalllib(install_lib):
644 '''
665 '''
645 This is a specialization of install_lib that replaces the copy_file used
666 This is a specialization of install_lib that replaces the copy_file used
646 there so that it supports setting the mode of files after copying them,
667 there so that it supports setting the mode of files after copying them,
647 instead of just preserving the mode that the files originally had. If your
668 instead of just preserving the mode that the files originally had. If your
648 system has a umask of something like 027, preserving the permissions when
669 system has a umask of something like 027, preserving the permissions when
649 copying will lead to a broken install.
670 copying will lead to a broken install.
650
671
651 Note that just passing keep_permissions=False to copy_file would be
672 Note that just passing keep_permissions=False to copy_file would be
652 insufficient, as it might still be applying a umask.
673 insufficient, as it might still be applying a umask.
653 '''
674 '''
654
675
655 def run(self):
676 def run(self):
656 realcopyfile = file_util.copy_file
677 realcopyfile = file_util.copy_file
657 def copyfileandsetmode(*args, **kwargs):
678 def copyfileandsetmode(*args, **kwargs):
658 src, dst = args[0], args[1]
679 src, dst = args[0], args[1]
659 dst, copied = realcopyfile(*args, **kwargs)
680 dst, copied = realcopyfile(*args, **kwargs)
660 if copied:
681 if copied:
661 st = os.stat(src)
682 st = os.stat(src)
662 # Persist executable bit (apply it to group and other if user
683 # Persist executable bit (apply it to group and other if user
663 # has it)
684 # has it)
664 if st[stat.ST_MODE] & stat.S_IXUSR:
685 if st[stat.ST_MODE] & stat.S_IXUSR:
665 setmode = int('0755', 8)
686 setmode = int('0755', 8)
666 else:
687 else:
667 setmode = int('0644', 8)
688 setmode = int('0644', 8)
668 m = stat.S_IMODE(st[stat.ST_MODE])
689 m = stat.S_IMODE(st[stat.ST_MODE])
669 m = (m & ~int('0777', 8)) | setmode
690 m = (m & ~int('0777', 8)) | setmode
670 os.chmod(dst, m)
691 os.chmod(dst, m)
671 file_util.copy_file = copyfileandsetmode
692 file_util.copy_file = copyfileandsetmode
672 try:
693 try:
673 install_lib.run(self)
694 install_lib.run(self)
674 finally:
695 finally:
675 file_util.copy_file = realcopyfile
696 file_util.copy_file = realcopyfile
676
697
677 class hginstallscripts(install_scripts):
698 class hginstallscripts(install_scripts):
678 '''
699 '''
679 This is a specialization of install_scripts that replaces the @LIBDIR@ with
700 This is a specialization of install_scripts that replaces the @LIBDIR@ with
680 the configured directory for modules. If possible, the path is made relative
701 the configured directory for modules. If possible, the path is made relative
681 to the directory for scripts.
702 to the directory for scripts.
682 '''
703 '''
683
704
684 def initialize_options(self):
705 def initialize_options(self):
685 install_scripts.initialize_options(self)
706 install_scripts.initialize_options(self)
686
707
687 self.install_lib = None
708 self.install_lib = None
688
709
689 def finalize_options(self):
710 def finalize_options(self):
690 install_scripts.finalize_options(self)
711 install_scripts.finalize_options(self)
691 self.set_undefined_options('install',
712 self.set_undefined_options('install',
692 ('install_lib', 'install_lib'))
713 ('install_lib', 'install_lib'))
693
714
694 def run(self):
715 def run(self):
695 install_scripts.run(self)
716 install_scripts.run(self)
696
717
697 # It only makes sense to replace @LIBDIR@ with the install path if
718 # It only makes sense to replace @LIBDIR@ with the install path if
698 # the install path is known. For wheels, the logic below calculates
719 # the install path is known. For wheels, the logic below calculates
699 # the libdir to be "../..". This is because the internal layout of a
720 # the libdir to be "../..". This is because the internal layout of a
700 # wheel archive looks like:
721 # wheel archive looks like:
701 #
722 #
702 # mercurial-3.6.1.data/scripts/hg
723 # mercurial-3.6.1.data/scripts/hg
703 # mercurial/__init__.py
724 # mercurial/__init__.py
704 #
725 #
705 # When installing wheels, the subdirectories of the "<pkg>.data"
726 # When installing wheels, the subdirectories of the "<pkg>.data"
706 # directory are translated to system local paths and files therein
727 # directory are translated to system local paths and files therein
707 # are copied in place. The mercurial/* files are installed into the
728 # are copied in place. The mercurial/* files are installed into the
708 # site-packages directory. However, the site-packages directory
729 # site-packages directory. However, the site-packages directory
709 # isn't known until wheel install time. This means we have no clue
730 # isn't known until wheel install time. This means we have no clue
710 # at wheel generation time what the installed site-packages directory
731 # at wheel generation time what the installed site-packages directory
711 # will be. And, wheels don't appear to provide the ability to register
732 # will be. And, wheels don't appear to provide the ability to register
712 # custom code to run during wheel installation. This all means that
733 # custom code to run during wheel installation. This all means that
713 # we can't reliably set the libdir in wheels: the default behavior
734 # we can't reliably set the libdir in wheels: the default behavior
714 # of looking in sys.path must do.
735 # of looking in sys.path must do.
715
736
716 if (os.path.splitdrive(self.install_dir)[0] !=
737 if (os.path.splitdrive(self.install_dir)[0] !=
717 os.path.splitdrive(self.install_lib)[0]):
738 os.path.splitdrive(self.install_lib)[0]):
718 # can't make relative paths from one drive to another, so use an
739 # can't make relative paths from one drive to another, so use an
719 # absolute path instead
740 # absolute path instead
720 libdir = self.install_lib
741 libdir = self.install_lib
721 else:
742 else:
722 common = os.path.commonprefix((self.install_dir, self.install_lib))
743 common = os.path.commonprefix((self.install_dir, self.install_lib))
723 rest = self.install_dir[len(common):]
744 rest = self.install_dir[len(common):]
724 uplevel = len([n for n in os.path.split(rest) if n])
745 uplevel = len([n for n in os.path.split(rest) if n])
725
746
726 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
747 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
727
748
728 for outfile in self.outfiles:
749 for outfile in self.outfiles:
729 with open(outfile, 'rb') as fp:
750 with open(outfile, 'rb') as fp:
730 data = fp.read()
751 data = fp.read()
731
752
732 # skip binary files
753 # skip binary files
733 if b'\0' in data:
754 if b'\0' in data:
734 continue
755 continue
735
756
736 # During local installs, the shebang will be rewritten to the final
757 # During local installs, the shebang will be rewritten to the final
737 # install path. During wheel packaging, the shebang has a special
758 # install path. During wheel packaging, the shebang has a special
738 # value.
759 # value.
739 if data.startswith(b'#!python'):
760 if data.startswith(b'#!python'):
740 log.info('not rewriting @LIBDIR@ in %s because install path '
761 log.info('not rewriting @LIBDIR@ in %s because install path '
741 'not known' % outfile)
762 'not known' % outfile)
742 continue
763 continue
743
764
744 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
765 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
745 with open(outfile, 'wb') as fp:
766 with open(outfile, 'wb') as fp:
746 fp.write(data)
767 fp.write(data)
747
768
748 cmdclass = {'build': hgbuild,
769 cmdclass = {'build': hgbuild,
749 'build_mo': hgbuildmo,
770 'build_mo': hgbuildmo,
750 'build_ext': hgbuildext,
771 'build_ext': hgbuildext,
751 'build_py': hgbuildpy,
772 'build_py': hgbuildpy,
752 'build_scripts': hgbuildscripts,
773 'build_scripts': hgbuildscripts,
753 'build_hgextindex': buildhgextindex,
774 'build_hgextindex': buildhgextindex,
754 'install': hginstall,
775 'install': hginstall,
755 'install_lib': hginstalllib,
776 'install_lib': hginstalllib,
756 'install_scripts': hginstallscripts,
777 'install_scripts': hginstallscripts,
757 'build_hgexe': buildhgexe,
778 'build_hgexe': buildhgexe,
758 }
779 }
759
780
760 packages = ['mercurial',
781 packages = ['mercurial',
761 'mercurial.cext',
782 'mercurial.cext',
762 'mercurial.cffi',
783 'mercurial.cffi',
763 'mercurial.hgweb',
784 'mercurial.hgweb',
764 'mercurial.httpclient',
785 'mercurial.httpclient',
765 'mercurial.pure',
786 'mercurial.pure',
766 'mercurial.thirdparty',
787 'mercurial.thirdparty',
767 'mercurial.thirdparty.attr',
788 'mercurial.thirdparty.attr',
768 'hgext', 'hgext.convert', 'hgext.fsmonitor',
789 'hgext', 'hgext.convert', 'hgext.fsmonitor',
769 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
790 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
770 'hgext.largefiles', 'hgext.lfs', 'hgext.zeroconf', 'hgext3rd',
791 'hgext.largefiles', 'hgext.lfs', 'hgext.zeroconf', 'hgext3rd',
771 'hgdemandimport']
792 'hgdemandimport']
772
793
773 common_depends = ['mercurial/bitmanipulation.h',
794 common_depends = ['mercurial/bitmanipulation.h',
774 'mercurial/compat.h',
795 'mercurial/compat.h',
775 'mercurial/cext/util.h']
796 'mercurial/cext/util.h']
776 common_include_dirs = ['mercurial']
797 common_include_dirs = ['mercurial']
777
798
778 osutil_cflags = []
799 osutil_cflags = []
779 osutil_ldflags = []
800 osutil_ldflags = []
780
801
781 # platform specific macros
802 # platform specific macros
782 for plat, func in [('bsd', 'setproctitle')]:
803 for plat, func in [('bsd', 'setproctitle')]:
783 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
804 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
784 osutil_cflags.append('-DHAVE_%s' % func.upper())
805 osutil_cflags.append('-DHAVE_%s' % func.upper())
785
806
786 for plat, macro, code in [
807 for plat, macro, code in [
787 ('bsd|darwin', 'BSD_STATFS', '''
808 ('bsd|darwin', 'BSD_STATFS', '''
788 #include <sys/param.h>
809 #include <sys/param.h>
789 #include <sys/mount.h>
810 #include <sys/mount.h>
790 int main() { struct statfs s; return sizeof(s.f_fstypename); }
811 int main() { struct statfs s; return sizeof(s.f_fstypename); }
791 '''),
812 '''),
792 ('linux', 'LINUX_STATFS', '''
813 ('linux', 'LINUX_STATFS', '''
793 #include <linux/magic.h>
814 #include <linux/magic.h>
794 #include <sys/vfs.h>
815 #include <sys/vfs.h>
795 int main() { struct statfs s; return sizeof(s.f_type); }
816 int main() { struct statfs s; return sizeof(s.f_type); }
796 '''),
817 '''),
797 ]:
818 ]:
798 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
819 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
799 osutil_cflags.append('-DHAVE_%s' % macro)
820 osutil_cflags.append('-DHAVE_%s' % macro)
800
821
801 if sys.platform == 'darwin':
822 if sys.platform == 'darwin':
802 osutil_ldflags += ['-framework', 'ApplicationServices']
823 osutil_ldflags += ['-framework', 'ApplicationServices']
803
824
804 extmodules = [
825 extmodules = [
805 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
826 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
806 include_dirs=common_include_dirs,
827 include_dirs=common_include_dirs,
807 depends=common_depends),
828 depends=common_depends),
808 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
829 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
809 'mercurial/cext/bdiff.c'],
830 'mercurial/cext/bdiff.c'],
810 include_dirs=common_include_dirs,
831 include_dirs=common_include_dirs,
811 depends=common_depends + ['mercurial/bdiff.h']),
832 depends=common_depends + ['mercurial/bdiff.h']),
812 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
833 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
813 include_dirs=common_include_dirs,
834 include_dirs=common_include_dirs,
814 depends=common_depends),
835 depends=common_depends),
815 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
836 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
816 'mercurial/cext/mpatch.c'],
837 'mercurial/cext/mpatch.c'],
817 include_dirs=common_include_dirs,
838 include_dirs=common_include_dirs,
818 depends=common_depends),
839 depends=common_depends),
819 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
840 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
820 'mercurial/cext/dirs.c',
841 'mercurial/cext/dirs.c',
821 'mercurial/cext/manifest.c',
842 'mercurial/cext/manifest.c',
822 'mercurial/cext/parsers.c',
843 'mercurial/cext/parsers.c',
823 'mercurial/cext/pathencode.c',
844 'mercurial/cext/pathencode.c',
824 'mercurial/cext/revlog.c'],
845 'mercurial/cext/revlog.c'],
825 include_dirs=common_include_dirs,
846 include_dirs=common_include_dirs,
826 depends=common_depends + ['mercurial/cext/charencode.h']),
847 depends=common_depends + ['mercurial/cext/charencode.h']),
827 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
848 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
828 include_dirs=common_include_dirs,
849 include_dirs=common_include_dirs,
829 extra_compile_args=osutil_cflags,
850 extra_compile_args=osutil_cflags,
830 extra_link_args=osutil_ldflags,
851 extra_link_args=osutil_ldflags,
831 depends=common_depends),
852 depends=common_depends),
832 Extension('hgext.fsmonitor.pywatchman.bser',
853 Extension('hgext.fsmonitor.pywatchman.bser',
833 ['hgext/fsmonitor/pywatchman/bser.c']),
854 ['hgext/fsmonitor/pywatchman/bser.c']),
834 ]
855 ]
835
856
836 sys.path.insert(0, 'contrib/python-zstandard')
857 sys.path.insert(0, 'contrib/python-zstandard')
837 import setup_zstd
858 import setup_zstd
838 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
859 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
839
860
840 try:
861 try:
841 from distutils import cygwinccompiler
862 from distutils import cygwinccompiler
842
863
843 # the -mno-cygwin option has been deprecated for years
864 # the -mno-cygwin option has been deprecated for years
844 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
865 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
845
866
846 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
867 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
847 def __init__(self, *args, **kwargs):
868 def __init__(self, *args, **kwargs):
848 mingw32compilerclass.__init__(self, *args, **kwargs)
869 mingw32compilerclass.__init__(self, *args, **kwargs)
849 for i in 'compiler compiler_so linker_exe linker_so'.split():
870 for i in 'compiler compiler_so linker_exe linker_so'.split():
850 try:
871 try:
851 getattr(self, i).remove('-mno-cygwin')
872 getattr(self, i).remove('-mno-cygwin')
852 except ValueError:
873 except ValueError:
853 pass
874 pass
854
875
855 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
876 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
856 except ImportError:
877 except ImportError:
857 # the cygwinccompiler package is not available on some Python
878 # the cygwinccompiler package is not available on some Python
858 # distributions like the ones from the optware project for Synology
879 # distributions like the ones from the optware project for Synology
859 # DiskStation boxes
880 # DiskStation boxes
860 class HackedMingw32CCompiler(object):
881 class HackedMingw32CCompiler(object):
861 pass
882 pass
862
883
863 if os.name == 'nt':
884 if os.name == 'nt':
864 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
885 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
865 # extra_link_args to distutils.extensions.Extension() doesn't have any
886 # extra_link_args to distutils.extensions.Extension() doesn't have any
866 # effect.
887 # effect.
867 from distutils import msvccompiler
888 from distutils import msvccompiler
868
889
869 msvccompilerclass = msvccompiler.MSVCCompiler
890 msvccompilerclass = msvccompiler.MSVCCompiler
870
891
871 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
892 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
872 def initialize(self):
893 def initialize(self):
873 msvccompilerclass.initialize(self)
894 msvccompilerclass.initialize(self)
874 # "warning LNK4197: export 'func' specified multiple times"
895 # "warning LNK4197: export 'func' specified multiple times"
875 self.ldflags_shared.append('/ignore:4197')
896 self.ldflags_shared.append('/ignore:4197')
876 self.ldflags_shared_debug.append('/ignore:4197')
897 self.ldflags_shared_debug.append('/ignore:4197')
877
898
878 msvccompiler.MSVCCompiler = HackedMSVCCompiler
899 msvccompiler.MSVCCompiler = HackedMSVCCompiler
879
900
880 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
901 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
881 'help/*.txt',
902 'help/*.txt',
882 'help/internals/*.txt',
903 'help/internals/*.txt',
883 'default.d/*.rc',
904 'default.d/*.rc',
884 'dummycert.pem']}
905 'dummycert.pem']}
885
906
886 def ordinarypath(p):
907 def ordinarypath(p):
887 return p and p[0] != '.' and p[-1] != '~'
908 return p and p[0] != '.' and p[-1] != '~'
888
909
889 for root in ('templates',):
910 for root in ('templates',):
890 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
911 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
891 curdir = curdir.split(os.sep, 1)[1]
912 curdir = curdir.split(os.sep, 1)[1]
892 dirs[:] = filter(ordinarypath, dirs)
913 dirs[:] = filter(ordinarypath, dirs)
893 for f in filter(ordinarypath, files):
914 for f in filter(ordinarypath, files):
894 f = os.path.join(curdir, f)
915 f = os.path.join(curdir, f)
895 packagedata['mercurial'].append(f)
916 packagedata['mercurial'].append(f)
896
917
897 datafiles = []
918 datafiles = []
898
919
899 # distutils expects version to be str/unicode. Converting it to
920 # distutils expects version to be str/unicode. Converting it to
900 # unicode on Python 2 still works because it won't contain any
921 # unicode on Python 2 still works because it won't contain any
901 # non-ascii bytes and will be implicitly converted back to bytes
922 # non-ascii bytes and will be implicitly converted back to bytes
902 # when operated on.
923 # when operated on.
903 assert isinstance(version, bytes)
924 assert isinstance(version, bytes)
904 setupversion = version.decode('ascii')
925 setupversion = version.decode('ascii')
905
926
906 extra = {}
927 extra = {}
907
928
908 if issetuptools:
929 if issetuptools:
909 extra['python_requires'] = supportedpy
930 extra['python_requires'] = supportedpy
910 if py2exeloaded:
931 if py2exeloaded:
911 extra['console'] = [
932 extra['console'] = [
912 {'script':'hg',
933 {'script':'hg',
913 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
934 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
914 'product_version':version}]
935 'product_version':version}]
915 # sub command of 'build' because 'py2exe' does not handle sub_commands
936 # sub command of 'build' because 'py2exe' does not handle sub_commands
916 build.sub_commands.insert(0, ('build_hgextindex', None))
937 build.sub_commands.insert(0, ('build_hgextindex', None))
917 # put dlls in sub directory so that they won't pollute PATH
938 # put dlls in sub directory so that they won't pollute PATH
918 extra['zipfile'] = 'lib/library.zip'
939 extra['zipfile'] = 'lib/library.zip'
919
940
920 if os.name == 'nt':
941 if os.name == 'nt':
921 # Windows binary file versions for exe/dll files must have the
942 # Windows binary file versions for exe/dll files must have the
922 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
943 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
923 setupversion = version.split('+', 1)[0]
944 setupversion = version.split('+', 1)[0]
924
945
925 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
946 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
926 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
947 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
927 if version:
948 if version:
928 version = version[0]
949 version = version[0]
929 if sys.version_info[0] == 3:
950 if sys.version_info[0] == 3:
930 version = version.decode('utf-8')
951 version = version.decode('utf-8')
931 xcode4 = (version.startswith('Xcode') and
952 xcode4 = (version.startswith('Xcode') and
932 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
953 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
933 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
954 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
934 else:
955 else:
935 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
956 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
936 # installed, but instead with only command-line tools. Assume
957 # installed, but instead with only command-line tools. Assume
937 # that only happens on >= Lion, thus no PPC support.
958 # that only happens on >= Lion, thus no PPC support.
938 xcode4 = True
959 xcode4 = True
939 xcode51 = False
960 xcode51 = False
940
961
941 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
962 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
942 # distutils.sysconfig
963 # distutils.sysconfig
943 if xcode4:
964 if xcode4:
944 os.environ['ARCHFLAGS'] = ''
965 os.environ['ARCHFLAGS'] = ''
945
966
946 # XCode 5.1 changes clang such that it now fails to compile if the
967 # XCode 5.1 changes clang such that it now fails to compile if the
947 # -mno-fused-madd flag is passed, but the version of Python shipped with
968 # -mno-fused-madd flag is passed, but the version of Python shipped with
948 # OS X 10.9 Mavericks includes this flag. This causes problems in all
969 # OS X 10.9 Mavericks includes this flag. This causes problems in all
949 # C extension modules, and a bug has been filed upstream at
970 # C extension modules, and a bug has been filed upstream at
950 # http://bugs.python.org/issue21244. We also need to patch this here
971 # http://bugs.python.org/issue21244. We also need to patch this here
951 # so Mercurial can continue to compile in the meantime.
972 # so Mercurial can continue to compile in the meantime.
952 if xcode51:
973 if xcode51:
953 cflags = get_config_var('CFLAGS')
974 cflags = get_config_var('CFLAGS')
954 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
975 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
955 os.environ['CFLAGS'] = (
976 os.environ['CFLAGS'] = (
956 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
977 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
957
978
958 setup(name='mercurial',
979 setup(name='mercurial',
959 version=setupversion,
980 version=setupversion,
960 author='Matt Mackall and many others',
981 author='Matt Mackall and many others',
961 author_email='mercurial@mercurial-scm.org',
982 author_email='mercurial@mercurial-scm.org',
962 url='https://mercurial-scm.org/',
983 url='https://mercurial-scm.org/',
963 download_url='https://mercurial-scm.org/release/',
984 download_url='https://mercurial-scm.org/release/',
964 description=('Fast scalable distributed SCM (revision control, version '
985 description=('Fast scalable distributed SCM (revision control, version '
965 'control) system'),
986 'control) system'),
966 long_description=('Mercurial is a distributed SCM tool written in Python.'
987 long_description=('Mercurial is a distributed SCM tool written in Python.'
967 ' It is used by a number of large projects that require'
988 ' It is used by a number of large projects that require'
968 ' fast, reliable distributed revision control, such as '
989 ' fast, reliable distributed revision control, such as '
969 'Mozilla.'),
990 'Mozilla.'),
970 license='GNU GPLv2 or any later version',
991 license='GNU GPLv2 or any later version',
971 classifiers=[
992 classifiers=[
972 'Development Status :: 6 - Mature',
993 'Development Status :: 6 - Mature',
973 'Environment :: Console',
994 'Environment :: Console',
974 'Intended Audience :: Developers',
995 'Intended Audience :: Developers',
975 'Intended Audience :: System Administrators',
996 'Intended Audience :: System Administrators',
976 'License :: OSI Approved :: GNU General Public License (GPL)',
997 'License :: OSI Approved :: GNU General Public License (GPL)',
977 'Natural Language :: Danish',
998 'Natural Language :: Danish',
978 'Natural Language :: English',
999 'Natural Language :: English',
979 'Natural Language :: German',
1000 'Natural Language :: German',
980 'Natural Language :: Italian',
1001 'Natural Language :: Italian',
981 'Natural Language :: Japanese',
1002 'Natural Language :: Japanese',
982 'Natural Language :: Portuguese (Brazilian)',
1003 'Natural Language :: Portuguese (Brazilian)',
983 'Operating System :: Microsoft :: Windows',
1004 'Operating System :: Microsoft :: Windows',
984 'Operating System :: OS Independent',
1005 'Operating System :: OS Independent',
985 'Operating System :: POSIX',
1006 'Operating System :: POSIX',
986 'Programming Language :: C',
1007 'Programming Language :: C',
987 'Programming Language :: Python',
1008 'Programming Language :: Python',
988 'Topic :: Software Development :: Version Control',
1009 'Topic :: Software Development :: Version Control',
989 ],
1010 ],
990 scripts=scripts,
1011 scripts=scripts,
991 packages=packages,
1012 packages=packages,
992 ext_modules=extmodules,
1013 ext_modules=extmodules,
993 data_files=datafiles,
1014 data_files=datafiles,
994 package_data=packagedata,
1015 package_data=packagedata,
995 cmdclass=cmdclass,
1016 cmdclass=cmdclass,
996 distclass=hgdist,
1017 distclass=hgdist,
997 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
1018 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
998 # implicitly imported per module policy
1019 # implicitly imported per module policy
999 # (cffi wouldn't be used as a frozen exe)
1020 # (cffi wouldn't be used as a frozen exe)
1000 'mercurial.cext',
1021 'mercurial.cext',
1001 #'mercurial.cffi',
1022 #'mercurial.cffi',
1002 'mercurial.pure']},
1023 'mercurial.pure']},
1003 'bdist_mpkg': {'zipdist': False,
1024 'bdist_mpkg': {'zipdist': False,
1004 'license': 'COPYING',
1025 'license': 'COPYING',
1005 'readme': 'contrib/macosx/Readme.html',
1026 'readme': 'contrib/macosx/Readme.html',
1006 'welcome': 'contrib/macosx/Welcome.html',
1027 'welcome': 'contrib/macosx/Welcome.html',
1007 },
1028 },
1008 },
1029 },
1009 **extra)
1030 **extra)
General Comments 0
You need to be logged in to leave comments. Login now