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