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