##// END OF EJS Templates
cleanup: update references to /help/ that should now be /helptext/...
Augie Fackler -
r44135:640bae94 default
parent child Browse files
Show More
@@ -1,258 +1,258
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 PYTHON?=python
8 PYTHON?=python
9 $(eval HGROOT := $(shell pwd))
9 $(eval HGROOT := $(shell pwd))
10 HGPYTHONS ?= $(HGROOT)/build/pythons
10 HGPYTHONS ?= $(HGROOT)/build/pythons
11 PURE=
11 PURE=
12 PYFILESCMD=find mercurial hgext doc -name '*.py'
12 PYFILESCMD=find mercurial hgext doc -name '*.py'
13 PYFILES:=$(shell $(PYFILESCMD))
13 PYFILES:=$(shell $(PYFILESCMD))
14 DOCFILES=mercurial/help/*.txt
14 DOCFILES=mercurial/helptext/*.txt
15 export LANGUAGE=C
15 export LANGUAGE=C
16 export LC_ALL=C
16 export LC_ALL=C
17 TESTFLAGS ?= $(shell echo $$HGTESTFLAGS)
17 TESTFLAGS ?= $(shell echo $$HGTESTFLAGS)
18 OSXVERSIONFLAGS ?= $(shell echo $$OSXVERSIONFLAGS)
18 OSXVERSIONFLAGS ?= $(shell echo $$OSXVERSIONFLAGS)
19 CARGO = cargo
19 CARGO = cargo
20
20
21 # Set this to e.g. "mingw32" to use a non-default compiler.
21 # Set this to e.g. "mingw32" to use a non-default compiler.
22 COMPILER=
22 COMPILER=
23
23
24 COMPILERFLAG_tmp_ =
24 COMPILERFLAG_tmp_ =
25 COMPILERFLAG_tmp_${COMPILER} ?= -c $(COMPILER)
25 COMPILERFLAG_tmp_${COMPILER} ?= -c $(COMPILER)
26 COMPILERFLAG=${COMPILERFLAG_tmp_${COMPILER}}
26 COMPILERFLAG=${COMPILERFLAG_tmp_${COMPILER}}
27
27
28 help:
28 help:
29 @echo 'Commonly used make targets:'
29 @echo 'Commonly used make targets:'
30 @echo ' all - build program and documentation'
30 @echo ' all - build program and documentation'
31 @echo ' install - install program and man pages to $$PREFIX ($(PREFIX))'
31 @echo ' install - install program and man pages to $$PREFIX ($(PREFIX))'
32 @echo ' install-home - install with setup.py install --home=$$HOME ($(HOME))'
32 @echo ' install-home - install with setup.py install --home=$$HOME ($(HOME))'
33 @echo ' local - build for inplace usage'
33 @echo ' local - build for inplace usage'
34 @echo ' tests - run all tests in the automatic test suite'
34 @echo ' tests - run all tests in the automatic test suite'
35 @echo ' test-foo - run only specified tests (e.g. test-merge1.t)'
35 @echo ' test-foo - run only specified tests (e.g. test-merge1.t)'
36 @echo ' dist - run all tests and create a source tarball in dist/'
36 @echo ' dist - run all tests and create a source tarball in dist/'
37 @echo ' clean - remove files created by other targets'
37 @echo ' clean - remove files created by other targets'
38 @echo ' (except installed files or dist source tarball)'
38 @echo ' (except installed files or dist source tarball)'
39 @echo ' update-pot - update i18n/hg.pot'
39 @echo ' update-pot - update i18n/hg.pot'
40 @echo
40 @echo
41 @echo 'Example for a system-wide installation under /usr/local:'
41 @echo 'Example for a system-wide installation under /usr/local:'
42 @echo ' make all && su -c "make install" && hg version'
42 @echo ' make all && su -c "make install" && hg version'
43 @echo
43 @echo
44 @echo 'Example for a local installation (usable in this directory):'
44 @echo 'Example for a local installation (usable in this directory):'
45 @echo ' make local && ./hg version'
45 @echo ' make local && ./hg version'
46
46
47 all: build doc
47 all: build doc
48
48
49 local:
49 local:
50 $(PYTHON) setup.py $(PURE) \
50 $(PYTHON) setup.py $(PURE) \
51 build_py -c -d . \
51 build_py -c -d . \
52 build_ext $(COMPILERFLAG) -i \
52 build_ext $(COMPILERFLAG) -i \
53 build_hgexe $(COMPILERFLAG) -i \
53 build_hgexe $(COMPILERFLAG) -i \
54 build_mo
54 build_mo
55 env HGRCPATH= $(PYTHON) hg version
55 env HGRCPATH= $(PYTHON) hg version
56
56
57 build:
57 build:
58 $(PYTHON) setup.py $(PURE) build $(COMPILERFLAG)
58 $(PYTHON) setup.py $(PURE) build $(COMPILERFLAG)
59
59
60 wheel:
60 wheel:
61 FORCE_SETUPTOOLS=1 $(PYTHON) setup.py $(PURE) bdist_wheel $(COMPILERFLAG)
61 FORCE_SETUPTOOLS=1 $(PYTHON) setup.py $(PURE) bdist_wheel $(COMPILERFLAG)
62
62
63 doc:
63 doc:
64 $(MAKE) -C doc
64 $(MAKE) -C doc
65
65
66 cleanbutpackages:
66 cleanbutpackages:
67 -$(PYTHON) setup.py clean --all # ignore errors from this command
67 -$(PYTHON) setup.py clean --all # ignore errors from this command
68 find contrib doc hgext hgext3rd i18n mercurial tests hgdemandimport \
68 find contrib doc hgext hgext3rd i18n mercurial tests hgdemandimport \
69 \( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';'
69 \( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';'
70 rm -f MANIFEST MANIFEST.in hgext/__index__.py tests/*.err
70 rm -f MANIFEST MANIFEST.in hgext/__index__.py tests/*.err
71 rm -f mercurial/__modulepolicy__.py
71 rm -f mercurial/__modulepolicy__.py
72 if test -d .hg; then rm -f mercurial/__version__.py; fi
72 if test -d .hg; then rm -f mercurial/__version__.py; fi
73 rm -rf build mercurial/locale
73 rm -rf build mercurial/locale
74 $(MAKE) -C doc clean
74 $(MAKE) -C doc clean
75 $(MAKE) -C contrib/chg distclean
75 $(MAKE) -C contrib/chg distclean
76 rm -rf rust/target
76 rm -rf rust/target
77 rm -f mercurial/rustext.so
77 rm -f mercurial/rustext.so
78
78
79 clean: cleanbutpackages
79 clean: cleanbutpackages
80 rm -rf packages
80 rm -rf packages
81
81
82 install: install-bin install-doc
82 install: install-bin install-doc
83
83
84 install-bin: build
84 install-bin: build
85 $(PYTHON) setup.py $(PURE) install --root="$(DESTDIR)/" --prefix="$(PREFIX)" --force
85 $(PYTHON) setup.py $(PURE) install --root="$(DESTDIR)/" --prefix="$(PREFIX)" --force
86
86
87 install-doc: doc
87 install-doc: doc
88 cd doc && $(MAKE) $(MFLAGS) install
88 cd doc && $(MAKE) $(MFLAGS) install
89
89
90 install-home: install-home-bin install-home-doc
90 install-home: install-home-bin install-home-doc
91
91
92 install-home-bin: build
92 install-home-bin: build
93 $(PYTHON) setup.py $(PURE) install --home="$(HOME)" --prefix="" --force
93 $(PYTHON) setup.py $(PURE) install --home="$(HOME)" --prefix="" --force
94
94
95 install-home-doc: doc
95 install-home-doc: doc
96 cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install
96 cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install
97
97
98 MANIFEST-doc:
98 MANIFEST-doc:
99 $(MAKE) -C doc MANIFEST
99 $(MAKE) -C doc MANIFEST
100
100
101 MANIFEST.in: MANIFEST-doc
101 MANIFEST.in: MANIFEST-doc
102 hg manifest | sed -e 's/^/include /' > MANIFEST.in
102 hg manifest | sed -e 's/^/include /' > MANIFEST.in
103 echo include mercurial/__version__.py >> MANIFEST.in
103 echo include mercurial/__version__.py >> MANIFEST.in
104 sed -e 's/^/include /' < doc/MANIFEST >> MANIFEST.in
104 sed -e 's/^/include /' < doc/MANIFEST >> MANIFEST.in
105
105
106 dist: tests dist-notests
106 dist: tests dist-notests
107
107
108 dist-notests: doc MANIFEST.in
108 dist-notests: doc MANIFEST.in
109 TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist
109 TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist
110
110
111 check: tests
111 check: tests
112
112
113 tests:
113 tests:
114 # Run Rust tests if cargo is installed
114 # Run Rust tests if cargo is installed
115 if command -v $(CARGO) >/dev/null 2>&1; then \
115 if command -v $(CARGO) >/dev/null 2>&1; then \
116 $(MAKE) rust-tests; \
116 $(MAKE) rust-tests; \
117 fi
117 fi
118 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS)
118 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS)
119
119
120 test-%:
120 test-%:
121 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@
121 cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@
122
122
123 testpy-%:
123 testpy-%:
124 @echo Looking for Python $* in $(HGPYTHONS)
124 @echo Looking for Python $* in $(HGPYTHONS)
125 [ -e $(HGPYTHONS)/$*/bin/python ] || ( \
125 [ -e $(HGPYTHONS)/$*/bin/python ] || ( \
126 cd $$(mktemp --directory --tmpdir) && \
126 cd $$(mktemp --directory --tmpdir) && \
127 $(MAKE) -f $(HGROOT)/contrib/Makefile.python PYTHONVER=$* PREFIX=$(HGPYTHONS)/$* python )
127 $(MAKE) -f $(HGROOT)/contrib/Makefile.python PYTHONVER=$* PREFIX=$(HGPYTHONS)/$* python )
128 cd tests && $(HGPYTHONS)/$*/bin/python run-tests.py $(TESTFLAGS)
128 cd tests && $(HGPYTHONS)/$*/bin/python run-tests.py $(TESTFLAGS)
129
129
130 rust-tests: py_feature = $(shell $(PYTHON) -c \
130 rust-tests: py_feature = $(shell $(PYTHON) -c \
131 'import sys; print(["python27-bin", "python3-bin"][sys.version_info[0] >= 3])')
131 'import sys; print(["python27-bin", "python3-bin"][sys.version_info[0] >= 3])')
132 rust-tests:
132 rust-tests:
133 cd $(HGROOT)/rust/hg-cpython \
133 cd $(HGROOT)/rust/hg-cpython \
134 && $(CARGO) test --quiet --all \
134 && $(CARGO) test --quiet --all \
135 --no-default-features --features "$(py_feature)"
135 --no-default-features --features "$(py_feature)"
136
136
137 check-code:
137 check-code:
138 hg manifest | xargs python contrib/check-code.py
138 hg manifest | xargs python contrib/check-code.py
139
139
140 format-c:
140 format-c:
141 clang-format --style file -i \
141 clang-format --style file -i \
142 `hg files 'set:(**.c or **.cc or **.h) and not "listfile:contrib/clang-format-ignorelist"'`
142 `hg files 'set:(**.c or **.cc or **.h) and not "listfile:contrib/clang-format-ignorelist"'`
143
143
144 update-pot: i18n/hg.pot
144 update-pot: i18n/hg.pot
145
145
146 i18n/hg.pot: $(PYFILES) $(DOCFILES) i18n/posplit i18n/hggettext
146 i18n/hg.pot: $(PYFILES) $(DOCFILES) i18n/posplit i18n/hggettext
147 $(PYTHON) i18n/hggettext mercurial/commands.py \
147 $(PYTHON) i18n/hggettext mercurial/commands.py \
148 hgext/*.py hgext/*/__init__.py \
148 hgext/*.py hgext/*/__init__.py \
149 mercurial/fileset.py mercurial/revset.py \
149 mercurial/fileset.py mercurial/revset.py \
150 mercurial/templatefilters.py \
150 mercurial/templatefilters.py \
151 mercurial/templatefuncs.py \
151 mercurial/templatefuncs.py \
152 mercurial/templatekw.py \
152 mercurial/templatekw.py \
153 mercurial/filemerge.py \
153 mercurial/filemerge.py \
154 mercurial/hgweb/webcommands.py \
154 mercurial/hgweb/webcommands.py \
155 mercurial/util.py \
155 mercurial/util.py \
156 $(DOCFILES) > i18n/hg.pot.tmp
156 $(DOCFILES) > i18n/hg.pot.tmp
157 # All strings marked for translation in Mercurial contain
157 # All strings marked for translation in Mercurial contain
158 # ASCII characters only. But some files contain string
158 # ASCII characters only. But some files contain string
159 # literals like this '\037\213'. xgettext thinks it has to
159 # literals like this '\037\213'. xgettext thinks it has to
160 # parse them even though they are not marked for translation.
160 # parse them even though they are not marked for translation.
161 # Extracting with an explicit encoding of ISO-8859-1 will make
161 # Extracting with an explicit encoding of ISO-8859-1 will make
162 # xgettext "parse" and ignore them.
162 # xgettext "parse" and ignore them.
163 $(PYFILESCMD) | xargs \
163 $(PYFILESCMD) | xargs \
164 xgettext --package-name "Mercurial" \
164 xgettext --package-name "Mercurial" \
165 --msgid-bugs-address "<mercurial-devel@mercurial-scm.org>" \
165 --msgid-bugs-address "<mercurial-devel@mercurial-scm.org>" \
166 --copyright-holder "Matt Mackall <mpm@selenic.com> and others" \
166 --copyright-holder "Matt Mackall <mpm@selenic.com> and others" \
167 --from-code ISO-8859-1 --join --sort-by-file --add-comments=i18n: \
167 --from-code ISO-8859-1 --join --sort-by-file --add-comments=i18n: \
168 -d hg -p i18n -o hg.pot.tmp
168 -d hg -p i18n -o hg.pot.tmp
169 $(PYTHON) i18n/posplit i18n/hg.pot.tmp
169 $(PYTHON) i18n/posplit i18n/hg.pot.tmp
170 # The target file is not created before the last step. So it never is in
170 # The target file is not created before the last step. So it never is in
171 # an intermediate state.
171 # an intermediate state.
172 mv -f i18n/hg.pot.tmp i18n/hg.pot
172 mv -f i18n/hg.pot.tmp i18n/hg.pot
173
173
174 %.po: i18n/hg.pot
174 %.po: i18n/hg.pot
175 # work on a temporary copy for never having a half completed target
175 # work on a temporary copy for never having a half completed target
176 cp $@ $@.tmp
176 cp $@ $@.tmp
177 msgmerge --no-location --update $@.tmp $^
177 msgmerge --no-location --update $@.tmp $^
178 mv -f $@.tmp $@
178 mv -f $@.tmp $@
179
179
180 # Packaging targets
180 # Packaging targets
181
181
182 packaging_targets := \
182 packaging_targets := \
183 centos5 \
183 centos5 \
184 centos6 \
184 centos6 \
185 centos7 \
185 centos7 \
186 centos8 \
186 centos8 \
187 deb \
187 deb \
188 docker-centos5 \
188 docker-centos5 \
189 docker-centos6 \
189 docker-centos6 \
190 docker-centos7 \
190 docker-centos7 \
191 docker-centos8 \
191 docker-centos8 \
192 docker-debian-bullseye \
192 docker-debian-bullseye \
193 docker-debian-buster \
193 docker-debian-buster \
194 docker-debian-stretch \
194 docker-debian-stretch \
195 docker-fedora \
195 docker-fedora \
196 docker-ubuntu-trusty \
196 docker-ubuntu-trusty \
197 docker-ubuntu-trusty-ppa \
197 docker-ubuntu-trusty-ppa \
198 docker-ubuntu-xenial \
198 docker-ubuntu-xenial \
199 docker-ubuntu-xenial-ppa \
199 docker-ubuntu-xenial-ppa \
200 docker-ubuntu-artful \
200 docker-ubuntu-artful \
201 docker-ubuntu-artful-ppa \
201 docker-ubuntu-artful-ppa \
202 docker-ubuntu-bionic \
202 docker-ubuntu-bionic \
203 docker-ubuntu-bionic-ppa \
203 docker-ubuntu-bionic-ppa \
204 fedora \
204 fedora \
205 linux-wheels \
205 linux-wheels \
206 linux-wheels-x86_64 \
206 linux-wheels-x86_64 \
207 linux-wheels-i686 \
207 linux-wheels-i686 \
208 ppa
208 ppa
209
209
210 # Forward packaging targets for convenience.
210 # Forward packaging targets for convenience.
211 $(packaging_targets):
211 $(packaging_targets):
212 $(MAKE) -C contrib/packaging $@
212 $(MAKE) -C contrib/packaging $@
213
213
214 osx:
214 osx:
215 rm -rf build/mercurial
215 rm -rf build/mercurial
216 /usr/bin/python2.7 setup.py install --optimize=1 \
216 /usr/bin/python2.7 setup.py install --optimize=1 \
217 --root=build/mercurial/ --prefix=/usr/local/ \
217 --root=build/mercurial/ --prefix=/usr/local/ \
218 --install-lib=/Library/Python/2.7/site-packages/
218 --install-lib=/Library/Python/2.7/site-packages/
219 make -C doc all install DESTDIR="$(PWD)/build/mercurial/"
219 make -C doc all install DESTDIR="$(PWD)/build/mercurial/"
220 # Place a bogon .DS_Store file in the target dir so we can be
220 # Place a bogon .DS_Store file in the target dir so we can be
221 # sure it doesn't get included in the final package.
221 # sure it doesn't get included in the final package.
222 touch build/mercurial/.DS_Store
222 touch build/mercurial/.DS_Store
223 # install zsh completions - this location appears to be
223 # install zsh completions - this location appears to be
224 # searched by default as of macOS Sierra.
224 # searched by default as of macOS Sierra.
225 install -d build/mercurial/usr/local/share/zsh/site-functions/
225 install -d build/mercurial/usr/local/share/zsh/site-functions/
226 install -m 0644 contrib/zsh_completion build/mercurial/usr/local/share/zsh/site-functions/_hg
226 install -m 0644 contrib/zsh_completion build/mercurial/usr/local/share/zsh/site-functions/_hg
227 # install bash completions - there doesn't appear to be a
227 # install bash completions - there doesn't appear to be a
228 # place that's searched by default for bash, so we'll follow
228 # place that's searched by default for bash, so we'll follow
229 # the lead of Apple's git install and just put it in a
229 # the lead of Apple's git install and just put it in a
230 # location of our own.
230 # location of our own.
231 install -d build/mercurial/usr/local/hg/contrib/
231 install -d build/mercurial/usr/local/hg/contrib/
232 install -m 0644 contrib/bash_completion build/mercurial/usr/local/hg/contrib/hg-completion.bash
232 install -m 0644 contrib/bash_completion build/mercurial/usr/local/hg/contrib/hg-completion.bash
233 make -C contrib/chg \
233 make -C contrib/chg \
234 HGPATH=/usr/local/bin/hg \
234 HGPATH=/usr/local/bin/hg \
235 PYTHON=/usr/bin/python2.7 \
235 PYTHON=/usr/bin/python2.7 \
236 HGEXTDIR=/Library/Python/2.7/site-packages/hgext \
236 HGEXTDIR=/Library/Python/2.7/site-packages/hgext \
237 DESTDIR=../../build/mercurial \
237 DESTDIR=../../build/mercurial \
238 PREFIX=/usr/local \
238 PREFIX=/usr/local \
239 clean install
239 clean install
240 mkdir -p $${OUTPUTDIR:-dist}
240 mkdir -p $${OUTPUTDIR:-dist}
241 HGVER=$$(python contrib/genosxversion.py $(OSXVERSIONFLAGS) build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py) && \
241 HGVER=$$(python contrib/genosxversion.py $(OSXVERSIONFLAGS) build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py) && \
242 OSXVER=$$(sw_vers -productVersion | cut -d. -f1,2) && \
242 OSXVER=$$(sw_vers -productVersion | cut -d. -f1,2) && \
243 pkgbuild --filter \\.DS_Store --root build/mercurial/ \
243 pkgbuild --filter \\.DS_Store --root build/mercurial/ \
244 --identifier org.mercurial-scm.mercurial \
244 --identifier org.mercurial-scm.mercurial \
245 --version "$${HGVER}" \
245 --version "$${HGVER}" \
246 build/mercurial.pkg && \
246 build/mercurial.pkg && \
247 productbuild --distribution contrib/packaging/macosx/distribution.xml \
247 productbuild --distribution contrib/packaging/macosx/distribution.xml \
248 --package-path build/ \
248 --package-path build/ \
249 --version "$${HGVER}" \
249 --version "$${HGVER}" \
250 --resources contrib/packaging/macosx/ \
250 --resources contrib/packaging/macosx/ \
251 "$${OUTPUTDIR:-dist/}"/Mercurial-"$${HGVER}"-macosx"$${OSXVER}".pkg
251 "$${OUTPUTDIR:-dist/}"/Mercurial-"$${HGVER}"-macosx"$${OSXVER}".pkg
252
252
253 .PHONY: help all local build doc cleanbutpackages clean install install-bin \
253 .PHONY: help all local build doc cleanbutpackages clean install install-bin \
254 install-doc install-home install-home-bin install-home-doc \
254 install-doc install-home install-home-bin install-home-doc \
255 dist dist-notests check tests rust-tests check-code format-c \
255 dist dist-notests check tests rust-tests check-code format-c \
256 update-pot \
256 update-pot \
257 $(packaging_targets) \
257 $(packaging_targets) \
258 osx
258 osx
@@ -1,245 +1,245
1 # py2exe.py - Functionality for performing py2exe builds.
1 # py2exe.py - Functionality for performing py2exe builds.
2 #
2 #
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 # no-check-code because Python 3 native.
8 # no-check-code because Python 3 native.
9
9
10 import os
10 import os
11 import pathlib
11 import pathlib
12 import subprocess
12 import subprocess
13
13
14 from .downloads import download_entry
14 from .downloads import download_entry
15 from .util import (
15 from .util import (
16 extract_tar_to_directory,
16 extract_tar_to_directory,
17 extract_zip_to_directory,
17 extract_zip_to_directory,
18 process_install_rules,
18 process_install_rules,
19 python_exe_info,
19 python_exe_info,
20 )
20 )
21
21
22
22
23 STAGING_RULES = [
23 STAGING_RULES = [
24 ('contrib/bash_completion', 'Contrib/'),
24 ('contrib/bash_completion', 'Contrib/'),
25 ('contrib/hgk', 'Contrib/hgk.tcl'),
25 ('contrib/hgk', 'Contrib/hgk.tcl'),
26 ('contrib/hgweb.fcgi', 'Contrib/'),
26 ('contrib/hgweb.fcgi', 'Contrib/'),
27 ('contrib/hgweb.wsgi', 'Contrib/'),
27 ('contrib/hgweb.wsgi', 'Contrib/'),
28 ('contrib/logo-droplets.svg', 'Contrib/'),
28 ('contrib/logo-droplets.svg', 'Contrib/'),
29 ('contrib/mercurial.el', 'Contrib/'),
29 ('contrib/mercurial.el', 'Contrib/'),
30 ('contrib/mq.el', 'Contrib/'),
30 ('contrib/mq.el', 'Contrib/'),
31 ('contrib/tcsh_completion', 'Contrib/'),
31 ('contrib/tcsh_completion', 'Contrib/'),
32 ('contrib/tcsh_completion_build.sh', 'Contrib/'),
32 ('contrib/tcsh_completion_build.sh', 'Contrib/'),
33 ('contrib/vim/*', 'Contrib/Vim/'),
33 ('contrib/vim/*', 'Contrib/Vim/'),
34 ('contrib/win32/postinstall.txt', 'ReleaseNotes.txt'),
34 ('contrib/win32/postinstall.txt', 'ReleaseNotes.txt'),
35 ('contrib/win32/ReadMe.html', 'ReadMe.html'),
35 ('contrib/win32/ReadMe.html', 'ReadMe.html'),
36 ('contrib/xml.rnc', 'Contrib/'),
36 ('contrib/xml.rnc', 'Contrib/'),
37 ('contrib/zsh_completion', 'Contrib/'),
37 ('contrib/zsh_completion', 'Contrib/'),
38 ('dist/hg.exe', './'),
38 ('dist/hg.exe', './'),
39 ('dist/lib/*.dll', 'lib/'),
39 ('dist/lib/*.dll', 'lib/'),
40 ('dist/lib/*.pyd', 'lib/'),
40 ('dist/lib/*.pyd', 'lib/'),
41 ('dist/lib/library.zip', 'lib/'),
41 ('dist/lib/library.zip', 'lib/'),
42 ('dist/Microsoft.VC*.CRT.manifest', './'),
42 ('dist/Microsoft.VC*.CRT.manifest', './'),
43 ('dist/msvc*.dll', './'),
43 ('dist/msvc*.dll', './'),
44 ('dist/python*.dll', './'),
44 ('dist/python*.dll', './'),
45 ('doc/*.html', 'doc/'),
45 ('doc/*.html', 'doc/'),
46 ('doc/style.css', 'doc/'),
46 ('doc/style.css', 'doc/'),
47 ('mercurial/help/**/*.txt', 'help/'),
47 ('mercurial/helptext/**/*.txt', 'helptext/'),
48 ('mercurial/default.d/*.rc', 'hgrc.d/'),
48 ('mercurial/default.d/*.rc', 'hgrc.d/'),
49 ('mercurial/locale/**/*', 'locale/'),
49 ('mercurial/locale/**/*', 'locale/'),
50 ('mercurial/templates/**/*', 'Templates/'),
50 ('mercurial/templates/**/*', 'Templates/'),
51 ('COPYING', 'Copying.txt'),
51 ('COPYING', 'Copying.txt'),
52 ]
52 ]
53
53
54 # List of paths to exclude from the staging area.
54 # List of paths to exclude from the staging area.
55 STAGING_EXCLUDES = [
55 STAGING_EXCLUDES = [
56 'doc/hg-ssh.8.html',
56 'doc/hg-ssh.8.html',
57 ]
57 ]
58
58
59
59
60 def build_py2exe(
60 def build_py2exe(
61 source_dir: pathlib.Path,
61 source_dir: pathlib.Path,
62 build_dir: pathlib.Path,
62 build_dir: pathlib.Path,
63 python_exe: pathlib.Path,
63 python_exe: pathlib.Path,
64 build_name: str,
64 build_name: str,
65 venv_requirements_txt: pathlib.Path,
65 venv_requirements_txt: pathlib.Path,
66 extra_packages=None,
66 extra_packages=None,
67 extra_excludes=None,
67 extra_excludes=None,
68 extra_dll_excludes=None,
68 extra_dll_excludes=None,
69 extra_packages_script=None,
69 extra_packages_script=None,
70 ):
70 ):
71 """Build Mercurial with py2exe.
71 """Build Mercurial with py2exe.
72
72
73 Build files will be placed in ``build_dir``.
73 Build files will be placed in ``build_dir``.
74
74
75 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
75 py2exe's setup.py doesn't use setuptools. It doesn't have modern logic
76 for finding the Python 2.7 toolchain. So, we require the environment
76 for finding the Python 2.7 toolchain. So, we require the environment
77 to already be configured with an active toolchain.
77 to already be configured with an active toolchain.
78 """
78 """
79 if 'VCINSTALLDIR' not in os.environ:
79 if 'VCINSTALLDIR' not in os.environ:
80 raise Exception(
80 raise Exception(
81 'not running from a Visual C++ build environment; '
81 'not running from a Visual C++ build environment; '
82 'execute the "Visual C++ <version> Command Prompt" '
82 'execute the "Visual C++ <version> Command Prompt" '
83 'application shortcut or a vcsvarsall.bat file'
83 'application shortcut or a vcsvarsall.bat file'
84 )
84 )
85
85
86 # Identity x86/x64 and validate the environment matches the Python
86 # Identity x86/x64 and validate the environment matches the Python
87 # architecture.
87 # architecture.
88 vc_x64 = r'\x64' in os.environ['LIB']
88 vc_x64 = r'\x64' in os.environ['LIB']
89
89
90 py_info = python_exe_info(python_exe)
90 py_info = python_exe_info(python_exe)
91
91
92 if vc_x64:
92 if vc_x64:
93 if py_info['arch'] != '64bit':
93 if py_info['arch'] != '64bit':
94 raise Exception(
94 raise Exception(
95 'architecture mismatch: Visual C++ environment '
95 'architecture mismatch: Visual C++ environment '
96 'is configured for 64-bit but Python is 32-bit'
96 'is configured for 64-bit but Python is 32-bit'
97 )
97 )
98 else:
98 else:
99 if py_info['arch'] != '32bit':
99 if py_info['arch'] != '32bit':
100 raise Exception(
100 raise Exception(
101 'architecture mismatch: Visual C++ environment '
101 'architecture mismatch: Visual C++ environment '
102 'is configured for 32-bit but Python is 64-bit'
102 'is configured for 32-bit but Python is 64-bit'
103 )
103 )
104
104
105 if py_info['py3']:
105 if py_info['py3']:
106 raise Exception('Only Python 2 is currently supported')
106 raise Exception('Only Python 2 is currently supported')
107
107
108 build_dir.mkdir(exist_ok=True)
108 build_dir.mkdir(exist_ok=True)
109
109
110 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
110 gettext_pkg, gettext_entry = download_entry('gettext', build_dir)
111 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
111 gettext_dep_pkg = download_entry('gettext-dep', build_dir)[0]
112 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
112 virtualenv_pkg, virtualenv_entry = download_entry('virtualenv', build_dir)
113 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
113 py2exe_pkg, py2exe_entry = download_entry('py2exe', build_dir)
114
114
115 venv_path = build_dir / (
115 venv_path = build_dir / (
116 'venv-%s-%s' % (build_name, 'x64' if vc_x64 else 'x86')
116 'venv-%s-%s' % (build_name, 'x64' if vc_x64 else 'x86')
117 )
117 )
118
118
119 gettext_root = build_dir / ('gettext-win-%s' % gettext_entry['version'])
119 gettext_root = build_dir / ('gettext-win-%s' % gettext_entry['version'])
120
120
121 if not gettext_root.exists():
121 if not gettext_root.exists():
122 extract_zip_to_directory(gettext_pkg, gettext_root)
122 extract_zip_to_directory(gettext_pkg, gettext_root)
123 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
123 extract_zip_to_directory(gettext_dep_pkg, gettext_root)
124
124
125 # This assumes Python 2. We don't need virtualenv on Python 3.
125 # This assumes Python 2. We don't need virtualenv on Python 3.
126 virtualenv_src_path = build_dir / (
126 virtualenv_src_path = build_dir / (
127 'virtualenv-%s' % virtualenv_entry['version']
127 'virtualenv-%s' % virtualenv_entry['version']
128 )
128 )
129 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
129 virtualenv_py = virtualenv_src_path / 'virtualenv.py'
130
130
131 if not virtualenv_src_path.exists():
131 if not virtualenv_src_path.exists():
132 extract_tar_to_directory(virtualenv_pkg, build_dir)
132 extract_tar_to_directory(virtualenv_pkg, build_dir)
133
133
134 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
134 py2exe_source_path = build_dir / ('py2exe-%s' % py2exe_entry['version'])
135
135
136 if not py2exe_source_path.exists():
136 if not py2exe_source_path.exists():
137 extract_zip_to_directory(py2exe_pkg, build_dir)
137 extract_zip_to_directory(py2exe_pkg, build_dir)
138
138
139 if not venv_path.exists():
139 if not venv_path.exists():
140 print('creating virtualenv with dependencies')
140 print('creating virtualenv with dependencies')
141 subprocess.run(
141 subprocess.run(
142 [str(python_exe), str(virtualenv_py), str(venv_path)], check=True
142 [str(python_exe), str(virtualenv_py), str(venv_path)], check=True
143 )
143 )
144
144
145 venv_python = venv_path / 'Scripts' / 'python.exe'
145 venv_python = venv_path / 'Scripts' / 'python.exe'
146 venv_pip = venv_path / 'Scripts' / 'pip.exe'
146 venv_pip = venv_path / 'Scripts' / 'pip.exe'
147
147
148 subprocess.run(
148 subprocess.run(
149 [str(venv_pip), 'install', '-r', str(venv_requirements_txt)], check=True
149 [str(venv_pip), 'install', '-r', str(venv_requirements_txt)], check=True
150 )
150 )
151
151
152 # Force distutils to use VC++ settings from environment, which was
152 # Force distutils to use VC++ settings from environment, which was
153 # validated above.
153 # validated above.
154 env = dict(os.environ)
154 env = dict(os.environ)
155 env['DISTUTILS_USE_SDK'] = '1'
155 env['DISTUTILS_USE_SDK'] = '1'
156 env['MSSdk'] = '1'
156 env['MSSdk'] = '1'
157
157
158 if extra_packages_script:
158 if extra_packages_script:
159 more_packages = set(
159 more_packages = set(
160 subprocess.check_output(extra_packages_script, cwd=build_dir)
160 subprocess.check_output(extra_packages_script, cwd=build_dir)
161 .split(b'\0')[-1]
161 .split(b'\0')[-1]
162 .strip()
162 .strip()
163 .decode('utf-8')
163 .decode('utf-8')
164 .splitlines()
164 .splitlines()
165 )
165 )
166 if more_packages:
166 if more_packages:
167 if not extra_packages:
167 if not extra_packages:
168 extra_packages = more_packages
168 extra_packages = more_packages
169 else:
169 else:
170 extra_packages |= more_packages
170 extra_packages |= more_packages
171
171
172 if extra_packages:
172 if extra_packages:
173 env['HG_PY2EXE_EXTRA_PACKAGES'] = ' '.join(sorted(extra_packages))
173 env['HG_PY2EXE_EXTRA_PACKAGES'] = ' '.join(sorted(extra_packages))
174 hgext3rd_extras = sorted(
174 hgext3rd_extras = sorted(
175 e for e in extra_packages if e.startswith('hgext3rd.')
175 e for e in extra_packages if e.startswith('hgext3rd.')
176 )
176 )
177 if hgext3rd_extras:
177 if hgext3rd_extras:
178 env['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'] = ' '.join(hgext3rd_extras)
178 env['HG_PY2EXE_EXTRA_INSTALL_PACKAGES'] = ' '.join(hgext3rd_extras)
179 if extra_excludes:
179 if extra_excludes:
180 env['HG_PY2EXE_EXTRA_EXCLUDES'] = ' '.join(sorted(extra_excludes))
180 env['HG_PY2EXE_EXTRA_EXCLUDES'] = ' '.join(sorted(extra_excludes))
181 if extra_dll_excludes:
181 if extra_dll_excludes:
182 env['HG_PY2EXE_EXTRA_DLL_EXCLUDES'] = ' '.join(
182 env['HG_PY2EXE_EXTRA_DLL_EXCLUDES'] = ' '.join(
183 sorted(extra_dll_excludes)
183 sorted(extra_dll_excludes)
184 )
184 )
185
185
186 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
186 py2exe_py_path = venv_path / 'Lib' / 'site-packages' / 'py2exe'
187 if not py2exe_py_path.exists():
187 if not py2exe_py_path.exists():
188 print('building py2exe')
188 print('building py2exe')
189 subprocess.run(
189 subprocess.run(
190 [str(venv_python), 'setup.py', 'install'],
190 [str(venv_python), 'setup.py', 'install'],
191 cwd=py2exe_source_path,
191 cwd=py2exe_source_path,
192 env=env,
192 env=env,
193 check=True,
193 check=True,
194 )
194 )
195
195
196 # Register location of msgfmt and other binaries.
196 # Register location of msgfmt and other binaries.
197 env['PATH'] = '%s%s%s' % (
197 env['PATH'] = '%s%s%s' % (
198 env['PATH'],
198 env['PATH'],
199 os.pathsep,
199 os.pathsep,
200 str(gettext_root / 'bin'),
200 str(gettext_root / 'bin'),
201 )
201 )
202
202
203 print('building Mercurial')
203 print('building Mercurial')
204 subprocess.run(
204 subprocess.run(
205 [str(venv_python), 'setup.py', 'py2exe', 'build_doc', '--html'],
205 [str(venv_python), 'setup.py', 'py2exe', 'build_doc', '--html'],
206 cwd=str(source_dir),
206 cwd=str(source_dir),
207 env=env,
207 env=env,
208 check=True,
208 check=True,
209 )
209 )
210
210
211
211
212 def stage_install(
212 def stage_install(
213 source_dir: pathlib.Path, staging_dir: pathlib.Path, lower_case=False
213 source_dir: pathlib.Path, staging_dir: pathlib.Path, lower_case=False
214 ):
214 ):
215 """Copy all files to be installed to a directory.
215 """Copy all files to be installed to a directory.
216
216
217 This allows packaging to simply walk a directory tree to find source
217 This allows packaging to simply walk a directory tree to find source
218 files.
218 files.
219 """
219 """
220 if lower_case:
220 if lower_case:
221 rules = []
221 rules = []
222 for source, dest in STAGING_RULES:
222 for source, dest in STAGING_RULES:
223 # Only lower directory names.
223 # Only lower directory names.
224 if '/' in dest:
224 if '/' in dest:
225 parent, leaf = dest.rsplit('/', 1)
225 parent, leaf = dest.rsplit('/', 1)
226 dest = '%s/%s' % (parent.lower(), leaf)
226 dest = '%s/%s' % (parent.lower(), leaf)
227 rules.append((source, dest))
227 rules.append((source, dest))
228 else:
228 else:
229 rules = STAGING_RULES
229 rules = STAGING_RULES
230
230
231 process_install_rules(rules, source_dir, staging_dir)
231 process_install_rules(rules, source_dir, staging_dir)
232
232
233 # Write out a default editor.rc file to configure notepad as the
233 # Write out a default editor.rc file to configure notepad as the
234 # default editor.
234 # default editor.
235 with (staging_dir / 'hgrc.d' / 'editor.rc').open(
235 with (staging_dir / 'hgrc.d' / 'editor.rc').open(
236 'w', encoding='utf-8'
236 'w', encoding='utf-8'
237 ) as fh:
237 ) as fh:
238 fh.write('[ui]\neditor = notepad\n')
238 fh.write('[ui]\neditor = notepad\n')
239
239
240 # Purge any files we don't want to be there.
240 # Purge any files we don't want to be there.
241 for f in STAGING_EXCLUDES:
241 for f in STAGING_EXCLUDES:
242 p = staging_dir / f
242 p = staging_dir / f
243 if p.exists():
243 if p.exists():
244 print('removing %s' % p)
244 print('removing %s' % p)
245 p.unlink()
245 p.unlink()
@@ -1,48 +1,48
1 SOURCES=$(notdir $(wildcard ../mercurial/help/*.[0-9].txt))
1 SOURCES=$(notdir $(wildcard ../mercurial/helptext/*.[0-9].txt))
2 MAN=$(SOURCES:%.txt=%)
2 MAN=$(SOURCES:%.txt=%)
3 HTML=$(SOURCES:%.txt=%.html)
3 HTML=$(SOURCES:%.txt=%.html)
4 GENDOC=gendoc.py ../mercurial/commands.py ../mercurial/help.py \
4 GENDOC=gendoc.py ../mercurial/commands.py ../mercurial/help.py \
5 ../mercurial/help/*.txt ../hgext/*.py ../hgext/*/__init__.py
5 ../mercurial/helptext/*.txt ../hgext/*.py ../hgext/*/__init__.py
6 PREFIX=/usr/local
6 PREFIX=/usr/local
7 MANDIR=$(PREFIX)/share/man
7 MANDIR=$(PREFIX)/share/man
8 INSTALL=install -c -m 644
8 INSTALL=install -c -m 644
9 PYTHON?=python
9 PYTHON?=python
10 RSTARGS=
10 RSTARGS=
11
11
12 export HGENCODING=UTF-8
12 export HGENCODING=UTF-8
13
13
14 all: man html
14 all: man html
15
15
16 man: $(MAN)
16 man: $(MAN)
17
17
18 html: $(HTML)
18 html: $(HTML)
19
19
20 # This logic is duplicated in setup.py:hgbuilddoc()
20 # This logic is duplicated in setup.py:hgbuilddoc()
21 common.txt $(SOURCES) $(SOURCES:%.txt=%.gendoc.txt): $(GENDOC)
21 common.txt $(SOURCES) $(SOURCES:%.txt=%.gendoc.txt): $(GENDOC)
22 ${PYTHON} gendoc.py "$(basename $@)" > $@.tmp
22 ${PYTHON} gendoc.py "$(basename $@)" > $@.tmp
23 mv $@.tmp $@
23 mv $@.tmp $@
24
24
25 %: %.txt %.gendoc.txt common.txt
25 %: %.txt %.gendoc.txt common.txt
26 $(PYTHON) runrst hgmanpage $(RSTARGS) --halt warning \
26 $(PYTHON) runrst hgmanpage $(RSTARGS) --halt warning \
27 --strip-elements-with-class htmlonly $*.txt $*
27 --strip-elements-with-class htmlonly $*.txt $*
28
28
29 %.html: %.txt %.gendoc.txt common.txt
29 %.html: %.txt %.gendoc.txt common.txt
30 $(PYTHON) runrst html $(RSTARGS) --halt warning \
30 $(PYTHON) runrst html $(RSTARGS) --halt warning \
31 --link-stylesheet --stylesheet-path style.css $*.txt $*.html
31 --link-stylesheet --stylesheet-path style.css $*.txt $*.html
32
32
33 MANIFEST: man html
33 MANIFEST: man html
34 # tracked files are already in the main MANIFEST
34 # tracked files are already in the main MANIFEST
35 $(RM) $@
35 $(RM) $@
36 for i in $(MAN) $(HTML); do \
36 for i in $(MAN) $(HTML); do \
37 echo "doc/$$i" >> $@ ; \
37 echo "doc/$$i" >> $@ ; \
38 done
38 done
39
39
40 install: man
40 install: man
41 for i in $(MAN) ; do \
41 for i in $(MAN) ; do \
42 subdir=`echo $$i | sed -n 's/^.*\.\([0-9]\)$$/man\1/p'` ; \
42 subdir=`echo $$i | sed -n 's/^.*\.\([0-9]\)$$/man\1/p'` ; \
43 mkdir -p "$(DESTDIR)$(MANDIR)"/$$subdir ; \
43 mkdir -p "$(DESTDIR)$(MANDIR)"/$$subdir ; \
44 $(INSTALL) $$i "$(DESTDIR)$(MANDIR)"/$$subdir ; \
44 $(INSTALL) $$i "$(DESTDIR)$(MANDIR)"/$$subdir ; \
45 done
45 done
46
46
47 clean:
47 clean:
48 $(RM) $(MAN) $(HTML) common.txt $(SOURCES) $(SOURCES:%.txt=%.gendoc.txt) MANIFEST
48 $(RM) $(MAN) $(HTML) common.txt $(SOURCES) $(SOURCES:%.txt=%.gendoc.txt) MANIFEST
@@ -1,467 +1,467
1 # linelog - efficient cache for annotate data
1 # linelog - efficient cache for annotate data
2 #
2 #
3 # Copyright 2018 Google LLC.
3 # Copyright 2018 Google LLC.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """linelog is an efficient cache for annotate data inspired by SCCS Weaves.
7 """linelog is an efficient cache for annotate data inspired by SCCS Weaves.
8
8
9 SCCS Weaves are an implementation of
9 SCCS Weaves are an implementation of
10 https://en.wikipedia.org/wiki/Interleaved_deltas. See
10 https://en.wikipedia.org/wiki/Interleaved_deltas. See
11 mercurial/help/internals/linelog.txt for an exploration of SCCS weaves
11 mercurial/helptext/internals/linelog.txt for an exploration of SCCS weaves
12 and how linelog works in detail.
12 and how linelog works in detail.
13
13
14 Here's a hacker's summary: a linelog is a program which is executed in
14 Here's a hacker's summary: a linelog is a program which is executed in
15 the context of a revision. Executing the program emits information
15 the context of a revision. Executing the program emits information
16 about lines, including the revision that introduced them and the line
16 about lines, including the revision that introduced them and the line
17 number in the file at the introducing revision. When an insertion or
17 number in the file at the introducing revision. When an insertion or
18 deletion is performed on the file, a jump instruction is used to patch
18 deletion is performed on the file, a jump instruction is used to patch
19 in a new body of annotate information.
19 in a new body of annotate information.
20 """
20 """
21 from __future__ import absolute_import, print_function
21 from __future__ import absolute_import, print_function
22
22
23 import abc
23 import abc
24 import struct
24 import struct
25
25
26 from .thirdparty import attr
26 from .thirdparty import attr
27 from . import pycompat
27 from . import pycompat
28
28
29 _llentry = struct.Struct(b'>II')
29 _llentry = struct.Struct(b'>II')
30
30
31
31
32 class LineLogError(Exception):
32 class LineLogError(Exception):
33 """Error raised when something bad happens internally in linelog."""
33 """Error raised when something bad happens internally in linelog."""
34
34
35
35
36 @attr.s
36 @attr.s
37 class lineinfo(object):
37 class lineinfo(object):
38 # Introducing revision of this line.
38 # Introducing revision of this line.
39 rev = attr.ib()
39 rev = attr.ib()
40 # Line number for this line in its introducing revision.
40 # Line number for this line in its introducing revision.
41 linenum = attr.ib()
41 linenum = attr.ib()
42 # Private. Offset in the linelog program of this line. Used internally.
42 # Private. Offset in the linelog program of this line. Used internally.
43 _offset = attr.ib()
43 _offset = attr.ib()
44
44
45
45
46 @attr.s
46 @attr.s
47 class annotateresult(object):
47 class annotateresult(object):
48 rev = attr.ib()
48 rev = attr.ib()
49 lines = attr.ib()
49 lines = attr.ib()
50 _eof = attr.ib()
50 _eof = attr.ib()
51
51
52 def __iter__(self):
52 def __iter__(self):
53 return iter(self.lines)
53 return iter(self.lines)
54
54
55
55
56 class _llinstruction(object): # pytype: disable=ignored-metaclass
56 class _llinstruction(object): # pytype: disable=ignored-metaclass
57
57
58 __metaclass__ = abc.ABCMeta
58 __metaclass__ = abc.ABCMeta
59
59
60 @abc.abstractmethod
60 @abc.abstractmethod
61 def __init__(self, op1, op2):
61 def __init__(self, op1, op2):
62 pass
62 pass
63
63
64 @abc.abstractmethod
64 @abc.abstractmethod
65 def __str__(self):
65 def __str__(self):
66 pass
66 pass
67
67
68 def __repr__(self):
68 def __repr__(self):
69 return str(self)
69 return str(self)
70
70
71 @abc.abstractmethod
71 @abc.abstractmethod
72 def __eq__(self, other):
72 def __eq__(self, other):
73 pass
73 pass
74
74
75 @abc.abstractmethod
75 @abc.abstractmethod
76 def encode(self):
76 def encode(self):
77 """Encode this instruction to the binary linelog format."""
77 """Encode this instruction to the binary linelog format."""
78
78
79 @abc.abstractmethod
79 @abc.abstractmethod
80 def execute(self, rev, pc, emit):
80 def execute(self, rev, pc, emit):
81 """Execute this instruction.
81 """Execute this instruction.
82
82
83 Args:
83 Args:
84 rev: The revision we're annotating.
84 rev: The revision we're annotating.
85 pc: The current offset in the linelog program.
85 pc: The current offset in the linelog program.
86 emit: A function that accepts a single lineinfo object.
86 emit: A function that accepts a single lineinfo object.
87
87
88 Returns:
88 Returns:
89 The new value of pc. Returns None if exeuction should stop
89 The new value of pc. Returns None if exeuction should stop
90 (that is, we've found the end of the file.)
90 (that is, we've found the end of the file.)
91 """
91 """
92
92
93
93
94 class _jge(_llinstruction):
94 class _jge(_llinstruction):
95 """If the current rev is greater than or equal to op1, jump to op2."""
95 """If the current rev is greater than or equal to op1, jump to op2."""
96
96
97 def __init__(self, op1, op2):
97 def __init__(self, op1, op2):
98 self._cmprev = op1
98 self._cmprev = op1
99 self._target = op2
99 self._target = op2
100
100
101 def __str__(self):
101 def __str__(self):
102 return 'JGE %d %d' % (self._cmprev, self._target)
102 return 'JGE %d %d' % (self._cmprev, self._target)
103
103
104 def __eq__(self, other):
104 def __eq__(self, other):
105 return (
105 return (
106 type(self) == type(other)
106 type(self) == type(other)
107 and self._cmprev == other._cmprev
107 and self._cmprev == other._cmprev
108 and self._target == other._target
108 and self._target == other._target
109 )
109 )
110
110
111 def encode(self):
111 def encode(self):
112 return _llentry.pack(self._cmprev << 2, self._target)
112 return _llentry.pack(self._cmprev << 2, self._target)
113
113
114 def execute(self, rev, pc, emit):
114 def execute(self, rev, pc, emit):
115 if rev >= self._cmprev:
115 if rev >= self._cmprev:
116 return self._target
116 return self._target
117 return pc + 1
117 return pc + 1
118
118
119
119
120 class _jump(_llinstruction):
120 class _jump(_llinstruction):
121 """Unconditional jumps are expressed as a JGE with op1 set to 0."""
121 """Unconditional jumps are expressed as a JGE with op1 set to 0."""
122
122
123 def __init__(self, op1, op2):
123 def __init__(self, op1, op2):
124 if op1 != 0:
124 if op1 != 0:
125 raise LineLogError(b"malformed JUMP, op1 must be 0, got %d" % op1)
125 raise LineLogError(b"malformed JUMP, op1 must be 0, got %d" % op1)
126 self._target = op2
126 self._target = op2
127
127
128 def __str__(self):
128 def __str__(self):
129 return 'JUMP %d' % (self._target)
129 return 'JUMP %d' % (self._target)
130
130
131 def __eq__(self, other):
131 def __eq__(self, other):
132 return type(self) == type(other) and self._target == other._target
132 return type(self) == type(other) and self._target == other._target
133
133
134 def encode(self):
134 def encode(self):
135 return _llentry.pack(0, self._target)
135 return _llentry.pack(0, self._target)
136
136
137 def execute(self, rev, pc, emit):
137 def execute(self, rev, pc, emit):
138 return self._target
138 return self._target
139
139
140
140
141 class _eof(_llinstruction):
141 class _eof(_llinstruction):
142 """EOF is expressed as a JGE that always jumps to 0."""
142 """EOF is expressed as a JGE that always jumps to 0."""
143
143
144 def __init__(self, op1, op2):
144 def __init__(self, op1, op2):
145 if op1 != 0:
145 if op1 != 0:
146 raise LineLogError(b"malformed EOF, op1 must be 0, got %d" % op1)
146 raise LineLogError(b"malformed EOF, op1 must be 0, got %d" % op1)
147 if op2 != 0:
147 if op2 != 0:
148 raise LineLogError(b"malformed EOF, op2 must be 0, got %d" % op2)
148 raise LineLogError(b"malformed EOF, op2 must be 0, got %d" % op2)
149
149
150 def __str__(self):
150 def __str__(self):
151 return r'EOF'
151 return r'EOF'
152
152
153 def __eq__(self, other):
153 def __eq__(self, other):
154 return type(self) == type(other)
154 return type(self) == type(other)
155
155
156 def encode(self):
156 def encode(self):
157 return _llentry.pack(0, 0)
157 return _llentry.pack(0, 0)
158
158
159 def execute(self, rev, pc, emit):
159 def execute(self, rev, pc, emit):
160 return None
160 return None
161
161
162
162
163 class _jl(_llinstruction):
163 class _jl(_llinstruction):
164 """If the current rev is less than op1, jump to op2."""
164 """If the current rev is less than op1, jump to op2."""
165
165
166 def __init__(self, op1, op2):
166 def __init__(self, op1, op2):
167 self._cmprev = op1
167 self._cmprev = op1
168 self._target = op2
168 self._target = op2
169
169
170 def __str__(self):
170 def __str__(self):
171 return 'JL %d %d' % (self._cmprev, self._target)
171 return 'JL %d %d' % (self._cmprev, self._target)
172
172
173 def __eq__(self, other):
173 def __eq__(self, other):
174 return (
174 return (
175 type(self) == type(other)
175 type(self) == type(other)
176 and self._cmprev == other._cmprev
176 and self._cmprev == other._cmprev
177 and self._target == other._target
177 and self._target == other._target
178 )
178 )
179
179
180 def encode(self):
180 def encode(self):
181 return _llentry.pack(1 | (self._cmprev << 2), self._target)
181 return _llentry.pack(1 | (self._cmprev << 2), self._target)
182
182
183 def execute(self, rev, pc, emit):
183 def execute(self, rev, pc, emit):
184 if rev < self._cmprev:
184 if rev < self._cmprev:
185 return self._target
185 return self._target
186 return pc + 1
186 return pc + 1
187
187
188
188
189 class _line(_llinstruction):
189 class _line(_llinstruction):
190 """Emit a line."""
190 """Emit a line."""
191
191
192 def __init__(self, op1, op2):
192 def __init__(self, op1, op2):
193 # This line was introduced by this revision number.
193 # This line was introduced by this revision number.
194 self._rev = op1
194 self._rev = op1
195 # This line had the specified line number in the introducing revision.
195 # This line had the specified line number in the introducing revision.
196 self._origlineno = op2
196 self._origlineno = op2
197
197
198 def __str__(self):
198 def __str__(self):
199 return 'LINE %d %d' % (self._rev, self._origlineno)
199 return 'LINE %d %d' % (self._rev, self._origlineno)
200
200
201 def __eq__(self, other):
201 def __eq__(self, other):
202 return (
202 return (
203 type(self) == type(other)
203 type(self) == type(other)
204 and self._rev == other._rev
204 and self._rev == other._rev
205 and self._origlineno == other._origlineno
205 and self._origlineno == other._origlineno
206 )
206 )
207
207
208 def encode(self):
208 def encode(self):
209 return _llentry.pack(2 | (self._rev << 2), self._origlineno)
209 return _llentry.pack(2 | (self._rev << 2), self._origlineno)
210
210
211 def execute(self, rev, pc, emit):
211 def execute(self, rev, pc, emit):
212 emit(lineinfo(self._rev, self._origlineno, pc))
212 emit(lineinfo(self._rev, self._origlineno, pc))
213 return pc + 1
213 return pc + 1
214
214
215
215
216 def _decodeone(data, offset):
216 def _decodeone(data, offset):
217 """Decode a single linelog instruction from an offset in a buffer."""
217 """Decode a single linelog instruction from an offset in a buffer."""
218 try:
218 try:
219 op1, op2 = _llentry.unpack_from(data, offset)
219 op1, op2 = _llentry.unpack_from(data, offset)
220 except struct.error as e:
220 except struct.error as e:
221 raise LineLogError(b'reading an instruction failed: %r' % e)
221 raise LineLogError(b'reading an instruction failed: %r' % e)
222 opcode = op1 & 0b11
222 opcode = op1 & 0b11
223 op1 = op1 >> 2
223 op1 = op1 >> 2
224 if opcode == 0:
224 if opcode == 0:
225 if op1 == 0:
225 if op1 == 0:
226 if op2 == 0:
226 if op2 == 0:
227 return _eof(op1, op2)
227 return _eof(op1, op2)
228 return _jump(op1, op2)
228 return _jump(op1, op2)
229 return _jge(op1, op2)
229 return _jge(op1, op2)
230 elif opcode == 1:
230 elif opcode == 1:
231 return _jl(op1, op2)
231 return _jl(op1, op2)
232 elif opcode == 2:
232 elif opcode == 2:
233 return _line(op1, op2)
233 return _line(op1, op2)
234 raise NotImplementedError(b'Unimplemented opcode %r' % opcode)
234 raise NotImplementedError(b'Unimplemented opcode %r' % opcode)
235
235
236
236
237 class linelog(object):
237 class linelog(object):
238 """Efficient cache for per-line history information."""
238 """Efficient cache for per-line history information."""
239
239
240 def __init__(self, program=None, maxrev=0):
240 def __init__(self, program=None, maxrev=0):
241 if program is None:
241 if program is None:
242 # We pad the program with an extra leading EOF so that our
242 # We pad the program with an extra leading EOF so that our
243 # offsets will match the C code exactly. This means we can
243 # offsets will match the C code exactly. This means we can
244 # interoperate with the C code.
244 # interoperate with the C code.
245 program = [_eof(0, 0), _eof(0, 0)]
245 program = [_eof(0, 0), _eof(0, 0)]
246 self._program = program
246 self._program = program
247 self._lastannotate = None
247 self._lastannotate = None
248 self._maxrev = maxrev
248 self._maxrev = maxrev
249
249
250 def __eq__(self, other):
250 def __eq__(self, other):
251 return (
251 return (
252 type(self) == type(other)
252 type(self) == type(other)
253 and self._program == other._program
253 and self._program == other._program
254 and self._maxrev == other._maxrev
254 and self._maxrev == other._maxrev
255 )
255 )
256
256
257 def __repr__(self):
257 def __repr__(self):
258 return b'<linelog at %s: maxrev=%d size=%d>' % (
258 return b'<linelog at %s: maxrev=%d size=%d>' % (
259 hex(id(self)),
259 hex(id(self)),
260 self._maxrev,
260 self._maxrev,
261 len(self._program),
261 len(self._program),
262 )
262 )
263
263
264 def debugstr(self):
264 def debugstr(self):
265 fmt = '%%%dd %%s' % len(str(len(self._program)))
265 fmt = '%%%dd %%s' % len(str(len(self._program)))
266 return pycompat.sysstr(b'\n').join(
266 return pycompat.sysstr(b'\n').join(
267 fmt % (idx, i) for idx, i in enumerate(self._program[1:], 1)
267 fmt % (idx, i) for idx, i in enumerate(self._program[1:], 1)
268 )
268 )
269
269
270 @classmethod
270 @classmethod
271 def fromdata(cls, buf):
271 def fromdata(cls, buf):
272 if len(buf) % _llentry.size != 0:
272 if len(buf) % _llentry.size != 0:
273 raise LineLogError(
273 raise LineLogError(
274 b"invalid linelog buffer size %d (must be a multiple of %d)"
274 b"invalid linelog buffer size %d (must be a multiple of %d)"
275 % (len(buf), _llentry.size)
275 % (len(buf), _llentry.size)
276 )
276 )
277 expected = len(buf) / _llentry.size
277 expected = len(buf) / _llentry.size
278 fakejge = _decodeone(buf, 0)
278 fakejge = _decodeone(buf, 0)
279 if isinstance(fakejge, _jump):
279 if isinstance(fakejge, _jump):
280 maxrev = 0
280 maxrev = 0
281 elif isinstance(fakejge, (_jge, _jl)):
281 elif isinstance(fakejge, (_jge, _jl)):
282 maxrev = fakejge._cmprev
282 maxrev = fakejge._cmprev
283 else:
283 else:
284 raise LineLogError(
284 raise LineLogError(
285 'Expected one of _jump, _jge, or _jl. Got %s.'
285 'Expected one of _jump, _jge, or _jl. Got %s.'
286 % type(fakejge).__name__
286 % type(fakejge).__name__
287 )
287 )
288 assert isinstance(fakejge, (_jump, _jge, _jl)) # help pytype
288 assert isinstance(fakejge, (_jump, _jge, _jl)) # help pytype
289 numentries = fakejge._target
289 numentries = fakejge._target
290 if expected != numentries:
290 if expected != numentries:
291 raise LineLogError(
291 raise LineLogError(
292 b"corrupt linelog data: claimed"
292 b"corrupt linelog data: claimed"
293 b" %d entries but given data for %d entries"
293 b" %d entries but given data for %d entries"
294 % (expected, numentries)
294 % (expected, numentries)
295 )
295 )
296 instructions = [_eof(0, 0)]
296 instructions = [_eof(0, 0)]
297 for offset in pycompat.xrange(1, numentries):
297 for offset in pycompat.xrange(1, numentries):
298 instructions.append(_decodeone(buf, offset * _llentry.size))
298 instructions.append(_decodeone(buf, offset * _llentry.size))
299 return cls(instructions, maxrev=maxrev)
299 return cls(instructions, maxrev=maxrev)
300
300
301 def encode(self):
301 def encode(self):
302 hdr = _jge(self._maxrev, len(self._program)).encode()
302 hdr = _jge(self._maxrev, len(self._program)).encode()
303 return hdr + b''.join(i.encode() for i in self._program[1:])
303 return hdr + b''.join(i.encode() for i in self._program[1:])
304
304
305 def clear(self):
305 def clear(self):
306 self._program = []
306 self._program = []
307 self._maxrev = 0
307 self._maxrev = 0
308 self._lastannotate = None
308 self._lastannotate = None
309
309
310 def replacelines_vec(self, rev, a1, a2, blines):
310 def replacelines_vec(self, rev, a1, a2, blines):
311 return self.replacelines(
311 return self.replacelines(
312 rev, a1, a2, 0, len(blines), _internal_blines=blines
312 rev, a1, a2, 0, len(blines), _internal_blines=blines
313 )
313 )
314
314
315 def replacelines(self, rev, a1, a2, b1, b2, _internal_blines=None):
315 def replacelines(self, rev, a1, a2, b1, b2, _internal_blines=None):
316 """Replace lines [a1, a2) with lines [b1, b2)."""
316 """Replace lines [a1, a2) with lines [b1, b2)."""
317 if self._lastannotate:
317 if self._lastannotate:
318 # TODO(augie): make replacelines() accept a revision at
318 # TODO(augie): make replacelines() accept a revision at
319 # which we're editing as well as a revision to mark
319 # which we're editing as well as a revision to mark
320 # responsible for the edits. In hg-experimental it's
320 # responsible for the edits. In hg-experimental it's
321 # stateful like this, so we're doing the same thing to
321 # stateful like this, so we're doing the same thing to
322 # retain compatibility with absorb until that's imported.
322 # retain compatibility with absorb until that's imported.
323 ar = self._lastannotate
323 ar = self._lastannotate
324 else:
324 else:
325 ar = self.annotate(rev)
325 ar = self.annotate(rev)
326 # ar = self.annotate(self._maxrev)
326 # ar = self.annotate(self._maxrev)
327 if a1 > len(ar.lines):
327 if a1 > len(ar.lines):
328 raise LineLogError(
328 raise LineLogError(
329 b'%d contains %d lines, tried to access line %d'
329 b'%d contains %d lines, tried to access line %d'
330 % (rev, len(ar.lines), a1)
330 % (rev, len(ar.lines), a1)
331 )
331 )
332 elif a1 == len(ar.lines):
332 elif a1 == len(ar.lines):
333 # Simulated EOF instruction since we're at EOF, which
333 # Simulated EOF instruction since we're at EOF, which
334 # doesn't have a "real" line.
334 # doesn't have a "real" line.
335 a1inst = _eof(0, 0)
335 a1inst = _eof(0, 0)
336 a1info = lineinfo(0, 0, ar._eof)
336 a1info = lineinfo(0, 0, ar._eof)
337 else:
337 else:
338 a1info = ar.lines[a1]
338 a1info = ar.lines[a1]
339 a1inst = self._program[a1info._offset]
339 a1inst = self._program[a1info._offset]
340 programlen = self._program.__len__
340 programlen = self._program.__len__
341 oldproglen = programlen()
341 oldproglen = programlen()
342 appendinst = self._program.append
342 appendinst = self._program.append
343
343
344 # insert
344 # insert
345 blineinfos = []
345 blineinfos = []
346 bappend = blineinfos.append
346 bappend = blineinfos.append
347 if b1 < b2:
347 if b1 < b2:
348 # Determine the jump target for the JGE at the start of
348 # Determine the jump target for the JGE at the start of
349 # the new block.
349 # the new block.
350 tgt = oldproglen + (b2 - b1 + 1)
350 tgt = oldproglen + (b2 - b1 + 1)
351 # Jump to skip the insert if we're at an older revision.
351 # Jump to skip the insert if we're at an older revision.
352 appendinst(_jl(rev, tgt))
352 appendinst(_jl(rev, tgt))
353 for linenum in pycompat.xrange(b1, b2):
353 for linenum in pycompat.xrange(b1, b2):
354 if _internal_blines is None:
354 if _internal_blines is None:
355 bappend(lineinfo(rev, linenum, programlen()))
355 bappend(lineinfo(rev, linenum, programlen()))
356 appendinst(_line(rev, linenum))
356 appendinst(_line(rev, linenum))
357 else:
357 else:
358 newrev, newlinenum = _internal_blines[linenum]
358 newrev, newlinenum = _internal_blines[linenum]
359 bappend(lineinfo(newrev, newlinenum, programlen()))
359 bappend(lineinfo(newrev, newlinenum, programlen()))
360 appendinst(_line(newrev, newlinenum))
360 appendinst(_line(newrev, newlinenum))
361 # delete
361 # delete
362 if a1 < a2:
362 if a1 < a2:
363 if a2 > len(ar.lines):
363 if a2 > len(ar.lines):
364 raise LineLogError(
364 raise LineLogError(
365 b'%d contains %d lines, tried to access line %d'
365 b'%d contains %d lines, tried to access line %d'
366 % (rev, len(ar.lines), a2)
366 % (rev, len(ar.lines), a2)
367 )
367 )
368 elif a2 == len(ar.lines):
368 elif a2 == len(ar.lines):
369 endaddr = ar._eof
369 endaddr = ar._eof
370 else:
370 else:
371 endaddr = ar.lines[a2]._offset
371 endaddr = ar.lines[a2]._offset
372 if a2 > 0 and rev < self._maxrev:
372 if a2 > 0 and rev < self._maxrev:
373 # If we're here, we're deleting a chunk of an old
373 # If we're here, we're deleting a chunk of an old
374 # commit, so we need to be careful and not touch
374 # commit, so we need to be careful and not touch
375 # invisible lines between a2-1 and a2 (IOW, lines that
375 # invisible lines between a2-1 and a2 (IOW, lines that
376 # are added later).
376 # are added later).
377 endaddr = ar.lines[a2 - 1]._offset + 1
377 endaddr = ar.lines[a2 - 1]._offset + 1
378 appendinst(_jge(rev, endaddr))
378 appendinst(_jge(rev, endaddr))
379 # copy instruction from a1
379 # copy instruction from a1
380 a1instpc = programlen()
380 a1instpc = programlen()
381 appendinst(a1inst)
381 appendinst(a1inst)
382 # if a1inst isn't a jump or EOF, then we need to add an unconditional
382 # if a1inst isn't a jump or EOF, then we need to add an unconditional
383 # jump back into the program here.
383 # jump back into the program here.
384 if not isinstance(a1inst, (_jump, _eof)):
384 if not isinstance(a1inst, (_jump, _eof)):
385 appendinst(_jump(0, a1info._offset + 1))
385 appendinst(_jump(0, a1info._offset + 1))
386 # Patch instruction at a1, which makes our patch live.
386 # Patch instruction at a1, which makes our patch live.
387 self._program[a1info._offset] = _jump(0, oldproglen)
387 self._program[a1info._offset] = _jump(0, oldproglen)
388
388
389 # Update self._lastannotate in place. This serves as a cache to avoid
389 # Update self._lastannotate in place. This serves as a cache to avoid
390 # expensive "self.annotate" in this function, when "replacelines" is
390 # expensive "self.annotate" in this function, when "replacelines" is
391 # used continuously.
391 # used continuously.
392 if len(self._lastannotate.lines) > a1:
392 if len(self._lastannotate.lines) > a1:
393 self._lastannotate.lines[a1]._offset = a1instpc
393 self._lastannotate.lines[a1]._offset = a1instpc
394 else:
394 else:
395 assert isinstance(a1inst, _eof)
395 assert isinstance(a1inst, _eof)
396 self._lastannotate._eof = a1instpc
396 self._lastannotate._eof = a1instpc
397 self._lastannotate.lines[a1:a2] = blineinfos
397 self._lastannotate.lines[a1:a2] = blineinfos
398 self._lastannotate.rev = max(self._lastannotate.rev, rev)
398 self._lastannotate.rev = max(self._lastannotate.rev, rev)
399
399
400 if rev > self._maxrev:
400 if rev > self._maxrev:
401 self._maxrev = rev
401 self._maxrev = rev
402
402
403 def annotate(self, rev):
403 def annotate(self, rev):
404 pc = 1
404 pc = 1
405 lines = []
405 lines = []
406 executed = 0
406 executed = 0
407 # Sanity check: if instructions executed exceeds len(program), we
407 # Sanity check: if instructions executed exceeds len(program), we
408 # hit an infinite loop in the linelog program somehow and we
408 # hit an infinite loop in the linelog program somehow and we
409 # should stop.
409 # should stop.
410 while pc is not None and executed < len(self._program):
410 while pc is not None and executed < len(self._program):
411 inst = self._program[pc]
411 inst = self._program[pc]
412 lastpc = pc
412 lastpc = pc
413 pc = inst.execute(rev, pc, lines.append)
413 pc = inst.execute(rev, pc, lines.append)
414 executed += 1
414 executed += 1
415 if pc is not None:
415 if pc is not None:
416 raise LineLogError(
416 raise LineLogError(
417 r'Probably hit an infinite loop in linelog. Program:\n'
417 r'Probably hit an infinite loop in linelog. Program:\n'
418 + self.debugstr()
418 + self.debugstr()
419 )
419 )
420 ar = annotateresult(rev, lines, lastpc)
420 ar = annotateresult(rev, lines, lastpc)
421 self._lastannotate = ar
421 self._lastannotate = ar
422 return ar
422 return ar
423
423
424 @property
424 @property
425 def maxrev(self):
425 def maxrev(self):
426 return self._maxrev
426 return self._maxrev
427
427
428 # Stateful methods which depend on the value of the last
428 # Stateful methods which depend on the value of the last
429 # annotation run. This API is for compatiblity with the original
429 # annotation run. This API is for compatiblity with the original
430 # linelog, and we should probably consider refactoring it.
430 # linelog, and we should probably consider refactoring it.
431 @property
431 @property
432 def annotateresult(self):
432 def annotateresult(self):
433 """Return the last annotation result. C linelog code exposed this."""
433 """Return the last annotation result. C linelog code exposed this."""
434 return [(l.rev, l.linenum) for l in self._lastannotate.lines]
434 return [(l.rev, l.linenum) for l in self._lastannotate.lines]
435
435
436 def getoffset(self, line):
436 def getoffset(self, line):
437 return self._lastannotate.lines[line]._offset
437 return self._lastannotate.lines[line]._offset
438
438
439 def getalllines(self, start=0, end=0):
439 def getalllines(self, start=0, end=0):
440 """Get all lines that ever occurred in [start, end).
440 """Get all lines that ever occurred in [start, end).
441
441
442 Passing start == end == 0 means "all lines ever".
442 Passing start == end == 0 means "all lines ever".
443
443
444 This works in terms of *internal* program offsets, not line numbers.
444 This works in terms of *internal* program offsets, not line numbers.
445 """
445 """
446 pc = start or 1
446 pc = start or 1
447 lines = []
447 lines = []
448 # only take as many steps as there are instructions in the
448 # only take as many steps as there are instructions in the
449 # program - if we don't find an EOF or our stop-line before
449 # program - if we don't find an EOF or our stop-line before
450 # then, something is badly broken.
450 # then, something is badly broken.
451 for step in pycompat.xrange(len(self._program)):
451 for step in pycompat.xrange(len(self._program)):
452 inst = self._program[pc]
452 inst = self._program[pc]
453 nextpc = pc + 1
453 nextpc = pc + 1
454 if isinstance(inst, _jump):
454 if isinstance(inst, _jump):
455 nextpc = inst._target
455 nextpc = inst._target
456 elif isinstance(inst, _eof):
456 elif isinstance(inst, _eof):
457 return lines
457 return lines
458 elif isinstance(inst, (_jl, _jge)):
458 elif isinstance(inst, (_jl, _jge)):
459 pass
459 pass
460 elif isinstance(inst, _line):
460 elif isinstance(inst, _line):
461 lines.append((inst._rev, inst._origlineno))
461 lines.append((inst._rev, inst._origlineno))
462 else:
462 else:
463 raise LineLogError(b"Illegal instruction %r" % inst)
463 raise LineLogError(b"Illegal instruction %r" % inst)
464 if nextpc == end:
464 if nextpc == end:
465 return lines
465 return lines
466 pc = nextpc
466 pc = nextpc
467 raise LineLogError(b"Failed to perform getalllines")
467 raise LineLogError(b"Failed to perform getalllines")
General Comments 0
You need to be logged in to leave comments. Login now