##// END OF EJS Templates
completion: install completers to conventional locations...
Matthew Martin -
r49845:8d7eaff9 default
parent child Browse files
Show More
@@ -1,293 +1,283 b''
1 1 # If you want to change PREFIX, do not just edit it below. The changed
2 2 # value wont get passed on to recursive make calls. You should instead
3 3 # override the variable on the command like:
4 4 #
5 5 # % make PREFIX=/opt/ install
6 6
7 7 export PREFIX=/usr/local
8 8
9 9 # Default to Python 3.
10 10 #
11 11 # Windows ships Python 3 as `python.exe`, which may not be on PATH. py.exe is.
12 12 ifeq ($(OS),Windows_NT)
13 13 PYTHON?=py -3
14 14 else
15 15 PYTHON?=python3
16 16 endif
17 17
18 18 PYOXIDIZER?=pyoxidizer
19 19
20 20 $(eval HGROOT := $(shell pwd))
21 21 HGPYTHONS ?= $(HGROOT)/build/pythons
22 22 PURE=
23 23 PYFILESCMD=find mercurial hgext doc -name '*.py'
24 24 PYFILES:=$(shell $(PYFILESCMD))
25 25 DOCFILES=mercurial/helptext/*.txt
26 26 export LANGUAGE=C
27 27 export LC_ALL=C
28 28 TESTFLAGS ?= $(shell echo $$HGTESTFLAGS)
29 29 OSXVERSIONFLAGS ?= $(shell echo $$OSXVERSIONFLAGS)
30 30 CARGO = cargo
31 31
32 32 # Set this to e.g. "mingw32" to use a non-default compiler.
33 33 COMPILER=
34 34
35 35 COMPILERFLAG_tmp_ =
36 36 COMPILERFLAG_tmp_${COMPILER} ?= -c $(COMPILER)
37 37 COMPILERFLAG=${COMPILERFLAG_tmp_${COMPILER}}
38 38
39 39 help:
40 40 @echo 'Commonly used make targets:'
41 41 @echo ' all - build program and documentation'
42 42 @echo ' install - install program and man pages to $$PREFIX ($(PREFIX))'
43 43 @echo ' install-home - install with setup.py install --home=$$HOME ($(HOME))'
44 44 @echo ' local - build for inplace usage'
45 45 @echo ' tests - run all tests in the automatic test suite'
46 46 @echo ' test-foo - run only specified tests (e.g. test-merge1.t)'
47 47 @echo ' dist - run all tests and create a source tarball in dist/'
48 48 @echo ' clean - remove files created by other targets'
49 49 @echo ' (except installed files or dist source tarball)'
50 50 @echo ' update-pot - update i18n/hg.pot'
51 51 @echo
52 52 @echo 'Example for a system-wide installation under /usr/local:'
53 53 @echo ' make all && su -c "make install" && hg version'
54 54 @echo
55 55 @echo 'Example for a local installation (usable in this directory):'
56 56 @echo ' make local && ./hg version'
57 57
58 58 all: build doc
59 59
60 60 local:
61 61 $(PYTHON) setup.py $(PURE) \
62 62 build_py -c -d . \
63 63 build_ext $(COMPILERFLAG) -i \
64 64 build_hgexe $(COMPILERFLAG) -i \
65 65 build_mo
66 66 env HGRCPATH= $(PYTHON) hg version
67 67
68 68 build:
69 69 $(PYTHON) setup.py $(PURE) build $(COMPILERFLAG)
70 70
71 71 build-chg:
72 72 make -C contrib/chg
73 73
74 74 build-rhg:
75 75 (cd rust/rhg; cargo build --release)
76 76
77 77 wheel:
78 78 FORCE_SETUPTOOLS=1 $(PYTHON) setup.py $(PURE) bdist_wheel $(COMPILERFLAG)
79 79
80 80 doc:
81 81 $(MAKE) -C doc
82 82
83 83 cleanbutpackages:
84 84 rm -f hg.exe
85 85 -$(PYTHON) setup.py clean --all # ignore errors from this command
86 86 find contrib doc hgext hgext3rd i18n mercurial tests hgdemandimport \
87 87 \( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';'
88 88 rm -f MANIFEST MANIFEST.in hgext/__index__.py tests/*.err
89 89 rm -f mercurial/__modulepolicy__.py
90 90 if test -d .hg; then rm -f mercurial/__version__.py; fi
91 91 rm -rf build mercurial/locale
92 92 $(MAKE) -C doc clean
93 93 $(MAKE) -C contrib/chg distclean
94 94 rm -rf rust/target
95 95 rm -f mercurial/rustext.so
96 96
97 97 clean: cleanbutpackages
98 98 rm -rf packages
99 99
100 100 install: install-bin install-doc
101 101
102 102 install-bin: build
103 103 $(PYTHON) setup.py $(PURE) install --root="$(DESTDIR)/" --prefix="$(PREFIX)" --force
104 104
105 105 install-chg: build-chg
106 106 make -C contrib/chg install PREFIX="$(PREFIX)"
107 107
108 108 install-doc: doc
109 109 cd doc && $(MAKE) $(MFLAGS) install
110 110
111 111 install-home: install-home-bin install-home-doc
112 112
113 113 install-home-bin: build
114 114 $(PYTHON) setup.py $(PURE) install --home="$(HOME)" --prefix="" --force
115 115
116 116 install-home-doc: doc
117 117 cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install
118 118
119 119 install-rhg: build-rhg
120 120 install -m 755 rust/target/release/rhg "$(PREFIX)"/bin/
121 121
122 122 MANIFEST-doc:
123 123 $(MAKE) -C doc MANIFEST
124 124
125 125 MANIFEST.in: MANIFEST-doc
126 126 hg manifest | sed -e 's/^/include /' > MANIFEST.in
127 127 echo include mercurial/__version__.py >> MANIFEST.in
128 128 sed -e 's/^/include /' < doc/MANIFEST >> MANIFEST.in
129 129
130 130 dist: tests dist-notests
131 131
132 132 dist-notests: doc MANIFEST.in
133 133 TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist
134 134
135 135 check: tests
136 136
137 137 tests:
138 138 # Run Rust tests if cargo is installed
139 139 if command -v $(CARGO) >/dev/null 2>&1; then \
140 140 $(MAKE) rust-tests; \
141 141 fi
142 142 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS)
143 143
144 144 test-%:
145 145 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@
146 146
147 147 testpy-%:
148 148 @echo Looking for Python $* in $(HGPYTHONS)
149 149 [ -e $(HGPYTHONS)/$*/bin/python ] || ( \
150 150 cd $$(mktemp --directory --tmpdir) && \
151 151 $(MAKE) -f $(HGROOT)/contrib/Makefile.python PYTHONVER=$* PREFIX=$(HGPYTHONS)/$* python )
152 152 cd tests && $(HGPYTHONS)/$*/bin/python run-tests.py $(TESTFLAGS)
153 153
154 154 rust-tests:
155 155 cd $(HGROOT)/rust/hg-cpython \
156 156 && $(CARGO) test --quiet --all --features "$(HG_RUST_FEATURES)"
157 157
158 158 check-code:
159 159 hg manifest | xargs python contrib/check-code.py
160 160
161 161 format-c:
162 162 clang-format --style file -i \
163 163 `hg files 'set:(**.c or **.cc or **.h) and not "listfile:contrib/clang-format-ignorelist"'`
164 164
165 165 update-pot: i18n/hg.pot
166 166
167 167 i18n/hg.pot: $(PYFILES) $(DOCFILES) i18n/posplit i18n/hggettext
168 168 $(PYTHON) i18n/hggettext mercurial/commands.py \
169 169 hgext/*.py hgext/*/__init__.py \
170 170 mercurial/fileset.py mercurial/revset.py \
171 171 mercurial/templatefilters.py \
172 172 mercurial/templatefuncs.py \
173 173 mercurial/templatekw.py \
174 174 mercurial/filemerge.py \
175 175 mercurial/hgweb/webcommands.py \
176 176 mercurial/util.py \
177 177 $(DOCFILES) > i18n/hg.pot.tmp
178 178 # All strings marked for translation in Mercurial contain
179 179 # ASCII characters only. But some files contain string
180 180 # literals like this '\037\213'. xgettext thinks it has to
181 181 # parse them even though they are not marked for translation.
182 182 # Extracting with an explicit encoding of ISO-8859-1 will make
183 183 # xgettext "parse" and ignore them.
184 184 $(PYFILESCMD) | xargs \
185 185 xgettext --package-name "Mercurial" \
186 186 --msgid-bugs-address "<mercurial-devel@mercurial-scm.org>" \
187 187 --copyright-holder "Olivia Mackall <olivia@selenic.com> and others" \
188 188 --from-code ISO-8859-1 --join --sort-by-file --add-comments=i18n: \
189 189 -d hg -p i18n -o hg.pot.tmp
190 190 $(PYTHON) i18n/posplit i18n/hg.pot.tmp
191 191 # The target file is not created before the last step. So it never is in
192 192 # an intermediate state.
193 193 mv -f i18n/hg.pot.tmp i18n/hg.pot
194 194
195 195 %.po: i18n/hg.pot
196 196 # work on a temporary copy for never having a half completed target
197 197 cp $@ $@.tmp
198 198 msgmerge --no-location --update $@.tmp $^
199 199 mv -f $@.tmp $@
200 200
201 201 # Packaging targets
202 202
203 203 packaging_targets := \
204 204 rhel7 \
205 205 rhel8 \
206 206 deb \
207 207 docker-rhel7 \
208 208 docker-rhel8 \
209 209 docker-debian-bullseye \
210 210 docker-debian-buster \
211 211 docker-debian-stretch \
212 212 docker-fedora \
213 213 docker-ubuntu-xenial \
214 214 docker-ubuntu-xenial-ppa \
215 215 docker-ubuntu-bionic \
216 216 docker-ubuntu-bionic-ppa \
217 217 docker-ubuntu-focal \
218 218 docker-ubuntu-focal-ppa \
219 219 fedora \
220 220 linux-wheels \
221 221 linux-wheels-x86_64 \
222 222 linux-wheels-i686 \
223 223 ppa
224 224
225 225 # Forward packaging targets for convenience.
226 226 $(packaging_targets):
227 227 $(MAKE) -C contrib/packaging $@
228 228
229 229 osx:
230 230 rm -rf build/mercurial
231 231 /usr/bin/python2.7 setup.py install --optimize=1 \
232 232 --root=build/mercurial/ --prefix=/usr/local/ \
233 233 --install-lib=/Library/Python/2.7/site-packages/
234 234 make -C doc all install DESTDIR="$(PWD)/build/mercurial/"
235 235 # Place a bogon .DS_Store file in the target dir so we can be
236 236 # sure it doesn't get included in the final package.
237 237 touch build/mercurial/.DS_Store
238 # install zsh completions - this location appears to be
239 # searched by default as of macOS Sierra.
240 install -d build/mercurial/usr/local/share/zsh/site-functions/
241 install -m 0644 contrib/zsh_completion build/mercurial/usr/local/share/zsh/site-functions/_hg
242 # install bash completions - there doesn't appear to be a
243 # place that's searched by default for bash, so we'll follow
244 # the lead of Apple's git install and just put it in a
245 # location of our own.
246 install -d build/mercurial/usr/local/hg/contrib/
247 install -m 0644 contrib/bash_completion build/mercurial/usr/local/hg/contrib/hg-completion.bash
248 238 make -C contrib/chg \
249 239 HGPATH=/usr/local/bin/hg \
250 240 PYTHON=/usr/bin/python2.7 \
251 241 DESTDIR=../../build/mercurial \
252 242 PREFIX=/usr/local \
253 243 clean install
254 244 mkdir -p $${OUTPUTDIR:-dist}
255 245 HGVER=$$(python contrib/genosxversion.py $(OSXVERSIONFLAGS) build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py) && \
256 246 OSXVER=$$(sw_vers -productVersion | cut -d. -f1,2) && \
257 247 pkgbuild --filter \\.DS_Store --root build/mercurial/ \
258 248 --identifier org.mercurial-scm.mercurial \
259 249 --version "$${HGVER}" \
260 250 build/mercurial.pkg && \
261 251 productbuild --distribution contrib/packaging/macosx/distribution.xml \
262 252 --package-path build/ \
263 253 --version "$${HGVER}" \
264 254 --resources contrib/packaging/macosx/ \
265 255 "$${OUTPUTDIR:-dist/}"/Mercurial-"$${HGVER}"-macosx"$${OSXVER}".pkg
266 256
267 257 pyoxidizer:
268 258 $(PYOXIDIZER) build --path ./rust/hgcli --release
269 259
270 260
271 261 PYOX_DIR=build/pyoxidizer/x86_64-pc-windows-msvc/release/app
272 262
273 263 # a temporary target to setup all we need for run-tests.py --pyoxidizer
274 264 # (should go away as the run-tests implementation improves
275 265 pyoxidizer-windows-tests: pyoxidizer
276 266 rm -rf $(PYOX_DIR)/templates
277 267 cp -ar $(PYOX_DIR)/lib/mercurial/templates $(PYOX_DIR)/templates
278 268 rm -rf $(PYOX_DIR)/helptext
279 269 cp -ar $(PYOX_DIR)/lib/mercurial/helptext $(PYOX_DIR)/helptext
280 270 rm -rf $(PYOX_DIR)/defaultrc
281 271 cp -ar $(PYOX_DIR)/lib/mercurial/defaultrc $(PYOX_DIR)/defaultrc
282 272 rm -rf $(PYOX_DIR)/contrib
283 273 cp -ar contrib $(PYOX_DIR)/contrib
284 274 rm -rf $(PYOX_DIR)/doc
285 275 cp -ar doc $(PYOX_DIR)/doc
286 276
287 277
288 278 .PHONY: help all local build doc cleanbutpackages clean install install-bin \
289 279 install-doc install-home install-home-bin install-home-doc \
290 280 dist dist-notests check tests rust-tests check-code format-c \
291 281 update-pot pyoxidizer pyoxidizer-windows-tests \
292 282 $(packaging_targets) \
293 283 osx
@@ -1,103 +1,101 b''
1 1 #!/usr/bin/make -f
2 2 # Uncomment this to turn on verbose mode.
3 3 # export DH_VERBOSE=1
4 4
5 5 # By default we build a .deb where the native components are built with the
6 6 # current "default" version of py3 on the build machine. If you wish to build a
7 7 # .deb that has native components built for multiple versions of py3:
8 8 #
9 9 # 1. install python3.x and python3.x-dev for each version you want
10 10 # 2. set DEB_HG_MULTI_VERSION=1 or DEB_HG_PYTHON_VERSIONS in your environment
11 11 # (if both are set, DEB_HG_PYTHON_VERSIONS has precedence)
12 12 #
13 13 # If you choose `DEB_HG_MULTI_VERSION=1`, it will build for every "supported"
14 14 # version of py3 that's installed on the build machine. This may not be equal to
15 15 # the actual versions that are installed, see the comment above where we set
16 16 # DEB_HG_PYTHON_VERSIONS below. If you choose to set `DEB_HG_PYTHON_VERSIONS`
17 17 # yourself, set it to a space-separated string of python version numbers, like:
18 18 # DEB_HG_PYTHON_VERSIONS="3.7 3.8" make deb
19 19 DEB_HG_MULTI_VERSION?=0
20 20
21 21 # Set to 1 to make /usr/bin/hg a symlink to chg, and move hg to
22 22 # /usr/lib/mercurial/hg.
23 23 DEB_HG_CHG_BY_DEFAULT?=0
24 24
25 25 CPUS=$(shell cat /proc/cpuinfo | grep -E ^processor | wc -l)
26 26
27 27 # By default, only build for the version of python3 that the system considers
28 28 # the 'default' (which should be the one invoked by just running 'python3'
29 29 # without a minor version). If DEB_HG_PYTHON_VERSIONS is set, this is ignored.
30 30 ifeq ($(DEB_HG_MULTI_VERSION), 1)
31 31 # If we're building for multiple versions, use all of the "supported" versions
32 32 # on the build machine. Note: the mechanism in use here (`py3versions`) is the
33 33 # recommended one, but it relies on a file written by the python3-minimal
34 34 # package, and this file is not dynamic and does not account for manual
35 35 # installations, just the ones that would be installed by `python3-all`. This
36 36 # includes the `-i` flag, which claims it's to list all "installed" versions,
37 37 # but it doesn't. This was quite confusing, hence this tale of woe. :)
38 38 DEB_HG_PYTHON_VERSIONS?=$(shell py3versions -vs)
39 39 else
40 40 # If we're building for only one version, identify the "default" version on
41 41 # the build machine and use that when building; this is just so that we don't
42 42 # have to duplicate the rules below for multi-version vs. single-version. The
43 43 # shebang line will still be /usr/bin/python3 (no minor version).
44 44 DEB_HG_PYTHON_VERSIONS?=$(shell py3versions -vd)
45 45 endif
46 46
47 47 ifeq ($(DEB_HG_CHG_BY_DEFAULT), 1)
48 48 # Important: the "real" hg must have a 'basename' of 'hg'. Otherwise, hg
49 49 # behaves differently when setting $HG and breaks aliases that use that.
50 50 export HGPATH=/usr/lib/mercurial/hg
51 51 endif
52 52
53 53 export HGPYTHON3=1
54 54 export PYTHON=python3
55 55
56 56 %:
57 57 dh $@ --with python3
58 58
59 59 # Note: testing can be disabled using the standard `DEB_BUILD_OPTIONS=nocheck`
60 60 override_dh_auto_test:
61 61 http_proxy='' dh_auto_test -- TESTFLAGS="-j$(CPUS)"
62 62
63 63 override_dh_python3:
64 64 dh_python3 --shebang=/usr/bin/python3
65 65
66 66 override_dh_auto_clean:
67 67 $(MAKE) cleanbutpackages
68 68 $(MAKE) -C contrib/chg clean
69 69
70 70 override_dh_auto_build:
71 71 $(MAKE) all
72 72 $(MAKE) -C contrib/chg all
73 73
74 74 # Build the native extensions for a specfic python3 version (which must be
75 75 # installed on the build machine).
76 76 install-python%:
77 77 python$* setup.py install --root "$(CURDIR)"/debian/mercurial --install-layout=deb
78 78
79 79 # Build the final package. This rule has a dependencies section that causes the
80 80 # native extensions to be compiled for every version of python3 listed in
81 81 # DEB_HG_PYTHON_VERSIONS.
82 82 override_dh_auto_install: $(DEB_HG_PYTHON_VERSIONS:%=install-python%)
83 83 # chg
84 84 make -C contrib/chg \
85 85 DESTDIR="$(CURDIR)"/debian/mercurial \
86 86 PREFIX=/usr \
87 87 install
88 88 make install-doc PREFIX="$(CURDIR)"/debian/mercurial/usr
89 89 cp contrib/hg-ssh "$(CURDIR)"/debian/mercurial/usr/bin
90 90 mkdir -p "$(CURDIR)"/debian/mercurial/usr/share/mercurial
91 91 cp contrib/hgk "$(CURDIR)"/debian/mercurial/usr/share/mercurial
92 92 mkdir -p "$(CURDIR)"/debian/mercurial/etc/mercurial/hgrc.d/
93 93 cp contrib/packaging/debian/*.rc "$(CURDIR)"/debian/mercurial/etc/mercurial/hgrc.d/
94 94 # completions
95 mkdir -p "$(CURDIR)"/debian/mercurial/usr/share/bash-completion/completions
96 cp contrib/bash_completion "$(CURDIR)"/debian/mercurial/usr/share/bash-completion/completions/hg
97 95 mkdir -p "$(CURDIR)"/debian/mercurial/usr/share/zsh/vendor-completions
98 cp contrib/zsh_completion "$(CURDIR)"/debian/mercurial/usr/share/zsh/vendor-completions/_hg
96 mv "$(CURDIR)"/debian/mercurial/usr/share/zsh/site-functions/_hg "$(CURDIR)"/debian/mercurial/usr/share/zsh/vendor-completions/_hg
99 97 if [ "$(DEB_HG_CHG_BY_DEFAULT)" -eq 1 ]; then \
100 98 mkdir -p "$(CURDIR)"/debian/mercurial/usr/lib/mercurial; \
101 99 mv "$(CURDIR)"/debian/mercurial/usr/bin/hg "$(CURDIR)"/debian/mercurial/usr/lib/mercurial/hg; \
102 100 ln -s chg "$(CURDIR)"/debian/mercurial/usr/bin/hg; \
103 101 fi
@@ -1,175 +1,173 b''
1 1 # pyoxidizer.py - Packaging support for PyOxidizer
2 2 #
3 3 # Copyright 2020 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # no-check-code because Python 3 native.
9 9
10 10 import os
11 11 import pathlib
12 12 import shutil
13 13 import subprocess
14 14 import sys
15 15 import typing
16 16
17 17 from .downloads import download_entry
18 18 from .util import (
19 19 extract_zip_to_directory,
20 20 process_install_rules,
21 21 find_vc_runtime_dll,
22 22 )
23 23
24 24
25 25 STAGING_RULES_WINDOWS = [
26 ('contrib/bash_completion', 'contrib/'),
27 26 ('contrib/hgk', 'contrib/hgk.tcl'),
28 27 ('contrib/hgweb.fcgi', 'contrib/'),
29 28 ('contrib/hgweb.wsgi', 'contrib/'),
30 29 ('contrib/logo-droplets.svg', 'contrib/'),
31 30 ('contrib/mercurial.el', 'contrib/'),
32 31 ('contrib/mq.el', 'contrib/'),
33 32 ('contrib/tcsh_completion', 'contrib/'),
34 33 ('contrib/tcsh_completion_build.sh', 'contrib/'),
35 34 ('contrib/vim/*', 'contrib/vim/'),
36 35 ('contrib/win32/postinstall.txt', 'ReleaseNotes.txt'),
37 36 ('contrib/win32/ReadMe.html', 'ReadMe.html'),
38 37 ('contrib/xml.rnc', 'contrib/'),
39 ('contrib/zsh_completion', 'contrib/'),
40 38 ('doc/*.html', 'doc/'),
41 39 ('doc/style.css', 'doc/'),
42 40 ('COPYING', 'Copying.txt'),
43 41 ]
44 42
45 43 STAGING_RULES_APP = [
46 44 ('lib/mercurial/helptext/**/*.txt', 'helptext/'),
47 45 ('lib/mercurial/defaultrc/*.rc', 'defaultrc/'),
48 46 ('lib/mercurial/locale/**/*', 'locale/'),
49 47 ('lib/mercurial/templates/**/*', 'templates/'),
50 48 ]
51 49
52 50 STAGING_EXCLUDES_WINDOWS = [
53 51 "doc/hg-ssh.8.html",
54 52 ]
55 53
56 54
57 55 def build_docs_html(source_dir: pathlib.Path):
58 56 """Ensures HTML documentation is built.
59 57
60 58 This will fail if docutils isn't available.
61 59
62 60 (The HTML docs aren't built as part of `pip install` so we need to build them
63 61 out of band.)
64 62 """
65 63 subprocess.run(
66 64 [sys.executable, str(source_dir / "setup.py"), "build_doc", "--html"],
67 65 cwd=str(source_dir),
68 66 check=True,
69 67 )
70 68
71 69
72 70 def run_pyoxidizer(
73 71 source_dir: pathlib.Path,
74 72 build_dir: pathlib.Path,
75 73 target_triple: str,
76 74 build_vars: typing.Optional[typing.Dict[str, str]] = None,
77 75 target: typing.Optional[str] = None,
78 76 ) -> pathlib.Path:
79 77 """Run `pyoxidizer` in an environment with access to build dependencies.
80 78
81 79 Returns the output directory that pyoxidizer would have used for build
82 80 artifacts. Actual build artifacts are likely in a sub-directory with the
83 81 name of the pyoxidizer build target that was built.
84 82 """
85 83 build_vars = build_vars or {}
86 84
87 85 # We need to make gettext binaries available for compiling i18n files.
88 86 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
89 87 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
90 88
91 89 gettext_root = build_dir / ('gettext-win-%s' % gettext_entry['version'])
92 90
93 91 if not gettext_root.exists():
94 92 extract_zip_to_directory(gettext_pkg, gettext_root)
95 93 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
96 94
97 95 env = dict(os.environ)
98 96 env["PATH"] = "%s%s%s" % (
99 97 env["PATH"],
100 98 os.pathsep,
101 99 str(gettext_root / "bin"),
102 100 )
103 101
104 102 args = [
105 103 "pyoxidizer",
106 104 "build",
107 105 "--path",
108 106 str(source_dir / "rust" / "hgcli"),
109 107 "--release",
110 108 "--target-triple",
111 109 target_triple,
112 110 ]
113 111
114 112 for k, v in sorted(build_vars.items()):
115 113 args.extend(["--var", k, v])
116 114
117 115 if target:
118 116 args.append(target)
119 117
120 118 subprocess.run(args, env=env, check=True)
121 119
122 120 return source_dir / "build" / "pyoxidizer" / target_triple / "release"
123 121
124 122
125 123 def create_pyoxidizer_install_layout(
126 124 source_dir: pathlib.Path,
127 125 build_dir: pathlib.Path,
128 126 out_dir: pathlib.Path,
129 127 target_triple: str,
130 128 ):
131 129 """Build Mercurial with PyOxidizer and copy additional files into place.
132 130
133 131 After successful completion, ``out_dir`` contains files constituting a
134 132 Mercurial install.
135 133 """
136 134
137 135 run_pyoxidizer(source_dir, build_dir, target_triple)
138 136
139 137 build_dir = (
140 138 source_dir / "build" / "pyoxidizer" / target_triple / "release" / "app"
141 139 )
142 140
143 141 if out_dir.exists():
144 142 print("purging %s" % out_dir)
145 143 shutil.rmtree(out_dir)
146 144
147 145 # Now assemble all the files from PyOxidizer into the staging directory.
148 146 shutil.copytree(build_dir, out_dir)
149 147
150 148 # Move some of those files around. We can get rid of this once Mercurial
151 149 # is taught to use the importlib APIs for reading resources.
152 150 process_install_rules(STAGING_RULES_APP, build_dir, out_dir)
153 151
154 152 build_docs_html(source_dir)
155 153
156 154 if "windows" in target_triple:
157 155 process_install_rules(STAGING_RULES_WINDOWS, source_dir, out_dir)
158 156
159 157 # Write out a default editor.rc file to configure notepad as the
160 158 # default editor.
161 159 os.makedirs(out_dir / "defaultrc", exist_ok=True)
162 160 with (out_dir / "defaultrc" / "editor.rc").open(
163 161 "w", encoding="utf-8"
164 162 ) as fh:
165 163 fh.write("[ui]\neditor = notepad\n")
166 164
167 165 for f in STAGING_EXCLUDES_WINDOWS:
168 166 p = out_dir / f
169 167 if p.exists():
170 168 print("removing %s" % p)
171 169 p.unlink()
172 170
173 171 # Add vcruntimeXXX.dll next to executable.
174 172 vc_runtime_dll = find_vc_runtime_dll(x64="x86_64" in target_triple)
175 173 shutil.copy(vc_runtime_dll, out_dir / vc_runtime_dll.name)
@@ -1,174 +1,167 b''
1 1 %global emacs_lispdir %{_datadir}/emacs/site-lisp
2 2
3 3 %define withpython %{nil}
4 4
5 5 %global pythonexe python3
6 6 %global pythondocutils python3-docutils
7 7
8 8 %if "%{?withpython}"
9 9
10 10 %global pythonver %{withpython}
11 11 %global pythonname Python-%{withpython}
12 12 %global docutilsname docutils-0.14
13 13 %global docutilsmd5 c53768d63db3873b7d452833553469de
14 14 %global pythonhg python-hg
15 15 %global hgpyprefix /opt/%{pythonhg}
16 16 # byte compilation will fail on some some Python /test/ files
17 17 %global _python_bytecompile_errors_terminate_build 0
18 18
19 19 %else
20 20
21 21 %global pythonver %(%{pythonexe} -c 'import sys;print(".".join(map(str, sys.version_info[:2])))')
22 22
23 23 %endif
24 24
25 25 Summary: A fast, lightweight Source Control Management system
26 26 Name: mercurial
27 27 Version: snapshot
28 28 Release: 0
29 29 License: GPLv2+
30 30 Prefix: /
31 31 Group: Development/Tools
32 32 URL: https://mercurial-scm.org/
33 33 Source0: %{name}-%{version}-%{release}.tar.gz
34 34 %if "%{?withpython}"
35 35 Source1: %{pythonname}.tgz
36 36 Source2: %{docutilsname}.tar.gz
37 37 %endif
38 38 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
39 39
40 40 BuildRequires: make, gcc, gettext
41 41 %if "%{?withpython}"
42 42 BuildRequires: readline-devel, openssl-devel, ncurses-devel, zlib-devel, bzip2-devel
43 43 %else
44 44 BuildRequires: %{pythonexe} >= %{pythonver}, %{pythonexe}-devel, %{pythondocutils}
45 45 Requires: %{pythonexe} >= %{pythonver}
46 46 %endif
47 47 # The hgk extension uses the wish tcl interpreter, but we don't enforce it
48 48 #Requires: tk
49 49
50 50 %description
51 51 Mercurial is a fast, lightweight source control management system designed
52 52 for efficient handling of very large distributed projects.
53 53
54 54 %prep
55 55
56 56 %if "%{?withpython}"
57 57 %setup -q -n mercurial-%{version}-%{release} -a1 -a2
58 58 # despite the comments in cgi.py, we do this to prevent rpmdeps from picking /usr/local/bin/python up
59 59 sed -i '1c#! /usr/bin/env %{pythonexe}' %{pythonname}/Lib/cgi.py
60 60 %else
61 61 %setup -q -n mercurial-%{version}-%{release}
62 62 %endif
63 63
64 64 %build
65 65
66 66 export HGPYTHON3=1
67 67
68 68 %if "%{?withpython}"
69 69
70 70 PYPATH=$PWD/%{pythonname}
71 71 cd $PYPATH
72 72 ./configure --prefix=%{hgpyprefix}
73 73 make all %{?_smp_mflags}
74 74 cd -
75 75
76 76 cd %{docutilsname}
77 77 LD_LIBRARY_PATH=$PYPATH $PYPATH/python setup.py build
78 78 cd -
79 79
80 80 # verify Python environment
81 81 LD_LIBRARY_PATH=$PYPATH PYTHONPATH=$PWD/%{docutilsname} $PYPATH/python -c 'import sys, zlib, bz2, ssl, curses, readline'
82 82
83 83 # set environment for make
84 84 export PATH=$PYPATH:$PATH
85 85 export LD_LIBRARY_PATH=$PYPATH
86 86 export CFLAGS="-L $PYPATH"
87 87 export PYTHONPATH=$PWD/%{docutilsname}
88 88
89 89 %endif
90 90
91 91 make all PYTHON=%{pythonexe}
92 92 make -C contrib/chg
93 93
94 94 sed -i -e '1s|#!/usr/bin/env python$|#!/usr/bin/env %{pythonexe}|' contrib/hg-ssh
95 95
96 96 %install
97 97 rm -rf $RPM_BUILD_ROOT
98 98
99 99 export HGPYTHON3=1
100 100
101 101 %if "%{?withpython}"
102 102
103 103 PYPATH=$PWD/%{pythonname}
104 104 cd $PYPATH
105 105 make install DESTDIR=$RPM_BUILD_ROOT
106 106 # these .a are not necessary and they are readonly and strip fails - kill them!
107 107 rm -f %{buildroot}%{hgpyprefix}/lib/{,python2.*/config}/libpython2.*.a
108 108 cd -
109 109
110 110 cd %{docutilsname}
111 111 LD_LIBRARY_PATH=$PYPATH $PYPATH/python setup.py install --root="$RPM_BUILD_ROOT"
112 112 cd -
113 113
114 114 PATH=$PYPATH:$PATH LD_LIBRARY_PATH=$PYPATH make install PYTHON=%{pythonexe} DESTDIR=$RPM_BUILD_ROOT PREFIX=%{hgpyprefix} MANDIR=%{_mandir} PURE="--rust"
115 115 mkdir -p $RPM_BUILD_ROOT%{_bindir}
116 116 ( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/hg . )
117 117 ( cd $RPM_BUILD_ROOT%{_bindir}/ && ln -s ../..%{hgpyprefix}/bin/python2.? %{pythonhg} )
118 118
119 119 %else
120 120
121 121 make install PYTHON=%{pythonexe} DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix} MANDIR=%{_mandir} PURE="--rust"
122 122
123 123 %endif
124 124
125 125 install -m 755 contrib/chg/chg $RPM_BUILD_ROOT%{_bindir}/
126 126 install -m 755 contrib/hgk $RPM_BUILD_ROOT%{_bindir}/
127 127 install -m 755 contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir}/
128 128
129 bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
130 mkdir -p $bash_completion_dir
131 install -m 644 contrib/bash_completion $bash_completion_dir/mercurial.sh
132
133 zsh_completion_dir=$RPM_BUILD_ROOT%{_datadir}/zsh/site-functions
134 mkdir -p $zsh_completion_dir
135 install -m 644 contrib/zsh_completion $zsh_completion_dir/_mercurial
136
137 129 mkdir -p $RPM_BUILD_ROOT%{emacs_lispdir}
138 130 install -m 644 contrib/mercurial.el $RPM_BUILD_ROOT%{emacs_lispdir}/
139 131 install -m 644 contrib/mq.el $RPM_BUILD_ROOT%{emacs_lispdir}/
140 132
141 133 mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/mercurial/hgrc.d
142 134
143 135 %clean
144 136 rm -rf $RPM_BUILD_ROOT
145 137
146 138 %files
147 139 %defattr(-,root,root,-)
148 140 %doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html *.cgi contrib/*.fcgi contrib/*.wsgi
149 141 %doc %attr(644,root,root) %{_mandir}/man?/hg*
150 142 %doc %attr(644,root,root) contrib/*.svg
143 %dir %{_datadir}/bash-completion/
144 %dir %{_datadir}/bash-completion/completions
145 %{_datadir}/bash-completion/completions/hg
151 146 %dir %{_datadir}/zsh/
152 147 %dir %{_datadir}/zsh/site-functions/
153 %{_datadir}/zsh/site-functions/_mercurial
148 %{_datadir}/zsh/site-functions/_hg
154 149 %dir %{_datadir}/emacs/site-lisp/
155 150 %{_datadir}/emacs/site-lisp/mercurial.el
156 151 %{_datadir}/emacs/site-lisp/mq.el
157 152 %{_bindir}/hg
158 153 %{_bindir}/chg
159 154 %{_bindir}/hgk
160 155 %{_bindir}/hg-ssh
161 %dir %{_sysconfdir}/bash_completion.d/
162 %config(noreplace) %{_sysconfdir}/bash_completion.d/mercurial.sh
163 156 %dir %{_sysconfdir}/mercurial
164 157 %dir %{_sysconfdir}/mercurial/hgrc.d
165 158 %if "%{?withpython}"
166 159 %{_bindir}/%{pythonhg}
167 160 %{hgpyprefix}
168 161 %else
169 162 %{_libdir}/python%{pythonver}/site-packages/%{name}-*-py%{pythonver}.egg-info
170 163 %{_libdir}/python%{pythonver}/site-packages/%{name}
171 164 %{_libdir}/python%{pythonver}/site-packages/hgext
172 165 %{_libdir}/python%{pythonver}/site-packages/hgext3rd
173 166 %{_libdir}/python%{pythonver}/site-packages/hgdemandimport
174 167 %endif
@@ -1,1744 +1,1776 b''
1 1 #
2 2 # This is the mercurial setup script.
3 3 #
4 4 # 'python setup.py install', or
5 5 # 'python setup.py --help' for more options
6 6 import os
7 7
8 8 # Mercurial can't work on 3.6.0 or 3.6.1 due to a bug in % formatting
9 9 # in bytestrings.
10 10 supportedpy = ','.join(
11 11 [
12 12 '>=3.6.2',
13 13 ]
14 14 )
15 15
16 16 import sys, platform
17 17 import sysconfig
18 18
19 19
20 20 def sysstr(s):
21 21 return s.decode('latin-1')
22 22
23 23
24 24 import ssl
25 25
26 26 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
27 27 # 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
28 28 # (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
29 29 # were defined only if compiled against a OpenSSL version with TLS 1.1 / 1.2
30 30 # support. At the mentioned commit, they were unconditionally defined.
31 31 _notset = object()
32 32 has_tlsv1_1 = getattr(ssl, 'HAS_TLSv1_1', _notset)
33 33 if has_tlsv1_1 is _notset:
34 34 has_tlsv1_1 = getattr(ssl, 'PROTOCOL_TLSv1_1', _notset) is not _notset
35 35 has_tlsv1_2 = getattr(ssl, 'HAS_TLSv1_2', _notset)
36 36 if has_tlsv1_2 is _notset:
37 37 has_tlsv1_2 = getattr(ssl, 'PROTOCOL_TLSv1_2', _notset) is not _notset
38 38 if not (has_tlsv1_1 or has_tlsv1_2):
39 39 error = """
40 40 The `ssl` module does not advertise support for TLS 1.1 or TLS 1.2.
41 41 Please make sure that your Python installation was compiled against an OpenSSL
42 42 version enabling these features (likely this requires the OpenSSL version to
43 43 be at least 1.0.1).
44 44 """
45 45 print(error, file=sys.stderr)
46 46 sys.exit(1)
47 47
48 48 DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
49 49
50 50 # Solaris Python packaging brain damage
51 51 try:
52 52 import hashlib
53 53
54 54 sha = hashlib.sha1()
55 55 except ImportError:
56 56 try:
57 57 import sha
58 58
59 59 sha.sha # silence unused import warning
60 60 except ImportError:
61 61 raise SystemExit(
62 62 "Couldn't import standard hashlib (incomplete Python install)."
63 63 )
64 64
65 65 try:
66 66 import zlib
67 67
68 68 zlib.compressobj # silence unused import warning
69 69 except ImportError:
70 70 raise SystemExit(
71 71 "Couldn't import standard zlib (incomplete Python install)."
72 72 )
73 73
74 74 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
75 75 isironpython = False
76 76 try:
77 77 isironpython = (
78 78 platform.python_implementation().lower().find("ironpython") != -1
79 79 )
80 80 except AttributeError:
81 81 pass
82 82
83 83 if isironpython:
84 84 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
85 85 else:
86 86 try:
87 87 import bz2
88 88
89 89 bz2.BZ2Compressor # silence unused import warning
90 90 except ImportError:
91 91 raise SystemExit(
92 92 "Couldn't import standard bz2 (incomplete Python install)."
93 93 )
94 94
95 95 ispypy = "PyPy" in sys.version
96 96
97 97 import ctypes
98 98 import errno
99 99 import stat, subprocess, time
100 100 import re
101 101 import shutil
102 102 import tempfile
103 103
104 104 # We have issues with setuptools on some platforms and builders. Until
105 105 # those are resolved, setuptools is opt-in except for platforms where
106 106 # we don't have issues.
107 107 issetuptools = os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ
108 108 if issetuptools:
109 109 from setuptools import setup
110 110 else:
111 111 from distutils.core import setup
112 112 from distutils.ccompiler import new_compiler
113 113 from distutils.core import Command, Extension
114 114 from distutils.dist import Distribution
115 115 from distutils.command.build import build
116 116 from distutils.command.build_ext import build_ext
117 117 from distutils.command.build_py import build_py
118 118 from distutils.command.build_scripts import build_scripts
119 119 from distutils.command.install import install
120 120 from distutils.command.install_lib import install_lib
121 121 from distutils.command.install_scripts import install_scripts
122 122 from distutils import log
123 123 from distutils.spawn import spawn, find_executable
124 124 from distutils import file_util
125 125 from distutils.errors import (
126 126 CCompilerError,
127 127 DistutilsError,
128 128 DistutilsExecError,
129 129 )
130 130 from distutils.sysconfig import get_python_inc, get_config_var
131 131 from distutils.version import StrictVersion
132 132
133 133 # Explain to distutils.StrictVersion how our release candidates are versioned
134 134 StrictVersion.version_re = re.compile(r'^(\d+)\.(\d+)(\.(\d+))?-?(rc(\d+))?$')
135 135
136 136
137 137 def write_if_changed(path, content):
138 138 """Write content to a file iff the content hasn't changed."""
139 139 if os.path.exists(path):
140 140 with open(path, 'rb') as fh:
141 141 current = fh.read()
142 142 else:
143 143 current = b''
144 144
145 145 if current != content:
146 146 with open(path, 'wb') as fh:
147 147 fh.write(content)
148 148
149 149
150 150 scripts = ['hg']
151 151 if os.name == 'nt':
152 152 # We remove hg.bat if we are able to build hg.exe.
153 153 scripts.append('contrib/win32/hg.bat')
154 154
155 155
156 156 def cancompile(cc, code):
157 157 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
158 158 devnull = oldstderr = None
159 159 try:
160 160 fname = os.path.join(tmpdir, 'testcomp.c')
161 161 f = open(fname, 'w')
162 162 f.write(code)
163 163 f.close()
164 164 # Redirect stderr to /dev/null to hide any error messages
165 165 # from the compiler.
166 166 # This will have to be changed if we ever have to check
167 167 # for a function on Windows.
168 168 devnull = open('/dev/null', 'w')
169 169 oldstderr = os.dup(sys.stderr.fileno())
170 170 os.dup2(devnull.fileno(), sys.stderr.fileno())
171 171 objects = cc.compile([fname], output_dir=tmpdir)
172 172 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
173 173 return True
174 174 except Exception:
175 175 return False
176 176 finally:
177 177 if oldstderr is not None:
178 178 os.dup2(oldstderr, sys.stderr.fileno())
179 179 if devnull is not None:
180 180 devnull.close()
181 181 shutil.rmtree(tmpdir)
182 182
183 183
184 184 # simplified version of distutils.ccompiler.CCompiler.has_function
185 185 # that actually removes its temporary files.
186 186 def hasfunction(cc, funcname):
187 187 code = 'int main(void) { %s(); }\n' % funcname
188 188 return cancompile(cc, code)
189 189
190 190
191 191 def hasheader(cc, headername):
192 192 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
193 193 return cancompile(cc, code)
194 194
195 195
196 196 # py2exe needs to be installed to work
197 197 try:
198 198 import py2exe
199 199
200 200 py2exe.Distribution # silence unused import warning
201 201 py2exeloaded = True
202 202 # import py2exe's patched Distribution class
203 203 from distutils.core import Distribution
204 204 except ImportError:
205 205 py2exeloaded = False
206 206
207 207
208 208 def runcmd(cmd, env, cwd=None):
209 209 p = subprocess.Popen(
210 210 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd
211 211 )
212 212 out, err = p.communicate()
213 213 return p.returncode, out, err
214 214
215 215
216 216 class hgcommand:
217 217 def __init__(self, cmd, env):
218 218 self.cmd = cmd
219 219 self.env = env
220 220
221 221 def run(self, args):
222 222 cmd = self.cmd + args
223 223 returncode, out, err = runcmd(cmd, self.env)
224 224 err = filterhgerr(err)
225 225 if err or returncode != 0:
226 226 print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
227 227 print(err, file=sys.stderr)
228 228 return b''
229 229 return out
230 230
231 231
232 232 def filterhgerr(err):
233 233 # If root is executing setup.py, but the repository is owned by
234 234 # another user (as in "sudo python setup.py install") we will get
235 235 # trust warnings since the .hg/hgrc file is untrusted. That is
236 236 # fine, we don't want to load it anyway. Python may warn about
237 237 # a missing __init__.py in mercurial/locale, we also ignore that.
238 238 err = [
239 239 e
240 240 for e in err.splitlines()
241 241 if (
242 242 not e.startswith(b'not trusting file')
243 243 and not e.startswith(b'warning: Not importing')
244 244 and not e.startswith(b'obsolete feature not enabled')
245 245 and not e.startswith(b'*** failed to import extension')
246 246 and not e.startswith(b'devel-warn:')
247 247 and not (
248 248 e.startswith(b'(third party extension')
249 249 and e.endswith(b'or newer of Mercurial; disabling)')
250 250 )
251 251 )
252 252 ]
253 253 return b'\n'.join(b' ' + e for e in err)
254 254
255 255
256 256 def findhg():
257 257 """Try to figure out how we should invoke hg for examining the local
258 258 repository contents.
259 259
260 260 Returns an hgcommand object."""
261 261 # By default, prefer the "hg" command in the user's path. This was
262 262 # presumably the hg command that the user used to create this repository.
263 263 #
264 264 # This repository may require extensions or other settings that would not
265 265 # be enabled by running the hg script directly from this local repository.
266 266 hgenv = os.environ.copy()
267 267 # Use HGPLAIN to disable hgrc settings that would change output formatting,
268 268 # and disable localization for the same reasons.
269 269 hgenv['HGPLAIN'] = '1'
270 270 hgenv['LANGUAGE'] = 'C'
271 271 hgcmd = ['hg']
272 272 # Run a simple "hg log" command just to see if using hg from the user's
273 273 # path works and can successfully interact with this repository. Windows
274 274 # gives precedence to hg.exe in the current directory, so fall back to the
275 275 # python invocation of local hg, where pythonXY.dll can always be found.
276 276 check_cmd = ['log', '-r.', '-Ttest']
277 277 if os.name != 'nt' or not os.path.exists("hg.exe"):
278 278 try:
279 279 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
280 280 except EnvironmentError:
281 281 retcode = -1
282 282 if retcode == 0 and not filterhgerr(err):
283 283 return hgcommand(hgcmd, hgenv)
284 284
285 285 # Fall back to trying the local hg installation.
286 286 hgenv = localhgenv()
287 287 hgcmd = [sys.executable, 'hg']
288 288 try:
289 289 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
290 290 except EnvironmentError:
291 291 retcode = -1
292 292 if retcode == 0 and not filterhgerr(err):
293 293 return hgcommand(hgcmd, hgenv)
294 294
295 295 raise SystemExit(
296 296 'Unable to find a working hg binary to extract the '
297 297 'version from the repository tags'
298 298 )
299 299
300 300
301 301 def localhgenv():
302 302 """Get an environment dictionary to use for invoking or importing
303 303 mercurial from the local repository."""
304 304 # Execute hg out of this directory with a custom environment which takes
305 305 # care to not use any hgrc files and do no localization.
306 306 env = {
307 307 'HGMODULEPOLICY': 'py',
308 308 'HGRCPATH': '',
309 309 'LANGUAGE': 'C',
310 310 'PATH': '',
311 311 } # make pypi modules that use os.environ['PATH'] happy
312 312 if 'LD_LIBRARY_PATH' in os.environ:
313 313 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
314 314 if 'SystemRoot' in os.environ:
315 315 # SystemRoot is required by Windows to load various DLLs. See:
316 316 # https://bugs.python.org/issue13524#msg148850
317 317 env['SystemRoot'] = os.environ['SystemRoot']
318 318 return env
319 319
320 320
321 321 version = ''
322 322
323 323 if os.path.isdir('.hg'):
324 324 hg = findhg()
325 325 cmd = ['log', '-r', '.', '--template', '{tags}\n']
326 326 numerictags = [t for t in sysstr(hg.run(cmd)).split() if t[0:1].isdigit()]
327 327 hgid = sysstr(hg.run(['id', '-i'])).strip()
328 328 if not hgid:
329 329 # Bail out if hg is having problems interacting with this repository,
330 330 # rather than falling through and producing a bogus version number.
331 331 # Continuing with an invalid version number will break extensions
332 332 # that define minimumhgversion.
333 333 raise SystemExit('Unable to determine hg version from local repository')
334 334 if numerictags: # tag(s) found
335 335 version = numerictags[-1]
336 336 if hgid.endswith('+'): # propagate the dirty status to the tag
337 337 version += '+'
338 338 else: # no tag found
339 339 ltagcmd = ['parents', '--template', '{latesttag}']
340 340 ltag = sysstr(hg.run(ltagcmd))
341 341 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
342 342 changessince = len(hg.run(changessincecmd).splitlines())
343 343 version = '%s+hg%s.%s' % (ltag, changessince, hgid)
344 344 if version.endswith('+'):
345 345 version = version[:-1] + 'local' + time.strftime('%Y%m%d')
346 346 elif os.path.exists('.hg_archival.txt'):
347 347 kw = dict(
348 348 [[t.strip() for t in l.split(':', 1)] for l in open('.hg_archival.txt')]
349 349 )
350 350 if 'tag' in kw:
351 351 version = kw['tag']
352 352 elif 'latesttag' in kw:
353 353 if 'changessincelatesttag' in kw:
354 354 version = (
355 355 '%(latesttag)s+hg%(changessincelatesttag)s.%(node).12s' % kw
356 356 )
357 357 else:
358 358 version = '%(latesttag)s+hg%(latesttagdistance)s.%(node).12s' % kw
359 359 else:
360 360 version = '0+hg' + kw.get('node', '')[:12]
361 361 elif os.path.exists('mercurial/__version__.py'):
362 362 with open('mercurial/__version__.py') as f:
363 363 data = f.read()
364 364 version = re.search('version = b"(.*)"', data).group(1)
365 365
366 366 if version:
367 367 versionb = version
368 368 if not isinstance(versionb, bytes):
369 369 versionb = versionb.encode('ascii')
370 370
371 371 write_if_changed(
372 372 'mercurial/__version__.py',
373 373 b''.join(
374 374 [
375 375 b'# this file is autogenerated by setup.py\n'
376 376 b'version = b"%s"\n' % versionb,
377 377 ]
378 378 ),
379 379 )
380 380
381 381
382 382 class hgbuild(build):
383 383 # Insert hgbuildmo first so that files in mercurial/locale/ are found
384 384 # when build_py is run next.
385 385 sub_commands = [('build_mo', None)] + build.sub_commands
386 386
387 387
388 388 class hgbuildmo(build):
389 389
390 390 description = "build translations (.mo files)"
391 391
392 392 def run(self):
393 393 if not find_executable('msgfmt'):
394 394 self.warn(
395 395 "could not find msgfmt executable, no translations "
396 396 "will be built"
397 397 )
398 398 return
399 399
400 400 podir = 'i18n'
401 401 if not os.path.isdir(podir):
402 402 self.warn("could not find %s/ directory" % podir)
403 403 return
404 404
405 405 join = os.path.join
406 406 for po in os.listdir(podir):
407 407 if not po.endswith('.po'):
408 408 continue
409 409 pofile = join(podir, po)
410 410 modir = join('locale', po[:-3], 'LC_MESSAGES')
411 411 mofile = join(modir, 'hg.mo')
412 412 mobuildfile = join('mercurial', mofile)
413 413 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
414 414 if sys.platform != 'sunos5':
415 415 # msgfmt on Solaris does not know about -c
416 416 cmd.append('-c')
417 417 self.mkpath(join('mercurial', modir))
418 418 self.make_file([pofile], mobuildfile, spawn, (cmd,))
419 419
420 420
421 421 class hgdist(Distribution):
422 422 pure = False
423 423 rust = False
424 424 no_rust = False
425 425 cffi = ispypy
426 426
427 427 global_options = Distribution.global_options + [
428 428 ('pure', None, "use pure (slow) Python code instead of C extensions"),
429 429 ('rust', None, "use Rust extensions additionally to C extensions"),
430 430 (
431 431 'no-rust',
432 432 None,
433 433 "do not use Rust extensions additionally to C extensions",
434 434 ),
435 435 ]
436 436
437 437 negative_opt = Distribution.negative_opt.copy()
438 438 boolean_options = ['pure', 'rust', 'no-rust']
439 439 negative_opt['no-rust'] = 'rust'
440 440
441 441 def _set_command_options(self, command_obj, option_dict=None):
442 442 # Not all distutils versions in the wild have boolean_options.
443 443 # This should be cleaned up when we're Python 3 only.
444 444 command_obj.boolean_options = (
445 445 getattr(command_obj, 'boolean_options', []) + self.boolean_options
446 446 )
447 447 return Distribution._set_command_options(
448 448 self, command_obj, option_dict=option_dict
449 449 )
450 450
451 451 def parse_command_line(self):
452 452 ret = Distribution.parse_command_line(self)
453 453 if not (self.rust or self.no_rust):
454 454 hgrustext = os.environ.get('HGWITHRUSTEXT')
455 455 # TODO record it for proper rebuild upon changes
456 456 # (see mercurial/__modulepolicy__.py)
457 457 if hgrustext != 'cpython' and hgrustext is not None:
458 458 if hgrustext:
459 459 msg = 'unknown HGWITHRUSTEXT value: %s' % hgrustext
460 460 print(msg, file=sys.stderr)
461 461 hgrustext = None
462 462 self.rust = hgrustext is not None
463 463 self.no_rust = not self.rust
464 464 return ret
465 465
466 466 def has_ext_modules(self):
467 467 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
468 468 # too late for some cases
469 469 return not self.pure and Distribution.has_ext_modules(self)
470 470
471 471
472 472 # This is ugly as a one-liner. So use a variable.
473 473 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
474 474 buildextnegops['no-zstd'] = 'zstd'
475 475 buildextnegops['no-rust'] = 'rust'
476 476
477 477
478 478 class hgbuildext(build_ext):
479 479 user_options = build_ext.user_options + [
480 480 ('zstd', None, 'compile zstd bindings [default]'),
481 481 ('no-zstd', None, 'do not compile zstd bindings'),
482 482 (
483 483 'rust',
484 484 None,
485 485 'compile Rust extensions if they are in use '
486 486 '(requires Cargo) [default]',
487 487 ),
488 488 ('no-rust', None, 'do not compile Rust extensions'),
489 489 ]
490 490
491 491 boolean_options = build_ext.boolean_options + ['zstd', 'rust']
492 492 negative_opt = buildextnegops
493 493
494 494 def initialize_options(self):
495 495 self.zstd = True
496 496 self.rust = True
497 497
498 498 return build_ext.initialize_options(self)
499 499
500 500 def finalize_options(self):
501 501 # Unless overridden by the end user, build extensions in parallel.
502 502 # Only influences behavior on Python 3.5+.
503 503 if getattr(self, 'parallel', None) is None:
504 504 self.parallel = True
505 505
506 506 return build_ext.finalize_options(self)
507 507
508 508 def build_extensions(self):
509 509 ruststandalones = [
510 510 e for e in self.extensions if isinstance(e, RustStandaloneExtension)
511 511 ]
512 512 self.extensions = [
513 513 e for e in self.extensions if e not in ruststandalones
514 514 ]
515 515 # Filter out zstd if disabled via argument.
516 516 if not self.zstd:
517 517 self.extensions = [
518 518 e for e in self.extensions if e.name != 'mercurial.zstd'
519 519 ]
520 520
521 521 # Build Rust standalone extensions if it'll be used
522 522 # and its build is not explicitly disabled (for external build
523 523 # as Linux distributions would do)
524 524 if self.distribution.rust and self.rust:
525 525 if not sys.platform.startswith('linux'):
526 526 self.warn(
527 527 "rust extensions have only been tested on Linux "
528 528 "and may not behave correctly on other platforms"
529 529 )
530 530
531 531 for rustext in ruststandalones:
532 532 rustext.build('' if self.inplace else self.build_lib)
533 533
534 534 return build_ext.build_extensions(self)
535 535
536 536 def build_extension(self, ext):
537 537 if (
538 538 self.distribution.rust
539 539 and self.rust
540 540 and isinstance(ext, RustExtension)
541 541 ):
542 542 ext.rustbuild()
543 543 try:
544 544 build_ext.build_extension(self, ext)
545 545 except CCompilerError:
546 546 if not getattr(ext, 'optional', False):
547 547 raise
548 548 log.warn(
549 549 "Failed to build optional extension '%s' (skipping)", ext.name
550 550 )
551 551
552 552
553 553 class hgbuildscripts(build_scripts):
554 554 def run(self):
555 555 if os.name != 'nt' or self.distribution.pure:
556 556 return build_scripts.run(self)
557 557
558 558 exebuilt = False
559 559 try:
560 560 self.run_command('build_hgexe')
561 561 exebuilt = True
562 562 except (DistutilsError, CCompilerError):
563 563 log.warn('failed to build optional hg.exe')
564 564
565 565 if exebuilt:
566 566 # Copying hg.exe to the scripts build directory ensures it is
567 567 # installed by the install_scripts command.
568 568 hgexecommand = self.get_finalized_command('build_hgexe')
569 569 dest = os.path.join(self.build_dir, 'hg.exe')
570 570 self.mkpath(self.build_dir)
571 571 self.copy_file(hgexecommand.hgexepath, dest)
572 572
573 573 # Remove hg.bat because it is redundant with hg.exe.
574 574 self.scripts.remove('contrib/win32/hg.bat')
575 575
576 576 return build_scripts.run(self)
577 577
578 578
579 579 class hgbuildpy(build_py):
580 580 def finalize_options(self):
581 581 build_py.finalize_options(self)
582 582
583 583 if self.distribution.pure:
584 584 self.distribution.ext_modules = []
585 585 elif self.distribution.cffi:
586 586 from mercurial.cffi import (
587 587 bdiffbuild,
588 588 mpatchbuild,
589 589 )
590 590
591 591 exts = [
592 592 mpatchbuild.ffi.distutils_extension(),
593 593 bdiffbuild.ffi.distutils_extension(),
594 594 ]
595 595 # cffi modules go here
596 596 if sys.platform == 'darwin':
597 597 from mercurial.cffi import osutilbuild
598 598
599 599 exts.append(osutilbuild.ffi.distutils_extension())
600 600 self.distribution.ext_modules = exts
601 601 else:
602 602 h = os.path.join(get_python_inc(), 'Python.h')
603 603 if not os.path.exists(h):
604 604 raise SystemExit(
605 605 'Python headers are required to build '
606 606 'Mercurial but weren\'t found in %s' % h
607 607 )
608 608
609 609 def run(self):
610 610 basepath = os.path.join(self.build_lib, 'mercurial')
611 611 self.mkpath(basepath)
612 612
613 613 rust = self.distribution.rust
614 614 if self.distribution.pure:
615 615 modulepolicy = 'py'
616 616 elif self.build_lib == '.':
617 617 # in-place build should run without rebuilding and Rust extensions
618 618 modulepolicy = 'rust+c-allow' if rust else 'allow'
619 619 else:
620 620 modulepolicy = 'rust+c' if rust else 'c'
621 621
622 622 content = b''.join(
623 623 [
624 624 b'# this file is autogenerated by setup.py\n',
625 625 b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
626 626 ]
627 627 )
628 628 write_if_changed(os.path.join(basepath, '__modulepolicy__.py'), content)
629 629
630 630 build_py.run(self)
631 631
632 632
633 633 class buildhgextindex(Command):
634 634 description = 'generate prebuilt index of hgext (for frozen package)'
635 635 user_options = []
636 636 _indexfilename = 'hgext/__index__.py'
637 637
638 638 def initialize_options(self):
639 639 pass
640 640
641 641 def finalize_options(self):
642 642 pass
643 643
644 644 def run(self):
645 645 if os.path.exists(self._indexfilename):
646 646 with open(self._indexfilename, 'w') as f:
647 647 f.write('# empty\n')
648 648
649 649 # here no extension enabled, disabled() lists up everything
650 650 code = (
651 651 'import pprint; from mercurial import extensions; '
652 652 'ext = extensions.disabled();'
653 653 'ext.pop("__index__", None);'
654 654 'pprint.pprint(ext)'
655 655 )
656 656 returncode, out, err = runcmd(
657 657 [sys.executable, '-c', code], localhgenv()
658 658 )
659 659 if err or returncode != 0:
660 660 raise DistutilsExecError(err)
661 661
662 662 with open(self._indexfilename, 'wb') as f:
663 663 f.write(b'# this file is autogenerated by setup.py\n')
664 664 f.write(b'docs = ')
665 665 f.write(out)
666 666
667 667
668 668 class buildhgexe(build_ext):
669 669 description = 'compile hg.exe from mercurial/exewrapper.c'
670 670 user_options = build_ext.user_options + [
671 671 (
672 672 'long-paths-support',
673 673 None,
674 674 'enable support for long paths on '
675 675 'Windows (off by default and '
676 676 'experimental)',
677 677 ),
678 678 ]
679 679
680 680 LONG_PATHS_MANIFEST = """
681 681 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
682 682 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
683 683 <application>
684 684 <windowsSettings
685 685 xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
686 686 <ws2:longPathAware>true</ws2:longPathAware>
687 687 </windowsSettings>
688 688 </application>
689 689 </assembly>"""
690 690
691 691 def initialize_options(self):
692 692 build_ext.initialize_options(self)
693 693 self.long_paths_support = False
694 694
695 695 def build_extensions(self):
696 696 if os.name != 'nt':
697 697 return
698 698 if isinstance(self.compiler, HackedMingw32CCompiler):
699 699 self.compiler.compiler_so = self.compiler.compiler # no -mdll
700 700 self.compiler.dll_libraries = [] # no -lmsrvc90
701 701
702 702 pythonlib = None
703 703
704 704 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
705 705 self.hgtarget = os.path.join(dir, 'hg')
706 706
707 707 if getattr(sys, 'dllhandle', None):
708 708 # Different Python installs can have different Python library
709 709 # names. e.g. the official CPython distribution uses pythonXY.dll
710 710 # and MinGW uses libpythonX.Y.dll.
711 711 _kernel32 = ctypes.windll.kernel32
712 712 _kernel32.GetModuleFileNameA.argtypes = [
713 713 ctypes.c_void_p,
714 714 ctypes.c_void_p,
715 715 ctypes.c_ulong,
716 716 ]
717 717 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
718 718 size = 1000
719 719 buf = ctypes.create_string_buffer(size + 1)
720 720 filelen = _kernel32.GetModuleFileNameA(
721 721 sys.dllhandle, ctypes.byref(buf), size
722 722 )
723 723
724 724 if filelen > 0 and filelen != size:
725 725 dllbasename = os.path.basename(buf.value)
726 726 if not dllbasename.lower().endswith(b'.dll'):
727 727 raise SystemExit(
728 728 'Python DLL does not end with .dll: %s' % dllbasename
729 729 )
730 730 pythonlib = dllbasename[:-4]
731 731
732 732 # Copy the pythonXY.dll next to the binary so that it runs
733 733 # without tampering with PATH.
734 734 dest = os.path.join(
735 735 os.path.dirname(self.hgtarget),
736 736 os.fsdecode(dllbasename),
737 737 )
738 738
739 739 if not os.path.exists(dest):
740 740 shutil.copy(buf.value, dest)
741 741
742 742 # Also overwrite python3.dll so that hgext.git is usable.
743 743 # TODO: also handle the MSYS flavor
744 744 python_x = os.path.join(
745 745 os.path.dirname(os.fsdecode(buf.value)),
746 746 "python3.dll",
747 747 )
748 748
749 749 if os.path.exists(python_x):
750 750 dest = os.path.join(
751 751 os.path.dirname(self.hgtarget),
752 752 os.path.basename(python_x),
753 753 )
754 754
755 755 shutil.copy(python_x, dest)
756 756
757 757 if not pythonlib:
758 758 log.warn(
759 759 'could not determine Python DLL filename; assuming pythonXY'
760 760 )
761 761
762 762 hv = sys.hexversion
763 763 pythonlib = b'python%d%d' % (hv >> 24, (hv >> 16) & 0xFF)
764 764
765 765 log.info('using %s as Python library name' % pythonlib)
766 766 with open('mercurial/hgpythonlib.h', 'wb') as f:
767 767 f.write(b'/* this file is autogenerated by setup.py */\n')
768 768 f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
769 769
770 770 objects = self.compiler.compile(
771 771 ['mercurial/exewrapper.c'],
772 772 output_dir=self.build_temp,
773 773 macros=[('_UNICODE', None), ('UNICODE', None)],
774 774 )
775 775 self.compiler.link_executable(
776 776 objects, self.hgtarget, libraries=[], output_dir=self.build_temp
777 777 )
778 778 if self.long_paths_support:
779 779 self.addlongpathsmanifest()
780 780
781 781 def addlongpathsmanifest(self):
782 782 r"""Add manifest pieces so that hg.exe understands long paths
783 783
784 784 This is an EXPERIMENTAL feature, use with care.
785 785 To enable long paths support, one needs to do two things:
786 786 - build Mercurial with --long-paths-support option
787 787 - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
788 788 LongPathsEnabled to have value 1.
789 789
790 790 Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
791 791 it happens because Mercurial uses mt.exe circa 2008, which is not
792 792 yet aware of long paths support in the manifest (I think so at least).
793 793 This does not stop mt.exe from embedding/merging the XML properly.
794 794
795 795 Why resource #1 should be used for .exe manifests? I don't know and
796 796 wasn't able to find an explanation for mortals. But it seems to work.
797 797 """
798 798 exefname = self.compiler.executable_filename(self.hgtarget)
799 799 fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
800 800 os.close(fdauto)
801 801 with open(manfname, 'w') as f:
802 802 f.write(self.LONG_PATHS_MANIFEST)
803 803 log.info("long paths manifest is written to '%s'" % manfname)
804 804 inputresource = '-inputresource:%s;#1' % exefname
805 805 outputresource = '-outputresource:%s;#1' % exefname
806 806 log.info("running mt.exe to update hg.exe's manifest in-place")
807 807 # supplying both -manifest and -inputresource to mt.exe makes
808 808 # it merge the embedded and supplied manifests in the -outputresource
809 809 self.spawn(
810 810 [
811 811 'mt.exe',
812 812 '-nologo',
813 813 '-manifest',
814 814 manfname,
815 815 inputresource,
816 816 outputresource,
817 817 ]
818 818 )
819 819 log.info("done updating hg.exe's manifest")
820 820 os.remove(manfname)
821 821
822 822 @property
823 823 def hgexepath(self):
824 824 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
825 825 return os.path.join(self.build_temp, dir, 'hg.exe')
826 826
827 827
828 828 class hgbuilddoc(Command):
829 829 description = 'build documentation'
830 830 user_options = [
831 831 ('man', None, 'generate man pages'),
832 832 ('html', None, 'generate html pages'),
833 833 ]
834 834
835 835 def initialize_options(self):
836 836 self.man = None
837 837 self.html = None
838 838
839 839 def finalize_options(self):
840 840 # If --man or --html are set, only generate what we're told to.
841 841 # Otherwise generate everything.
842 842 have_subset = self.man is not None or self.html is not None
843 843
844 844 if have_subset:
845 845 self.man = True if self.man else False
846 846 self.html = True if self.html else False
847 847 else:
848 848 self.man = True
849 849 self.html = True
850 850
851 851 def run(self):
852 852 def normalizecrlf(p):
853 853 with open(p, 'rb') as fh:
854 854 orig = fh.read()
855 855
856 856 if b'\r\n' not in orig:
857 857 return
858 858
859 859 log.info('normalizing %s to LF line endings' % p)
860 860 with open(p, 'wb') as fh:
861 861 fh.write(orig.replace(b'\r\n', b'\n'))
862 862
863 863 def gentxt(root):
864 864 txt = 'doc/%s.txt' % root
865 865 log.info('generating %s' % txt)
866 866 res, out, err = runcmd(
867 867 [sys.executable, 'gendoc.py', root], os.environ, cwd='doc'
868 868 )
869 869 if res:
870 870 raise SystemExit(
871 871 'error running gendoc.py: %s'
872 872 % '\n'.join([sysstr(out), sysstr(err)])
873 873 )
874 874
875 875 with open(txt, 'wb') as fh:
876 876 fh.write(out)
877 877
878 878 def gengendoc(root):
879 879 gendoc = 'doc/%s.gendoc.txt' % root
880 880
881 881 log.info('generating %s' % gendoc)
882 882 res, out, err = runcmd(
883 883 [sys.executable, 'gendoc.py', '%s.gendoc' % root],
884 884 os.environ,
885 885 cwd='doc',
886 886 )
887 887 if res:
888 888 raise SystemExit(
889 889 'error running gendoc: %s'
890 890 % '\n'.join([sysstr(out), sysstr(err)])
891 891 )
892 892
893 893 with open(gendoc, 'wb') as fh:
894 894 fh.write(out)
895 895
896 896 def genman(root):
897 897 log.info('generating doc/%s' % root)
898 898 res, out, err = runcmd(
899 899 [
900 900 sys.executable,
901 901 'runrst',
902 902 'hgmanpage',
903 903 '--halt',
904 904 'warning',
905 905 '--strip-elements-with-class',
906 906 'htmlonly',
907 907 '%s.txt' % root,
908 908 root,
909 909 ],
910 910 os.environ,
911 911 cwd='doc',
912 912 )
913 913 if res:
914 914 raise SystemExit(
915 915 'error running runrst: %s'
916 916 % '\n'.join([sysstr(out), sysstr(err)])
917 917 )
918 918
919 919 normalizecrlf('doc/%s' % root)
920 920
921 921 def genhtml(root):
922 922 log.info('generating doc/%s.html' % root)
923 923 res, out, err = runcmd(
924 924 [
925 925 sys.executable,
926 926 'runrst',
927 927 'html',
928 928 '--halt',
929 929 'warning',
930 930 '--link-stylesheet',
931 931 '--stylesheet-path',
932 932 'style.css',
933 933 '%s.txt' % root,
934 934 '%s.html' % root,
935 935 ],
936 936 os.environ,
937 937 cwd='doc',
938 938 )
939 939 if res:
940 940 raise SystemExit(
941 941 'error running runrst: %s'
942 942 % '\n'.join([sysstr(out), sysstr(err)])
943 943 )
944 944
945 945 normalizecrlf('doc/%s.html' % root)
946 946
947 947 # This logic is duplicated in doc/Makefile.
948 948 sources = {
949 949 f
950 950 for f in os.listdir('mercurial/helptext')
951 951 if re.search(r'[0-9]\.txt$', f)
952 952 }
953 953
954 954 # common.txt is a one-off.
955 955 gentxt('common')
956 956
957 957 for source in sorted(sources):
958 958 assert source[-4:] == '.txt'
959 959 root = source[:-4]
960 960
961 961 gentxt(root)
962 962 gengendoc(root)
963 963
964 964 if self.man:
965 965 genman(root)
966 966 if self.html:
967 967 genhtml(root)
968 968
969 969
970 970 class hginstall(install):
971 971
972 972 user_options = install.user_options + [
973 973 (
974 974 'old-and-unmanageable',
975 975 None,
976 976 'noop, present for eggless setuptools compat',
977 977 ),
978 978 (
979 979 'single-version-externally-managed',
980 980 None,
981 981 'noop, present for eggless setuptools compat',
982 982 ),
983 983 ]
984 984
985 sub_commands = install.sub_commands + [
986 ('install_completion', lambda self: True)
987 ]
988
985 989 # Also helps setuptools not be sad while we refuse to create eggs.
986 990 single_version_externally_managed = True
987 991
988 992 def get_sub_commands(self):
989 993 # Screen out egg related commands to prevent egg generation. But allow
990 994 # mercurial.egg-info generation, since that is part of modern
991 995 # packaging.
992 996 excl = {'bdist_egg'}
993 997 return filter(lambda x: x not in excl, install.get_sub_commands(self))
994 998
995 999
996 1000 class hginstalllib(install_lib):
997 1001 """
998 1002 This is a specialization of install_lib that replaces the copy_file used
999 1003 there so that it supports setting the mode of files after copying them,
1000 1004 instead of just preserving the mode that the files originally had. If your
1001 1005 system has a umask of something like 027, preserving the permissions when
1002 1006 copying will lead to a broken install.
1003 1007
1004 1008 Note that just passing keep_permissions=False to copy_file would be
1005 1009 insufficient, as it might still be applying a umask.
1006 1010 """
1007 1011
1008 1012 def run(self):
1009 1013 realcopyfile = file_util.copy_file
1010 1014
1011 1015 def copyfileandsetmode(*args, **kwargs):
1012 1016 src, dst = args[0], args[1]
1013 1017 dst, copied = realcopyfile(*args, **kwargs)
1014 1018 if copied:
1015 1019 st = os.stat(src)
1016 1020 # Persist executable bit (apply it to group and other if user
1017 1021 # has it)
1018 1022 if st[stat.ST_MODE] & stat.S_IXUSR:
1019 1023 setmode = int('0755', 8)
1020 1024 else:
1021 1025 setmode = int('0644', 8)
1022 1026 m = stat.S_IMODE(st[stat.ST_MODE])
1023 1027 m = (m & ~int('0777', 8)) | setmode
1024 1028 os.chmod(dst, m)
1025 1029
1026 1030 file_util.copy_file = copyfileandsetmode
1027 1031 try:
1028 1032 install_lib.run(self)
1029 1033 finally:
1030 1034 file_util.copy_file = realcopyfile
1031 1035
1032 1036
1033 1037 class hginstallscripts(install_scripts):
1034 1038 """
1035 1039 This is a specialization of install_scripts that replaces the @LIBDIR@ with
1036 1040 the configured directory for modules. If possible, the path is made relative
1037 1041 to the directory for scripts.
1038 1042 """
1039 1043
1040 1044 def initialize_options(self):
1041 1045 install_scripts.initialize_options(self)
1042 1046
1043 1047 self.install_lib = None
1044 1048
1045 1049 def finalize_options(self):
1046 1050 install_scripts.finalize_options(self)
1047 1051 self.set_undefined_options('install', ('install_lib', 'install_lib'))
1048 1052
1049 1053 def run(self):
1050 1054 install_scripts.run(self)
1051 1055
1052 1056 # It only makes sense to replace @LIBDIR@ with the install path if
1053 1057 # the install path is known. For wheels, the logic below calculates
1054 1058 # the libdir to be "../..". This is because the internal layout of a
1055 1059 # wheel archive looks like:
1056 1060 #
1057 1061 # mercurial-3.6.1.data/scripts/hg
1058 1062 # mercurial/__init__.py
1059 1063 #
1060 1064 # When installing wheels, the subdirectories of the "<pkg>.data"
1061 1065 # directory are translated to system local paths and files therein
1062 1066 # are copied in place. The mercurial/* files are installed into the
1063 1067 # site-packages directory. However, the site-packages directory
1064 1068 # isn't known until wheel install time. This means we have no clue
1065 1069 # at wheel generation time what the installed site-packages directory
1066 1070 # will be. And, wheels don't appear to provide the ability to register
1067 1071 # custom code to run during wheel installation. This all means that
1068 1072 # we can't reliably set the libdir in wheels: the default behavior
1069 1073 # of looking in sys.path must do.
1070 1074
1071 1075 if (
1072 1076 os.path.splitdrive(self.install_dir)[0]
1073 1077 != os.path.splitdrive(self.install_lib)[0]
1074 1078 ):
1075 1079 # can't make relative paths from one drive to another, so use an
1076 1080 # absolute path instead
1077 1081 libdir = self.install_lib
1078 1082 else:
1079 1083 libdir = os.path.relpath(self.install_lib, self.install_dir)
1080 1084
1081 1085 for outfile in self.outfiles:
1082 1086 with open(outfile, 'rb') as fp:
1083 1087 data = fp.read()
1084 1088
1085 1089 # skip binary files
1086 1090 if b'\0' in data:
1087 1091 continue
1088 1092
1089 1093 # During local installs, the shebang will be rewritten to the final
1090 1094 # install path. During wheel packaging, the shebang has a special
1091 1095 # value.
1092 1096 if data.startswith(b'#!python'):
1093 1097 log.info(
1094 1098 'not rewriting @LIBDIR@ in %s because install path '
1095 1099 'not known' % outfile
1096 1100 )
1097 1101 continue
1098 1102
1099 1103 data = data.replace(b'@LIBDIR@', libdir.encode('unicode_escape'))
1100 1104 with open(outfile, 'wb') as fp:
1101 1105 fp.write(data)
1102 1106
1103 1107
1108 class hginstallcompletion(Command):
1109 description = 'Install shell completion'
1110
1111 def initialize_options(self):
1112 self.install_dir = None
1113
1114 def finalize_options(self):
1115 self.set_undefined_options(
1116 'install_data', ('install_dir', 'install_dir')
1117 )
1118
1119 def run(self):
1120 for src, dir_path, dest in (
1121 (
1122 'bash_completion',
1123 ('share', 'bash-completion', 'completions'),
1124 'hg',
1125 ),
1126 ('zsh_completion', ('share', 'zsh', 'site-functions'), '_hg'),
1127 ):
1128 dir = os.path.join(self.install_dir, *dir_path)
1129 self.mkpath(dir)
1130 self.copy_file(
1131 os.path.join('contrib', src), os.path.join(dir, dest)
1132 )
1133
1134
1104 1135 # virtualenv installs custom distutils/__init__.py and
1105 1136 # distutils/distutils.cfg files which essentially proxy back to the
1106 1137 # "real" distutils in the main Python install. The presence of this
1107 1138 # directory causes py2exe to pick up the "hacked" distutils package
1108 1139 # from the virtualenv and "import distutils" will fail from the py2exe
1109 1140 # build because the "real" distutils files can't be located.
1110 1141 #
1111 1142 # We work around this by monkeypatching the py2exe code finding Python
1112 1143 # modules to replace the found virtualenv distutils modules with the
1113 1144 # original versions via filesystem scanning. This is a bit hacky. But
1114 1145 # it allows us to use virtualenvs for py2exe packaging, which is more
1115 1146 # deterministic and reproducible.
1116 1147 #
1117 1148 # It's worth noting that the common StackOverflow suggestions for this
1118 1149 # problem involve copying the original distutils files into the
1119 1150 # virtualenv or into the staging directory after setup() is invoked.
1120 1151 # The former is very brittle and can easily break setup(). Our hacking
1121 1152 # of the found modules routine has a similar result as copying the files
1122 1153 # manually. But it makes fewer assumptions about how py2exe works and
1123 1154 # is less brittle.
1124 1155
1125 1156 # This only catches virtualenvs made with virtualenv (as opposed to
1126 1157 # venv, which is likely what Python 3 uses).
1127 1158 py2exehacked = py2exeloaded and getattr(sys, 'real_prefix', None) is not None
1128 1159
1129 1160 if py2exehacked:
1130 1161 from distutils.command.py2exe import py2exe as buildpy2exe
1131 1162 from py2exe.mf import Module as py2exemodule
1132 1163
1133 1164 class hgbuildpy2exe(buildpy2exe):
1134 1165 def find_needed_modules(self, mf, files, modules):
1135 1166 res = buildpy2exe.find_needed_modules(self, mf, files, modules)
1136 1167
1137 1168 # Replace virtualenv's distutils modules with the real ones.
1138 1169 modules = {}
1139 1170 for k, v in res.modules.items():
1140 1171 if k != 'distutils' and not k.startswith('distutils.'):
1141 1172 modules[k] = v
1142 1173
1143 1174 res.modules = modules
1144 1175
1145 1176 import opcode
1146 1177
1147 1178 distutilsreal = os.path.join(
1148 1179 os.path.dirname(opcode.__file__), 'distutils'
1149 1180 )
1150 1181
1151 1182 for root, dirs, files in os.walk(distutilsreal):
1152 1183 for f in sorted(files):
1153 1184 if not f.endswith('.py'):
1154 1185 continue
1155 1186
1156 1187 full = os.path.join(root, f)
1157 1188
1158 1189 parents = ['distutils']
1159 1190
1160 1191 if root != distutilsreal:
1161 1192 rel = os.path.relpath(root, distutilsreal)
1162 1193 parents.extend(p for p in rel.split(os.sep))
1163 1194
1164 1195 modname = '%s.%s' % ('.'.join(parents), f[:-3])
1165 1196
1166 1197 if modname.startswith('distutils.tests.'):
1167 1198 continue
1168 1199
1169 1200 if modname.endswith('.__init__'):
1170 1201 modname = modname[: -len('.__init__')]
1171 1202 path = os.path.dirname(full)
1172 1203 else:
1173 1204 path = None
1174 1205
1175 1206 res.modules[modname] = py2exemodule(
1176 1207 modname, full, path=path
1177 1208 )
1178 1209
1179 1210 if 'distutils' not in res.modules:
1180 1211 raise SystemExit('could not find distutils modules')
1181 1212
1182 1213 return res
1183 1214
1184 1215
1185 1216 cmdclass = {
1186 1217 'build': hgbuild,
1187 1218 'build_doc': hgbuilddoc,
1188 1219 'build_mo': hgbuildmo,
1189 1220 'build_ext': hgbuildext,
1190 1221 'build_py': hgbuildpy,
1191 1222 'build_scripts': hgbuildscripts,
1192 1223 'build_hgextindex': buildhgextindex,
1193 1224 'install': hginstall,
1225 'install_completion': hginstallcompletion,
1194 1226 'install_lib': hginstalllib,
1195 1227 'install_scripts': hginstallscripts,
1196 1228 'build_hgexe': buildhgexe,
1197 1229 }
1198 1230
1199 1231 if py2exehacked:
1200 1232 cmdclass['py2exe'] = hgbuildpy2exe
1201 1233
1202 1234 packages = [
1203 1235 'mercurial',
1204 1236 'mercurial.cext',
1205 1237 'mercurial.cffi',
1206 1238 'mercurial.defaultrc',
1207 1239 'mercurial.dirstateutils',
1208 1240 'mercurial.helptext',
1209 1241 'mercurial.helptext.internals',
1210 1242 'mercurial.hgweb',
1211 1243 'mercurial.interfaces',
1212 1244 'mercurial.pure',
1213 1245 'mercurial.templates',
1214 1246 'mercurial.thirdparty',
1215 1247 'mercurial.thirdparty.attr',
1216 1248 'mercurial.thirdparty.zope',
1217 1249 'mercurial.thirdparty.zope.interface',
1218 1250 'mercurial.upgrade_utils',
1219 1251 'mercurial.utils',
1220 1252 'mercurial.revlogutils',
1221 1253 'mercurial.testing',
1222 1254 'hgext',
1223 1255 'hgext.convert',
1224 1256 'hgext.fsmonitor',
1225 1257 'hgext.fastannotate',
1226 1258 'hgext.fsmonitor.pywatchman',
1227 1259 'hgext.git',
1228 1260 'hgext.highlight',
1229 1261 'hgext.hooklib',
1230 1262 'hgext.infinitepush',
1231 1263 'hgext.largefiles',
1232 1264 'hgext.lfs',
1233 1265 'hgext.narrow',
1234 1266 'hgext.remotefilelog',
1235 1267 'hgext.zeroconf',
1236 1268 'hgext3rd',
1237 1269 'hgdemandimport',
1238 1270 ]
1239 1271
1240 1272 for name in os.listdir(os.path.join('mercurial', 'templates')):
1241 1273 if name != '__pycache__' and os.path.isdir(
1242 1274 os.path.join('mercurial', 'templates', name)
1243 1275 ):
1244 1276 packages.append('mercurial.templates.%s' % name)
1245 1277
1246 1278 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
1247 1279 # py2exe can't cope with namespace packages very well, so we have to
1248 1280 # install any hgext3rd.* extensions that we want in the final py2exe
1249 1281 # image here. This is gross, but you gotta do what you gotta do.
1250 1282 packages.extend(os.environ['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'].split(' '))
1251 1283
1252 1284 common_depends = [
1253 1285 'mercurial/bitmanipulation.h',
1254 1286 'mercurial/compat.h',
1255 1287 'mercurial/cext/util.h',
1256 1288 ]
1257 1289 common_include_dirs = ['mercurial']
1258 1290
1259 1291 common_cflags = []
1260 1292
1261 1293 # MSVC 2008 still needs declarations at the top of the scope, but Python 3.9
1262 1294 # makes declarations not at the top of a scope in the headers.
1263 1295 if os.name != 'nt' and sys.version_info[1] < 9:
1264 1296 common_cflags = ['-Werror=declaration-after-statement']
1265 1297
1266 1298 osutil_cflags = []
1267 1299 osutil_ldflags = []
1268 1300
1269 1301 # platform specific macros
1270 1302 for plat, func in [('bsd', 'setproctitle')]:
1271 1303 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
1272 1304 osutil_cflags.append('-DHAVE_%s' % func.upper())
1273 1305
1274 1306 for plat, macro, code in [
1275 1307 (
1276 1308 'bsd|darwin',
1277 1309 'BSD_STATFS',
1278 1310 '''
1279 1311 #include <sys/param.h>
1280 1312 #include <sys/mount.h>
1281 1313 int main() { struct statfs s; return sizeof(s.f_fstypename); }
1282 1314 ''',
1283 1315 ),
1284 1316 (
1285 1317 'linux',
1286 1318 'LINUX_STATFS',
1287 1319 '''
1288 1320 #include <linux/magic.h>
1289 1321 #include <sys/vfs.h>
1290 1322 int main() { struct statfs s; return sizeof(s.f_type); }
1291 1323 ''',
1292 1324 ),
1293 1325 ]:
1294 1326 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
1295 1327 osutil_cflags.append('-DHAVE_%s' % macro)
1296 1328
1297 1329 if sys.platform == 'darwin':
1298 1330 osutil_ldflags += ['-framework', 'ApplicationServices']
1299 1331
1300 1332 if sys.platform == 'sunos5':
1301 1333 osutil_ldflags += ['-lsocket']
1302 1334
1303 1335 xdiff_srcs = [
1304 1336 'mercurial/thirdparty/xdiff/xdiffi.c',
1305 1337 'mercurial/thirdparty/xdiff/xprepare.c',
1306 1338 'mercurial/thirdparty/xdiff/xutils.c',
1307 1339 ]
1308 1340
1309 1341 xdiff_headers = [
1310 1342 'mercurial/thirdparty/xdiff/xdiff.h',
1311 1343 'mercurial/thirdparty/xdiff/xdiffi.h',
1312 1344 'mercurial/thirdparty/xdiff/xinclude.h',
1313 1345 'mercurial/thirdparty/xdiff/xmacros.h',
1314 1346 'mercurial/thirdparty/xdiff/xprepare.h',
1315 1347 'mercurial/thirdparty/xdiff/xtypes.h',
1316 1348 'mercurial/thirdparty/xdiff/xutils.h',
1317 1349 ]
1318 1350
1319 1351
1320 1352 class RustCompilationError(CCompilerError):
1321 1353 """Exception class for Rust compilation errors."""
1322 1354
1323 1355
1324 1356 class RustExtension(Extension):
1325 1357 """Base classes for concrete Rust Extension classes."""
1326 1358
1327 1359 rusttargetdir = os.path.join('rust', 'target', 'release')
1328 1360
1329 1361 def __init__(self, mpath, sources, rustlibname, subcrate, **kw):
1330 1362 Extension.__init__(self, mpath, sources, **kw)
1331 1363 srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
1332 1364
1333 1365 # adding Rust source and control files to depends so that the extension
1334 1366 # gets rebuilt if they've changed
1335 1367 self.depends.append(os.path.join(srcdir, 'Cargo.toml'))
1336 1368 cargo_lock = os.path.join(srcdir, 'Cargo.lock')
1337 1369 if os.path.exists(cargo_lock):
1338 1370 self.depends.append(cargo_lock)
1339 1371 for dirpath, subdir, fnames in os.walk(os.path.join(srcdir, 'src')):
1340 1372 self.depends.extend(
1341 1373 os.path.join(dirpath, fname)
1342 1374 for fname in fnames
1343 1375 if os.path.splitext(fname)[1] == '.rs'
1344 1376 )
1345 1377
1346 1378 @staticmethod
1347 1379 def rustdylibsuffix():
1348 1380 """Return the suffix for shared libraries produced by rustc.
1349 1381
1350 1382 See also: https://doc.rust-lang.org/reference/linkage.html
1351 1383 """
1352 1384 if sys.platform == 'darwin':
1353 1385 return '.dylib'
1354 1386 elif os.name == 'nt':
1355 1387 return '.dll'
1356 1388 else:
1357 1389 return '.so'
1358 1390
1359 1391 def rustbuild(self):
1360 1392 env = os.environ.copy()
1361 1393 if 'HGTEST_RESTOREENV' in env:
1362 1394 # Mercurial tests change HOME to a temporary directory,
1363 1395 # but, if installed with rustup, the Rust toolchain needs
1364 1396 # HOME to be correct (otherwise the 'no default toolchain'
1365 1397 # error message is issued and the build fails).
1366 1398 # This happens currently with test-hghave.t, which does
1367 1399 # invoke this build.
1368 1400
1369 1401 # Unix only fix (os.path.expanduser not really reliable if
1370 1402 # HOME is shadowed like this)
1371 1403 import pwd
1372 1404
1373 1405 env['HOME'] = pwd.getpwuid(os.getuid()).pw_dir
1374 1406
1375 1407 cargocmd = ['cargo', 'rustc', '--release']
1376 1408
1377 1409 rust_features = env.get("HG_RUST_FEATURES")
1378 1410 if rust_features:
1379 1411 cargocmd.extend(('--features', rust_features))
1380 1412
1381 1413 cargocmd.append('--')
1382 1414 if sys.platform == 'darwin':
1383 1415 cargocmd.extend(
1384 1416 ("-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup")
1385 1417 )
1386 1418 try:
1387 1419 subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
1388 1420 except OSError as exc:
1389 1421 if exc.errno == errno.ENOENT:
1390 1422 raise RustCompilationError("Cargo not found")
1391 1423 elif exc.errno == errno.EACCES:
1392 1424 raise RustCompilationError(
1393 1425 "Cargo found, but permission to execute it is denied"
1394 1426 )
1395 1427 else:
1396 1428 raise
1397 1429 except subprocess.CalledProcessError:
1398 1430 raise RustCompilationError(
1399 1431 "Cargo failed. Working directory: %r, "
1400 1432 "command: %r, environment: %r"
1401 1433 % (self.rustsrcdir, cargocmd, env)
1402 1434 )
1403 1435
1404 1436
1405 1437 class RustStandaloneExtension(RustExtension):
1406 1438 def __init__(self, pydottedname, rustcrate, dylibname, **kw):
1407 1439 RustExtension.__init__(
1408 1440 self, pydottedname, [], dylibname, rustcrate, **kw
1409 1441 )
1410 1442 self.dylibname = dylibname
1411 1443
1412 1444 def build(self, target_dir):
1413 1445 self.rustbuild()
1414 1446 target = [target_dir]
1415 1447 target.extend(self.name.split('.'))
1416 1448 target[-1] += DYLIB_SUFFIX
1417 1449 shutil.copy2(
1418 1450 os.path.join(
1419 1451 self.rusttargetdir, self.dylibname + self.rustdylibsuffix()
1420 1452 ),
1421 1453 os.path.join(*target),
1422 1454 )
1423 1455
1424 1456
1425 1457 extmodules = [
1426 1458 Extension(
1427 1459 'mercurial.cext.base85',
1428 1460 ['mercurial/cext/base85.c'],
1429 1461 include_dirs=common_include_dirs,
1430 1462 extra_compile_args=common_cflags,
1431 1463 depends=common_depends,
1432 1464 ),
1433 1465 Extension(
1434 1466 'mercurial.cext.bdiff',
1435 1467 ['mercurial/bdiff.c', 'mercurial/cext/bdiff.c'] + xdiff_srcs,
1436 1468 include_dirs=common_include_dirs,
1437 1469 extra_compile_args=common_cflags,
1438 1470 depends=common_depends + ['mercurial/bdiff.h'] + xdiff_headers,
1439 1471 ),
1440 1472 Extension(
1441 1473 'mercurial.cext.mpatch',
1442 1474 ['mercurial/mpatch.c', 'mercurial/cext/mpatch.c'],
1443 1475 include_dirs=common_include_dirs,
1444 1476 extra_compile_args=common_cflags,
1445 1477 depends=common_depends,
1446 1478 ),
1447 1479 Extension(
1448 1480 'mercurial.cext.parsers',
1449 1481 [
1450 1482 'mercurial/cext/charencode.c',
1451 1483 'mercurial/cext/dirs.c',
1452 1484 'mercurial/cext/manifest.c',
1453 1485 'mercurial/cext/parsers.c',
1454 1486 'mercurial/cext/pathencode.c',
1455 1487 'mercurial/cext/revlog.c',
1456 1488 ],
1457 1489 include_dirs=common_include_dirs,
1458 1490 extra_compile_args=common_cflags,
1459 1491 depends=common_depends
1460 1492 + [
1461 1493 'mercurial/cext/charencode.h',
1462 1494 'mercurial/cext/revlog.h',
1463 1495 ],
1464 1496 ),
1465 1497 Extension(
1466 1498 'mercurial.cext.osutil',
1467 1499 ['mercurial/cext/osutil.c'],
1468 1500 include_dirs=common_include_dirs,
1469 1501 extra_compile_args=common_cflags + osutil_cflags,
1470 1502 extra_link_args=osutil_ldflags,
1471 1503 depends=common_depends,
1472 1504 ),
1473 1505 Extension(
1474 1506 'mercurial.thirdparty.zope.interface._zope_interface_coptimizations',
1475 1507 [
1476 1508 'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
1477 1509 ],
1478 1510 extra_compile_args=common_cflags,
1479 1511 ),
1480 1512 Extension(
1481 1513 'mercurial.thirdparty.sha1dc',
1482 1514 [
1483 1515 'mercurial/thirdparty/sha1dc/cext.c',
1484 1516 'mercurial/thirdparty/sha1dc/lib/sha1.c',
1485 1517 'mercurial/thirdparty/sha1dc/lib/ubc_check.c',
1486 1518 ],
1487 1519 extra_compile_args=common_cflags,
1488 1520 ),
1489 1521 Extension(
1490 1522 'hgext.fsmonitor.pywatchman.bser',
1491 1523 ['hgext/fsmonitor/pywatchman/bser.c'],
1492 1524 extra_compile_args=common_cflags,
1493 1525 ),
1494 1526 RustStandaloneExtension(
1495 1527 'mercurial.rustext',
1496 1528 'hg-cpython',
1497 1529 'librusthg',
1498 1530 ),
1499 1531 ]
1500 1532
1501 1533
1502 1534 sys.path.insert(0, 'contrib/python-zstandard')
1503 1535 import setup_zstd
1504 1536
1505 1537 zstd = setup_zstd.get_c_extension(
1506 1538 name='mercurial.zstd', root=os.path.abspath(os.path.dirname(__file__))
1507 1539 )
1508 1540 zstd.extra_compile_args += common_cflags
1509 1541 extmodules.append(zstd)
1510 1542
1511 1543 try:
1512 1544 from distutils import cygwinccompiler
1513 1545
1514 1546 # the -mno-cygwin option has been deprecated for years
1515 1547 mingw32compilerclass = cygwinccompiler.Mingw32CCompiler
1516 1548
1517 1549 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
1518 1550 def __init__(self, *args, **kwargs):
1519 1551 mingw32compilerclass.__init__(self, *args, **kwargs)
1520 1552 for i in 'compiler compiler_so linker_exe linker_so'.split():
1521 1553 try:
1522 1554 getattr(self, i).remove('-mno-cygwin')
1523 1555 except ValueError:
1524 1556 pass
1525 1557
1526 1558 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
1527 1559 except ImportError:
1528 1560 # the cygwinccompiler package is not available on some Python
1529 1561 # distributions like the ones from the optware project for Synology
1530 1562 # DiskStation boxes
1531 1563 class HackedMingw32CCompiler:
1532 1564 pass
1533 1565
1534 1566
1535 1567 if os.name == 'nt':
1536 1568 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
1537 1569 # extra_link_args to distutils.extensions.Extension() doesn't have any
1538 1570 # effect.
1539 1571 from distutils import msvccompiler
1540 1572
1541 1573 msvccompilerclass = msvccompiler.MSVCCompiler
1542 1574
1543 1575 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
1544 1576 def initialize(self):
1545 1577 msvccompilerclass.initialize(self)
1546 1578 # "warning LNK4197: export 'func' specified multiple times"
1547 1579 self.ldflags_shared.append('/ignore:4197')
1548 1580 self.ldflags_shared_debug.append('/ignore:4197')
1549 1581
1550 1582 msvccompiler.MSVCCompiler = HackedMSVCCompiler
1551 1583
1552 1584 packagedata = {
1553 1585 'mercurial': [
1554 1586 'locale/*/LC_MESSAGES/hg.mo',
1555 1587 'dummycert.pem',
1556 1588 ],
1557 1589 'mercurial.defaultrc': [
1558 1590 '*.rc',
1559 1591 ],
1560 1592 'mercurial.helptext': [
1561 1593 '*.txt',
1562 1594 ],
1563 1595 'mercurial.helptext.internals': [
1564 1596 '*.txt',
1565 1597 ],
1566 1598 }
1567 1599
1568 1600
1569 1601 def ordinarypath(p):
1570 1602 return p and p[0] != '.' and p[-1] != '~'
1571 1603
1572 1604
1573 1605 for root in ('templates',):
1574 1606 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
1575 1607 packagename = curdir.replace(os.sep, '.')
1576 1608 packagedata[packagename] = list(filter(ordinarypath, files))
1577 1609
1578 1610 datafiles = []
1579 1611
1580 1612 # distutils expects version to be str/unicode. Converting it to
1581 1613 # unicode on Python 2 still works because it won't contain any
1582 1614 # non-ascii bytes and will be implicitly converted back to bytes
1583 1615 # when operated on.
1584 1616 assert isinstance(version, str)
1585 1617 setupversion = version
1586 1618
1587 1619 extra = {}
1588 1620
1589 1621 py2exepackages = [
1590 1622 'hgdemandimport',
1591 1623 'hgext3rd',
1592 1624 'hgext',
1593 1625 'email',
1594 1626 # implicitly imported per module policy
1595 1627 # (cffi wouldn't be used as a frozen exe)
1596 1628 'mercurial.cext',
1597 1629 #'mercurial.cffi',
1598 1630 'mercurial.pure',
1599 1631 ]
1600 1632
1601 1633 py2exe_includes = []
1602 1634
1603 1635 py2exeexcludes = []
1604 1636 py2exedllexcludes = ['crypt32.dll']
1605 1637
1606 1638 if issetuptools:
1607 1639 extra['python_requires'] = supportedpy
1608 1640
1609 1641 if py2exeloaded:
1610 1642 extra['console'] = [
1611 1643 {
1612 1644 'script': 'hg',
1613 1645 'copyright': 'Copyright (C) 2005-2022 Olivia Mackall and others',
1614 1646 'product_version': version,
1615 1647 }
1616 1648 ]
1617 1649 # Sub command of 'build' because 'py2exe' does not handle sub_commands.
1618 1650 # Need to override hgbuild because it has a private copy of
1619 1651 # build.sub_commands.
1620 1652 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1621 1653 # put dlls in sub directory so that they won't pollute PATH
1622 1654 extra['zipfile'] = 'lib/library.zip'
1623 1655
1624 1656 # We allow some configuration to be supplemented via environment
1625 1657 # variables. This is better than setup.cfg files because it allows
1626 1658 # supplementing configs instead of replacing them.
1627 1659 extrapackages = os.environ.get('HG_PY2EXE_EXTRA_PACKAGES')
1628 1660 if extrapackages:
1629 1661 py2exepackages.extend(extrapackages.split(' '))
1630 1662
1631 1663 extra_includes = os.environ.get('HG_PY2EXE_EXTRA_INCLUDES')
1632 1664 if extra_includes:
1633 1665 py2exe_includes.extend(extra_includes.split(' '))
1634 1666
1635 1667 excludes = os.environ.get('HG_PY2EXE_EXTRA_EXCLUDES')
1636 1668 if excludes:
1637 1669 py2exeexcludes.extend(excludes.split(' '))
1638 1670
1639 1671 dllexcludes = os.environ.get('HG_PY2EXE_EXTRA_DLL_EXCLUDES')
1640 1672 if dllexcludes:
1641 1673 py2exedllexcludes.extend(dllexcludes.split(' '))
1642 1674
1643 1675 if os.environ.get('PYOXIDIZER'):
1644 1676 hgbuild.sub_commands.insert(0, ('build_hgextindex', None))
1645 1677
1646 1678 if os.name == 'nt':
1647 1679 # Windows binary file versions for exe/dll files must have the
1648 1680 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
1649 1681 setupversion = setupversion.split(r'+', 1)[0]
1650 1682
1651 1683 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
1652 1684 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
1653 1685 if version:
1654 1686 version = version[0].decode('utf-8')
1655 1687 xcode4 = version.startswith('Xcode') and StrictVersion(
1656 1688 version.split()[1]
1657 1689 ) >= StrictVersion('4.0')
1658 1690 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
1659 1691 else:
1660 1692 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
1661 1693 # installed, but instead with only command-line tools. Assume
1662 1694 # that only happens on >= Lion, thus no PPC support.
1663 1695 xcode4 = True
1664 1696 xcode51 = False
1665 1697
1666 1698 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
1667 1699 # distutils.sysconfig
1668 1700 if xcode4:
1669 1701 os.environ['ARCHFLAGS'] = ''
1670 1702
1671 1703 # XCode 5.1 changes clang such that it now fails to compile if the
1672 1704 # -mno-fused-madd flag is passed, but the version of Python shipped with
1673 1705 # OS X 10.9 Mavericks includes this flag. This causes problems in all
1674 1706 # C extension modules, and a bug has been filed upstream at
1675 1707 # http://bugs.python.org/issue21244. We also need to patch this here
1676 1708 # so Mercurial can continue to compile in the meantime.
1677 1709 if xcode51:
1678 1710 cflags = get_config_var('CFLAGS')
1679 1711 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
1680 1712 os.environ['CFLAGS'] = (
1681 1713 os.environ.get('CFLAGS', '') + ' -Qunused-arguments'
1682 1714 )
1683 1715
1684 1716 setup(
1685 1717 name='mercurial',
1686 1718 version=setupversion,
1687 1719 author='Olivia Mackall and many others',
1688 1720 author_email='mercurial@mercurial-scm.org',
1689 1721 url='https://mercurial-scm.org/',
1690 1722 download_url='https://mercurial-scm.org/release/',
1691 1723 description=(
1692 1724 'Fast scalable distributed SCM (revision control, version '
1693 1725 'control) system'
1694 1726 ),
1695 1727 long_description=(
1696 1728 'Mercurial is a distributed SCM tool written in Python.'
1697 1729 ' It is used by a number of large projects that require'
1698 1730 ' fast, reliable distributed revision control, such as '
1699 1731 'Mozilla.'
1700 1732 ),
1701 1733 license='GNU GPLv2 or any later version',
1702 1734 classifiers=[
1703 1735 'Development Status :: 6 - Mature',
1704 1736 'Environment :: Console',
1705 1737 'Intended Audience :: Developers',
1706 1738 'Intended Audience :: System Administrators',
1707 1739 'License :: OSI Approved :: GNU General Public License (GPL)',
1708 1740 'Natural Language :: Danish',
1709 1741 'Natural Language :: English',
1710 1742 'Natural Language :: German',
1711 1743 'Natural Language :: Italian',
1712 1744 'Natural Language :: Japanese',
1713 1745 'Natural Language :: Portuguese (Brazilian)',
1714 1746 'Operating System :: Microsoft :: Windows',
1715 1747 'Operating System :: OS Independent',
1716 1748 'Operating System :: POSIX',
1717 1749 'Programming Language :: C',
1718 1750 'Programming Language :: Python',
1719 1751 'Topic :: Software Development :: Version Control',
1720 1752 ],
1721 1753 scripts=scripts,
1722 1754 packages=packages,
1723 1755 ext_modules=extmodules,
1724 1756 data_files=datafiles,
1725 1757 package_data=packagedata,
1726 1758 cmdclass=cmdclass,
1727 1759 distclass=hgdist,
1728 1760 options={
1729 1761 'py2exe': {
1730 1762 'bundle_files': 3,
1731 1763 'dll_excludes': py2exedllexcludes,
1732 1764 'includes': py2exe_includes,
1733 1765 'excludes': py2exeexcludes,
1734 1766 'packages': py2exepackages,
1735 1767 },
1736 1768 'bdist_mpkg': {
1737 1769 'zipdist': False,
1738 1770 'license': 'COPYING',
1739 1771 'readme': 'contrib/packaging/macosx/Readme.html',
1740 1772 'welcome': 'contrib/packaging/macosx/Welcome.html',
1741 1773 },
1742 1774 },
1743 1775 **extra
1744 1776 )
@@ -1,71 +1,71 b''
1 1 #require test-repo slow osx osxpackaging
2 2
3 3 $ . "$TESTDIR/helpers-testrepo.sh"
4 4 $ testrepohgenv
5 5
6 6 $ OUTPUTDIR="`pwd`"
7 7 $ export OUTPUTDIR
8 8 $ KEEPMPKG=yes
9 9 $ export KEEPMPKG
10 10
11 11 $ cd "$TESTDIR"/..
12 12 $ contrib/genosxversion.py --selftest ignoredarg
13 13 $ make osx > "$OUTPUTDIR/build.log" 2>&1
14 14 $ cd "$OUTPUTDIR"
15 15 $ ls -d *.pkg
16 16 Mercurial-*-macosx10.*.pkg (glob)
17 17
18 18 $ xar -xf Mercurial*.pkg
19 19
20 20 Gather list of all installed files:
21 21 $ lsbom mercurial.pkg/Bom > boms.txt
22 22
23 23 We've had problems with the filter logic in the past. Make sure no
24 24 .DS_Store files ended up in the final package:
25 25 $ grep DS_S boms.txt
26 26 [1]
27 27
28 28 Spot-check some randomly selected files:
29 29 $ grep bdiff boms.txt | cut -d ' ' -f 1,2,3
30 30 ./Library/Python/2.7/site-packages/mercurial/cext/bdiff.so 100755 0/0
31 31 ./Library/Python/2.7/site-packages/mercurial/cffi/bdiff.py 100644 0/0
32 32 ./Library/Python/2.7/site-packages/mercurial/cffi/bdiff.pyc 100644 0/0
33 33 ./Library/Python/2.7/site-packages/mercurial/cffi/bdiff.pyo 100644 0/0
34 34 ./Library/Python/2.7/site-packages/mercurial/cffi/bdiffbuild.py 100644 0/0
35 35 ./Library/Python/2.7/site-packages/mercurial/cffi/bdiffbuild.pyc 100644 0/0
36 36 ./Library/Python/2.7/site-packages/mercurial/cffi/bdiffbuild.pyo 100644 0/0
37 37 ./Library/Python/2.7/site-packages/mercurial/pure/bdiff.py 100644 0/0
38 38 ./Library/Python/2.7/site-packages/mercurial/pure/bdiff.pyc 100644 0/0
39 39 ./Library/Python/2.7/site-packages/mercurial/pure/bdiff.pyo 100644 0/0
40 40 $ grep zsh/site-functions/_hg boms.txt | cut -d ' ' -f 1,2,3
41 41 ./usr/local/share/zsh/site-functions/_hg 100644 0/0
42 $ grep hg-completion.bash boms.txt | cut -d ' ' -f 1,2,3
43 ./usr/local/hg/contrib/hg-completion.bash 100644 0/0
42 $ grep bash-completion/completions/hg boms.txt | cut -d ' ' -f 1,2,3
43 ./usr/local/share/bash-completion-completions/hg 100644 0/0
44 44 $ egrep 'man[15]' boms.txt | cut -d ' ' -f 1,2,3
45 45 ./usr/local/share/man/man1 40755 0/0
46 46 ./usr/local/share/man/man1/chg.1 100644 0/0
47 47 ./usr/local/share/man/man1/hg.1 100644 0/0
48 48 ./usr/local/share/man/man5 40755 0/0
49 49 ./usr/local/share/man/man5/hgignore.5 100644 0/0
50 50 ./usr/local/share/man/man5/hgrc.5 100644 0/0
51 51 $ grep bser boms.txt | cut -d ' ' -f 1,2,3
52 52 ./Library/Python/2.7/site-packages/hgext/fsmonitor/pywatchman/bser.so 100755 0/0
53 53 ./Library/Python/2.7/site-packages/hgext/fsmonitor/pywatchman/pybser.py 100644 0/0
54 54 ./Library/Python/2.7/site-packages/hgext/fsmonitor/pywatchman/pybser.pyc 100644 0/0
55 55 ./Library/Python/2.7/site-packages/hgext/fsmonitor/pywatchman/pybser.pyo 100644 0/0
56 56 $ grep localrepo boms.txt | cut -d ' ' -f 1,2,3
57 57 ./Library/Python/2.7/site-packages/mercurial/localrepo.py 100644 0/0
58 58 ./Library/Python/2.7/site-packages/mercurial/localrepo.pyc 100644 0/0
59 59 ./Library/Python/2.7/site-packages/mercurial/localrepo.pyo 100644 0/0
60 60 $ egrep 'bin/' boms.txt | cut -d ' ' -f 1,2,3
61 61 ./usr/local/bin/chg 100755 0/0
62 62 ./usr/local/bin/hg 100755 0/0
63 63
64 64 Make sure the built binary uses the system Python interpreter
65 65 $ bsdtar xf mercurial.pkg/Payload usr/local/bin
66 66 Use a glob to find this to avoid check-code whining about a fixed path.
67 67 $ head -n 1 usr/local/b?n/hg
68 68 #!/System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
69 69
70 70 Note that we're not currently installing any /etc/mercurial stuff,
71 71 including merge-tool configurations.
General Comments 0
You need to be logged in to leave comments. Login now