Compare commits
No commits in common. "debian/master" and "upstream" have entirely different histories.
debian/mas
...
upstream
64 changed files with 1365 additions and 4581 deletions
24
.github/workflows/disperse.yml
vendored
24
.github/workflows/disperse.yml
vendored
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
name: Disperse configuration
|
||||
|
||||
"on":
|
||||
- push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt install protobuf-compiler
|
||||
- name: Install disperse
|
||||
run: |
|
||||
pip install git+https://github.com/jelmer/disperse
|
||||
- name: Validate disperse.conf
|
||||
run: |
|
||||
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python disperse validate .
|
27
.github/workflows/pythonpackage.yml
vendored
27
.github/workflows/pythonpackage.yml
vendored
|
@ -1,11 +1,6 @@
|
|||
---
|
||||
name: Python package
|
||||
|
||||
"on":
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 6 * * *' # Daily 6AM UTC build
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -14,7 +9,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
python-version: [3.7, 3.8, 3.9, '3.10']
|
||||
python-version: [3.7, 3.8]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
|
@ -25,28 +20,28 @@ jobs:
|
|||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -e ".[remote,dep_server,dev]"
|
||||
python -m pip install --upgrade pip flake8 cython
|
||||
python setup.py develop
|
||||
- name: Install Debian-specific dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install python3-wheel libapt-pkg-dev
|
||||
python -m pip install \
|
||||
python_apt@git+https://salsa.debian.org/apt-team/python-apt.git
|
||||
sudo apt install libapt-pkg-dev
|
||||
python -m pip install wheel
|
||||
python -m pip install git+https://salsa.debian.org/apt-team/python-apt
|
||||
python -m pip install -e ".[debian]"
|
||||
python -m pip install testtools
|
||||
mkdir -p ~/.config/breezy/plugins
|
||||
brz branch lp:brz-debian ~/.config/breezy/plugins/debian
|
||||
if: "matrix.python-version != 'pypy3' && matrix.os == 'ubuntu-latest'"
|
||||
- name: Style checks
|
||||
run: |
|
||||
pip install flake8
|
||||
python -m flake8
|
||||
- name: Typing checks
|
||||
run: |
|
||||
pip install -U mypy types-toml
|
||||
pip install -U mypy
|
||||
python -m mypy ognibuild
|
||||
if: "matrix.python-version != 'pypy3'"
|
||||
- name: Test suite run
|
||||
run: |
|
||||
python -m unittest tests.test_suite
|
||||
python -m unittest ognibuild.tests.test_suite
|
||||
env:
|
||||
PYTHONHASHSEED: random
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,3 @@
|
|||
.coverage
|
||||
build
|
||||
*~
|
||||
ognibuild.egg-info
|
||||
|
|
20
Makefile
20
Makefile
|
@ -1,20 +0,0 @@
|
|||
check:: style
|
||||
|
||||
style:
|
||||
flake8
|
||||
|
||||
check:: testsuite
|
||||
|
||||
testsuite:
|
||||
python3 -m unittest tests.test_suite
|
||||
|
||||
check:: typing
|
||||
|
||||
typing:
|
||||
mypy ognibuild tests
|
||||
|
||||
coverage:
|
||||
python3 -m coverage run -m unittest tests.test_suite
|
||||
|
||||
coverage-html:
|
||||
python3 -m coverage html
|
17
PKG-INFO
Normal file
17
PKG-INFO
Normal file
|
@ -0,0 +1,17 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: ognibuild
|
||||
Version: 0.0.7
|
||||
Summary: Detect and run any build system
|
||||
Home-page: https://jelmer.uk/code/ognibuild
|
||||
Maintainer: Jelmer Vernooij
|
||||
Maintainer-email: jelmer@jelmer.uk
|
||||
License: GNU GPLv2 or later
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Operating System :: POSIX
|
||||
Provides-Extra: debian
|
|
@ -31,12 +31,6 @@ Ognibuild has a number of subcommands:
|
|||
It also includes a subcommand that can fix up the build dependencies
|
||||
for Debian packages, called deb-fix-build.
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
ogni -d https://gitlab.gnome.org/GNOME/fractal install
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Ognibuild is functional, but sometimes rough around the edges. If you run into
|
||||
|
|
62
debian/changelog
vendored
62
debian/changelog
vendored
|
@ -1,62 +0,0 @@
|
|||
ognibuild (0.0.15-0.1) UNRELEASED; urgency=low
|
||||
|
||||
* Non-maintainer upload.
|
||||
* New upstream release.
|
||||
* Bump debhelper from old 12 to 13.
|
||||
* Update standards version to 4.6.1, no changes needed.
|
||||
|
||||
-- Tianyu Chen <billchenchina2001@gmail.com> Tue, 22 Nov 2022 11:12:48 +0800
|
||||
|
||||
ognibuild (0.0.7-1) UNRELEASED; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Debian Janitor <janitor@jelmer.uk> Wed, 02 Jun 2021 15:12:48 -0000
|
||||
|
||||
ognibuild (0.0.6+git20210517.1.8189e91-1) unstable; urgency=low
|
||||
|
||||
* New upstream snapshot.
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Tue, 18 May 2021 20:53:15 +0100
|
||||
|
||||
ognibuild (0.0.5-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
+ Fixes cmake support. Closes: #988572
|
||||
+ Preserve environment when building Python packages. Closes: #988571
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Tue, 18 May 2021 01:34:11 +0100
|
||||
|
||||
ognibuild (0.0.4-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Wed, 07 Apr 2021 00:11:09 +0100
|
||||
|
||||
ognibuild (0.0.3-1) unstable; urgency=medium
|
||||
|
||||
* Add missing dependency on python3-lz4.
|
||||
* Set upstream metadata fields: Security-Contact.
|
||||
* Add upstream signing keys.
|
||||
* New upstream release.
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 27 Mar 2021 17:49:29 +0000
|
||||
|
||||
ognibuild (0.0.2-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Tue, 02 Mar 2021 18:25:45 +0000
|
||||
|
||||
ognibuild (0.0.1~git20210228.bc79314-1) unstable; urgency=medium
|
||||
|
||||
* New upstream snapshot.
|
||||
* Add dependency on buildlog-consultant.
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Sun, 28 Feb 2021 14:55:03 +0000
|
||||
|
||||
ognibuild (0.0.1~git20201031.4cbc8df-1) unstable; urgency=low
|
||||
|
||||
* Initial release. Closes: #981913
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Fri, 05 Feb 2021 03:00:40 +0000
|
43
debian/control
vendored
43
debian/control
vendored
|
@ -1,43 +0,0 @@
|
|||
Rules-Requires-Root: no
|
||||
Standards-Version: 4.6.1
|
||||
Build-Depends: debhelper-compat (= 13),
|
||||
dh-sequence-python3,
|
||||
python3-all,
|
||||
python3-apt,
|
||||
python3-breezy,
|
||||
python3-breezy.tests,
|
||||
python3-buildlog-consultant (>= 0.0.4),
|
||||
python3-requirement-parser,
|
||||
python3-debmutate,
|
||||
python3-lz4,
|
||||
python3-setuptools,
|
||||
brz-debian,
|
||||
python3-dulwich (>= 0.19.12)
|
||||
Source: ognibuild
|
||||
Priority: optional
|
||||
Section: devel
|
||||
Maintainer: Jelmer Vernooij <jelmer@debian.org>
|
||||
Vcs-Git: https://salsa.debian.org/jelmer/ognibuild.git
|
||||
Vcs-Browser: https://salsa.debian.org/jelmer/ognibuild
|
||||
Homepage: https://github.com/jelmer/ognibuild
|
||||
|
||||
Package: ognibuild
|
||||
Architecture: all
|
||||
Depends: python3-apt,
|
||||
python3-breezy,
|
||||
python3-buildlog-consultant (>= 0.0.4),
|
||||
python3-lz4,
|
||||
python3-requirement-parser,
|
||||
python3-toml,
|
||||
${misc:Depends},
|
||||
${python3:Depends},
|
||||
Recommends: brz-debian, python3-debmutate
|
||||
Description: Detect and run any build system
|
||||
Ognibuild is a simple wrapper with a common interface for invoking any kind of
|
||||
build tool.
|
||||
.
|
||||
The ideas is that it can be run to build and install any source code directory
|
||||
by detecting the build system that is in use and invoking that with the correct
|
||||
parameters.
|
||||
.
|
||||
It can also detect and install missing dependencies.
|
28
debian/copyright
vendored
28
debian/copyright
vendored
|
@ -1,28 +0,0 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: ognibuild
|
||||
|
||||
Files: *
|
||||
Copyright: Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
License: GPL-2+
|
||||
|
||||
Files: debian/*
|
||||
Copyright: Jelmer Vernooij <jelmer@debian.org>
|
||||
License: GPL-2+
|
||||
|
||||
License: GPL-2+
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
.
|
||||
On Debian systems, the full text of the GNU General Public License is available
|
||||
in /usr/share/common-licenses/GPL-2.
|
4
debian/rules
vendored
4
debian/rules
vendored
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=pybuild
|
1
debian/source/format
vendored
1
debian/source/format
vendored
|
@ -1 +0,0 @@
|
|||
3.0 (quilt)
|
5
debian/tests/control
vendored
5
debian/tests/control
vendored
|
@ -1,5 +0,0 @@
|
|||
Test-Command: python3 -m unittest ognibuild.tests.test_suite
|
||||
Depends: python3,
|
||||
@,
|
||||
lintian-brush, brz-debian, python3-breezy.tests
|
||||
Restrictions: allow-stderr
|
6
debian/upstream/metadata
vendored
6
debian/upstream/metadata
vendored
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
Bug-Database: https://github.com/jelmer/ognibuild/issues
|
||||
Bug-Submit: https://github.com/jelmer/ognibuild/issues/new
|
||||
Repository: https://github.com/jelmer/ognibuild.git
|
||||
Repository-Browse: https://github.com/jelmer/ognibuild
|
||||
Security-Contact: https://github.com/jelmer/ognibuild/tree/HEAD/SECURITY.md
|
858
debian/upstream/signing-key.asc
vendored
858
debian/upstream/signing-key.asc
vendored
|
@ -1,858 +0,0 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
cmtpbmcgZm9yIFNlck5ldAAKCRAAgG8r1ymkVwiCD/9Lt1XSCMadKZrZPfo0y9Np
|
||||
lkEgOxfxycOvnKYrZh+xBynJRDhqGyEQ7XDli5KIO209IIfuRVXvP/pZgb1rpZRa
|
||||
oiqVGtCYD6ITXGzNLwwyPFsdSh05uco7dYS6Cbkvkkwtql858816a+TupnPlO3Ve
|
||||
DzEEm/368Rx0NdNaMoDirNqSPClOOeIiJN+1vl3GyY9uMCP7P3dAG6q7GsowLbm8
|
||||
AjYnBDJb3XJK8u85l09NqtB3qyuOIINtBC/QFK6+Svb2UcFKdOpztNqQCd4Rf6k/
|
||||
o1ASlHBBr3o2RkRQr2LIk8/HFiOOJzkMGwfTDXOO3BueHpqWCmFU0TQw2gwxhLPA
|
||||
opbe/Bbo8mHXG9cKATCsqOGI0/BY9W036WEwadcy24RoZ/DhiJh/+c8Py9u3ZYdf
|
||||
g56BO3Wy4MdtvSkDuZx0lSRql+X72DD5SveRwQed68CIOkHFWIa5iQezk+XdOqjb
|
||||
i56T14jUEVWWq6p0d2Jw3obBy/uaEA53oxwAHckOURXMTzXKTqrhqRrQbcOQJTed
|
||||
RW/8SdL41FNd3NvswjkP9fGnzsPJbOmHHyaZxEgfbZI1NiuQ9hvNwlPHQS3Qzvmu
|
||||
t58eLwU9ofTDDmG/X8NSmSSKV7nRbENyFQHCKTjmJoqxpj55MKg9JCqlCJfaC1vt
|
||||
7TkRVVytJ0HahFXOyAiTTLQjSmVsbWVyIFZlcm5vb2lqIDxqZWxtZXJAYXBhY2hl
|
||||
Lm9yZz6JAlQEEwEKAD4CGwMCHgECF4AFCwkIBwMFFQoJCAsFFgIDAQAWIQTcg37h
|
||||
Sn43NH6HBhcAgG8r1ymkVwUCYOuSgQUJGWCzAAAKCRAAgG8r1ymkV/nLD/9QbEYT
|
||||
f0rlHJjgVa1u5FLjyBk9n19jleS11GsnKxmSmdvL3H28mE/MmSPPCHXatRL+ufjk
|
||||
r7CPD2NJIvClehrr1aS1ZscWsx+DYWRC2JoAPVIgNywn2QdrWh3S3UYsY+9yFkS1
|
||||
plSskWnaHWz+jJmnO0oI2EeLyTByUL8uY6GYyR6VlfMUi1yj1LH1CGKzr8g8gNNn
|
||||
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
|
||||
uwFmF5mevYO27rIdZyWaU3k+iblrTKzwsROFhTOvAWJhN7WhwOrfAGUpxUKwc9Em
|
||||
itX+7mib0MaK0UnIZtHR6SSbr9wpATaHhCaQ5ifW6ps3zsqW3fa4A6nIZ+leaG8e
|
||||
GBaIVU8RcqBlRFFu4pw8wYqRnIEFvsOh0YopLBMP9OtZmWjKbzV4CyYwMNbVdjaU
|
||||
CMf9lZvKlBVRDM2e9k5RRuq/Nbf/dhKUuqYh8vASpcL8b911+f5n1LFneMLXrWSC
|
||||
zS2msT2F1I/JyVV1oQdPZXzsa14sHlwBvHcGCQiZChBuJJGD1mPT4Wuz0SzQSzGJ
|
||||
f2EB9teqhFw0XVC0K40y9VVhkW1lRe+W441w0qFm725FNi1Cycm5JSeAEluOrT0V
|
||||
tyJYuIWoNEpvbdCpNP5LT7lJ7uHv96C69aGGSYaE4PQ+CcO/l9tUAF3chOPxViM1
|
||||
L0MgsBx9IXgtmqMd8xEw/NHHErn0pg8WhWaXiQJ1BCgBCgBfFiEE3IN+4Up+NzR+
|
||||
hwYXAIBvK9cppFcFAlns7CtBHQJodHRwczovL3d3dy55dWJpY28uY29tL3N1cHBv
|
||||
cnQvc2VjdXJpdHktYWR2aXNvcmllcy95c2EtMjAxNy0wMS8ACgkQAIBvK9cppFdh
|
||||
wA//WcTBgLqimYVJJIvEcdl/GdQtOLl5bZY2oX1SEXpx7Fj47MiMn1DHS1e0G1e7
|
||||
ZOQ9NWNTfIFvQHDCtUCfS+VbdQshkzEwZdwJzC9FAZLtGs7W0KqvmoNjgSTokLl4
|
||||
21wna6WbgYkgKC4OYCD4YpsojueT6YpDouO9/d0QKtTrY/oGYVXAKockLMwwGZcm
|
||||
atavjsppBZRGKtZ2kbPZHaw42jgb0OBDKRcolWp1+R0SE7CF9JBOGYXmFvcTtx0s
|
||||
xf2L8yDjthI7i0A4CGdP/jfTTqsNrT1pn3psNmB6OMdL0qipcJ3/r5bRB/50je/J
|
||||
Yo3Sgo8BfH+F6bn6w+dpiN+uqDIXZftXCYwIyguG68RAxNTCdJkafL5qbR7TOoLH
|
||||
V6FyawcnlxdASIsm7SsTttyXtv7QxTd8pOfbrZQikUpLXbIUVGnElthzGsDmtxiC
|
||||
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
|
||||
vA==
|
||||
=e6vU
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
2
debian/watch
vendored
2
debian/watch
vendored
|
@ -1,2 +0,0 @@
|
|||
version=4
|
||||
opts=pgpsigurlmangle=s/$/.asc/ https://pypi.debian.net/ognibuild/ognibuild-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
|
|
@ -1,8 +0,0 @@
|
|||
# See https://github.com/jelmer/disperse
|
||||
timeout_days: 5
|
||||
tag_name: "v$VERSION"
|
||||
verify_command: "python3 -m unittest tests.test_suite"
|
||||
update_version {
|
||||
path: "ognibuild/__init__.py"
|
||||
new_line: "__version__ = $TUPLED_VERSION"
|
||||
}
|
17
ognibuild.egg-info/PKG-INFO
Normal file
17
ognibuild.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,17 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: ognibuild
|
||||
Version: 0.0.7
|
||||
Summary: Detect and run any build system
|
||||
Home-page: https://jelmer.uk/code/ognibuild
|
||||
Maintainer: Jelmer Vernooij
|
||||
Maintainer-email: jelmer@jelmer.uk
|
||||
License: GNU GPLv2 or later
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 4 - Beta
|
||||
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Operating System :: POSIX
|
||||
Provides-Extra: debian
|
52
ognibuild.egg-info/SOURCES.txt
Normal file
52
ognibuild.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,52 @@
|
|||
.flake8
|
||||
.gitignore
|
||||
AUTHORS
|
||||
CODE_OF_CONDUCT.md
|
||||
LICENSE
|
||||
README.md
|
||||
SECURITY.md
|
||||
TODO
|
||||
releaser.conf
|
||||
setup.cfg
|
||||
setup.py
|
||||
.github/workflows/pythonpackage.yml
|
||||
notes/architecture.md
|
||||
notes/concepts.md
|
||||
notes/roadmap.md
|
||||
ognibuild/__init__.py
|
||||
ognibuild/__main__.py
|
||||
ognibuild/build.py
|
||||
ognibuild/buildlog.py
|
||||
ognibuild/buildsystem.py
|
||||
ognibuild/clean.py
|
||||
ognibuild/dist.py
|
||||
ognibuild/dist_catcher.py
|
||||
ognibuild/fix_build.py
|
||||
ognibuild/fixers.py
|
||||
ognibuild/info.py
|
||||
ognibuild/install.py
|
||||
ognibuild/outputs.py
|
||||
ognibuild/requirements.py
|
||||
ognibuild/test.py
|
||||
ognibuild/vcs.py
|
||||
ognibuild.egg-info/PKG-INFO
|
||||
ognibuild.egg-info/SOURCES.txt
|
||||
ognibuild.egg-info/dependency_links.txt
|
||||
ognibuild.egg-info/entry_points.txt
|
||||
ognibuild.egg-info/requires.txt
|
||||
ognibuild.egg-info/top_level.txt
|
||||
ognibuild/debian/__init__.py
|
||||
ognibuild/debian/apt.py
|
||||
ognibuild/debian/build.py
|
||||
ognibuild/debian/build_deps.py
|
||||
ognibuild/debian/file_search.py
|
||||
ognibuild/debian/fix_build.py
|
||||
ognibuild/debian/udd.py
|
||||
ognibuild/resolver/__init__.py
|
||||
ognibuild/resolver/apt.py
|
||||
ognibuild/session/__init__.py
|
||||
ognibuild/session/plain.py
|
||||
ognibuild/session/schroot.py
|
||||
ognibuild/tests/__init__.py
|
||||
ognibuild/tests/test_debian_build.py
|
||||
ognibuild/tests/test_debian_fix_build.py
|
1
ognibuild.egg-info/dependency_links.txt
Normal file
1
ognibuild.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
4
ognibuild.egg-info/entry_points.txt
Normal file
4
ognibuild.egg-info/entry_points.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
[console_scripts]
|
||||
deb-fix-build = ognibuild.debian.fix_build:main
|
||||
ogni = ognibuild.__main__:main
|
||||
|
8
ognibuild.egg-info/requires.txt
Normal file
8
ognibuild.egg-info/requires.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
breezy
|
||||
buildlog-consultant>=0.0.10
|
||||
requirements-parser
|
||||
|
||||
[debian]
|
||||
debmutate
|
||||
python_apt
|
||||
python_debian
|
1
ognibuild.egg-info/top_level.txt
Normal file
1
ognibuild.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
ognibuild
|
|
@ -18,14 +18,12 @@
|
|||
|
||||
import os
|
||||
import stat
|
||||
from typing import List, Dict, Type
|
||||
|
||||
|
||||
__version__ = (0, 0, 15)
|
||||
version_string = '.'.join(map(str, __version__))
|
||||
__version__ = (0, 0, 7)
|
||||
|
||||
|
||||
USER_AGENT = f"Ognibuild/{version_string}"
|
||||
USER_AGENT = "Ognibuild"
|
||||
|
||||
|
||||
class DetailedFailure(Exception):
|
||||
|
@ -34,12 +32,6 @@ class DetailedFailure(Exception):
|
|||
self.argv = argv
|
||||
self.error = error
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, type(self)) and
|
||||
self.retcode == other.retcode and
|
||||
self.argv == other.argv and
|
||||
self.error == other.error)
|
||||
|
||||
|
||||
class UnidentifiedError(Exception):
|
||||
"""An unidentified error."""
|
||||
|
@ -50,13 +42,6 @@ class UnidentifiedError(Exception):
|
|||
self.lines = lines
|
||||
self.secondary = secondary
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, type(self)) and
|
||||
self.retcode == other.retcode and
|
||||
self.argv == other.argv and
|
||||
self.lines == other.lines and
|
||||
self.secondary == other.secondary)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s(%r, %r, ..., secondary=%r)>" % (
|
||||
type(self).__name__,
|
||||
|
@ -79,64 +64,17 @@ def shebang_binary(p):
|
|||
return os.path.basename(args[0].decode()).strip()
|
||||
|
||||
|
||||
class UnknownRequirementFamily(Exception):
|
||||
"""Requirement family is unknown"""
|
||||
|
||||
def __init__(self, family):
|
||||
self.family = family
|
||||
|
||||
|
||||
class Requirement(object):
|
||||
|
||||
# Name of the family of requirements - e.g. "python-package"
|
||||
family: str
|
||||
|
||||
_JSON_DESERIALIZERS: Dict[str, Type["Requirement"]] = {}
|
||||
|
||||
@classmethod
|
||||
def _from_json(self, js):
|
||||
raise NotImplementedError(self._from_json)
|
||||
|
||||
@classmethod
|
||||
def from_json(self, js):
|
||||
try:
|
||||
family = Requirement._JSON_DESERIALIZERS[js[0]]
|
||||
except KeyError:
|
||||
raise UnknownRequirementFamily(js[0])
|
||||
return family._from_json(js[1])
|
||||
def __init__(self, family):
|
||||
self.family = family
|
||||
|
||||
def met(self, session):
|
||||
raise NotImplementedError(self)
|
||||
|
||||
def _json(self):
|
||||
raise NotImplementedError(self._json)
|
||||
|
||||
def json(self):
|
||||
return (type(self).family, self._json())
|
||||
|
||||
@classmethod
|
||||
def register_json(cls, subcls):
|
||||
Requirement._JSON_DESERIALIZERS[subcls.family] = subcls
|
||||
|
||||
|
||||
class OneOfRequirement(Requirement):
|
||||
|
||||
elements: List[Requirement]
|
||||
|
||||
family = 'or'
|
||||
|
||||
def __init__(self, elements):
|
||||
self.elements = elements
|
||||
|
||||
def met(self, session):
|
||||
for req in self.elements:
|
||||
if req.met(session):
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.elements)
|
||||
|
||||
|
||||
class UpstreamOutput(object):
|
||||
def __init__(self, family):
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from contextlib import ExitStack
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
from . import UnidentifiedError, DetailedFailure, version_string
|
||||
from . import UnidentifiedError, DetailedFailure
|
||||
from .buildlog import (
|
||||
InstallFixer,
|
||||
ExplainInstallFixer,
|
||||
|
@ -31,10 +29,9 @@ from .buildlog import (
|
|||
from .buildsystem import NoBuildToolsFound, detect_buildsystems
|
||||
from .resolver import (
|
||||
auto_resolver,
|
||||
select_resolvers,
|
||||
UnsatisfiedRequirements,
|
||||
native_resolvers,
|
||||
)
|
||||
from .session import SessionSetupFailure
|
||||
from .resolver.apt import AptResolver
|
||||
|
||||
|
||||
def display_explain_commands(commands):
|
||||
|
@ -42,33 +39,34 @@ def display_explain_commands(commands):
|
|||
for command, reqs in commands:
|
||||
if isinstance(command, list):
|
||||
command = shlex.join(command)
|
||||
logging.info(
|
||||
" %s (to install %s)", command, ", ".join(map(str, reqs)))
|
||||
logging.info(" %s (to install %s)", command, ", ".join(map(str, reqs)))
|
||||
|
||||
|
||||
def get_necessary_declared_requirements(resolver, requirements, stages):
|
||||
missing = []
|
||||
for stage, req in requirements:
|
||||
if stage in stages:
|
||||
missing.append(req)
|
||||
return missing
|
||||
|
||||
|
||||
def install_necessary_declared_requirements(
|
||||
session, resolver, fixers, buildsystems, stages, explain=False
|
||||
):
|
||||
relevant = []
|
||||
declared_reqs = []
|
||||
for buildsystem in buildsystems:
|
||||
try:
|
||||
declared_reqs.extend(buildsystem.get_declared_dependencies(session, fixers))
|
||||
except NotImplementedError:
|
||||
logging.warning(
|
||||
"Unable to determine declared dependencies from %r", buildsystem
|
||||
)
|
||||
relevant.extend(
|
||||
get_necessary_declared_requirements(resolver, declared_reqs, stages)
|
||||
)
|
||||
|
||||
if explain:
|
||||
relevant = []
|
||||
for buildsystem in buildsystems:
|
||||
declared_reqs = buildsystem.get_declared_dependencies(
|
||||
session, fixers)
|
||||
for stage, req in declared_reqs:
|
||||
if stage in stages:
|
||||
relevant.append(req)
|
||||
install_missing_reqs(session, resolver, relevant, explain=True)
|
||||
else:
|
||||
for buildsystem in buildsystems:
|
||||
try:
|
||||
buildsystem.install_declared_requirements(
|
||||
stages, session, resolver, fixers)
|
||||
except NotImplementedError:
|
||||
logging.warning(
|
||||
"Unable to determine declared dependencies from %r",
|
||||
buildsystem
|
||||
)
|
||||
install_missing_reqs(session, resolver, relevant, explain=explain)
|
||||
|
||||
|
||||
# Types of dependencies:
|
||||
|
@ -84,7 +82,6 @@ STAGE_MAP = {
|
|||
"test": ["test", "build", "core"],
|
||||
"build": ["build", "core"],
|
||||
"clean": [],
|
||||
"verify": ["build", "core", "test"],
|
||||
}
|
||||
|
||||
|
||||
|
@ -98,13 +95,9 @@ def determine_fixers(session, resolver, explain=False):
|
|||
def main(): # noqa: C901
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(prog='ogni')
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--version", action="version", version="%(prog)s " + version_string
|
||||
)
|
||||
parser.add_argument(
|
||||
"--directory", "-d", type=str, help="Directory for project.",
|
||||
default="."
|
||||
"--directory", "-d", type=str, help="Directory for project.", default="."
|
||||
)
|
||||
parser.add_argument("--schroot", type=str, help="schroot to run in.")
|
||||
parser.add_argument(
|
||||
|
@ -130,15 +123,6 @@ def main(): # noqa: C901
|
|||
action="store_true",
|
||||
help="Ignore declared dependencies, follow build errors only",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--user", action="store_true",
|
||||
help="Install in local-user directories."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dep-server-url", type=str,
|
||||
help="ognibuild dep server to use",
|
||||
default=os.environ.get('OGNIBUILD_DEPS'))
|
||||
|
||||
parser.add_argument("--verbose", action="store_true", help="Be verbose")
|
||||
subparsers = parser.add_subparsers(dest="subcommand")
|
||||
subparsers.add_parser("dist")
|
||||
|
@ -146,11 +130,12 @@ def main(): # noqa: C901
|
|||
subparsers.add_parser("clean")
|
||||
subparsers.add_parser("test")
|
||||
subparsers.add_parser("info")
|
||||
subparsers.add_parser("verify")
|
||||
exec_parser = subparsers.add_parser("exec")
|
||||
exec_parser.add_argument(
|
||||
'subargv', nargs=argparse.REMAINDER, help='Command to run.')
|
||||
exec_parser.add_argument('subargv', nargs=argparse.REMAINDER, help='Command to run.')
|
||||
install_parser = subparsers.add_parser("install")
|
||||
install_parser.add_argument(
|
||||
"--user", action="store_true", help="Install in local-user directories."
|
||||
)
|
||||
install_parser.add_argument(
|
||||
"--prefix", type=str, help='Prefix to install in')
|
||||
|
||||
|
@ -170,72 +155,38 @@ def main(): # noqa: C901
|
|||
from .session.plain import PlainSession
|
||||
|
||||
session = PlainSession()
|
||||
|
||||
with ExitStack() as es:
|
||||
try:
|
||||
es.enter_context(session)
|
||||
except SessionSetupFailure as e:
|
||||
logging.debug('Error lines: %r', e.errlines)
|
||||
logging.fatal('Failed to set up session: %s', e.reason)
|
||||
return 1
|
||||
|
||||
parsed_url = urlparse(args.directory)
|
||||
# TODO(jelmer): Get a list of supported schemes from breezy?
|
||||
if parsed_url.scheme in ('git', 'http', 'https', 'ssh'):
|
||||
import breezy.git # noqa: F401
|
||||
import breezy.bzr # noqa: F401
|
||||
from breezy.branch import Branch
|
||||
from silver_platter.utils import TemporarySprout
|
||||
b = Branch.open(args.directory)
|
||||
logging.info("Cloning %s", args.directory)
|
||||
wt = es.enter_context(TemporarySprout(b))
|
||||
external_dir, internal_dir = session.setup_from_vcs(wt)
|
||||
else:
|
||||
if parsed_url.scheme == 'file':
|
||||
directory = parsed_url.path
|
||||
else:
|
||||
directory = args.directory
|
||||
logging.info("Preparing directory %s", directory)
|
||||
external_dir, internal_dir = session.setup_from_directory(
|
||||
directory)
|
||||
with session:
|
||||
logging.info("Preparing directory %s", args.directory)
|
||||
external_dir, internal_dir = session.setup_from_directory(args.directory)
|
||||
session.chdir(internal_dir)
|
||||
os.chdir(external_dir)
|
||||
|
||||
if not session.is_temporary and args.subcommand == 'info':
|
||||
args.explain = True
|
||||
|
||||
if args.resolve == "auto":
|
||||
if args.resolve == "apt":
|
||||
resolver = AptResolver.from_session(session)
|
||||
elif args.resolve == "native":
|
||||
resolver = native_resolvers(session, user_local=args.user)
|
||||
elif args.resolve == "auto":
|
||||
resolver = auto_resolver(session, explain=args.explain)
|
||||
else:
|
||||
resolver = select_resolvers(
|
||||
session, user_local=args.user,
|
||||
resolvers=args.resolve.split(','),
|
||||
dep_server_url=args.dep_server_url)
|
||||
logging.info("Using requirement resolver: %s", resolver)
|
||||
fixers = determine_fixers(session, resolver, explain=args.explain)
|
||||
try:
|
||||
if args.subcommand == "exec":
|
||||
from .fix_build import run_with_build_fixers
|
||||
run_with_build_fixers(fixers, session, args.subargv)
|
||||
run_with_build_fixers(session, args.subargv, fixers)
|
||||
return 0
|
||||
bss = list(detect_buildsystems(external_dir))
|
||||
bss = list(detect_buildsystems(args.directory))
|
||||
logging.info("Detected buildsystems: %s", ", ".join(map(str, bss)))
|
||||
if not args.ignore_declared_dependencies:
|
||||
stages = STAGE_MAP[args.subcommand]
|
||||
if stages:
|
||||
logging.info(
|
||||
"Checking that declared requirements are present")
|
||||
logging.info("Checking that declared requirements are present")
|
||||
try:
|
||||
install_necessary_declared_requirements(
|
||||
session, resolver, fixers, bss, stages,
|
||||
explain=args.explain
|
||||
session, resolver, fixers, bss, stages, explain=args.explain
|
||||
)
|
||||
except UnsatisfiedRequirements as e:
|
||||
logging.info(
|
||||
'Unable to install declared dependencies:')
|
||||
for req in e.requirements:
|
||||
logging.info(' * %s', req)
|
||||
return 1
|
||||
except ExplainInstall as e:
|
||||
display_explain_commands(e.commands)
|
||||
return 1
|
||||
|
@ -256,15 +207,11 @@ def main(): # noqa: C901
|
|||
if args.subcommand == "build":
|
||||
from .build import run_build
|
||||
|
||||
run_build(
|
||||
session, buildsystems=bss, resolver=resolver,
|
||||
fixers=fixers)
|
||||
run_build(session, buildsystems=bss, resolver=resolver, fixers=fixers)
|
||||
if args.subcommand == "clean":
|
||||
from .clean import run_clean
|
||||
|
||||
run_clean(
|
||||
session, buildsystems=bss, resolver=resolver,
|
||||
fixers=fixers)
|
||||
run_clean(session, buildsystems=bss, resolver=resolver, fixers=fixers)
|
||||
if args.subcommand == "install":
|
||||
from .install import run_install
|
||||
|
||||
|
@ -279,42 +226,14 @@ def main(): # noqa: C901
|
|||
if args.subcommand == "test":
|
||||
from .test import run_test
|
||||
|
||||
run_test(
|
||||
session, buildsystems=bss, resolver=resolver,
|
||||
fixers=fixers)
|
||||
run_test(session, buildsystems=bss, resolver=resolver, fixers=fixers)
|
||||
if args.subcommand == "info":
|
||||
from .info import run_info
|
||||
|
||||
run_info(session, buildsystems=bss, fixers=fixers)
|
||||
|
||||
if args.subcommand == "verify":
|
||||
from .build import run_build
|
||||
from .test import run_test
|
||||
|
||||
run_build(
|
||||
session, buildsystems=bss, resolver=resolver,
|
||||
fixers=fixers)
|
||||
run_test(
|
||||
session, buildsystems=bss, resolver=resolver,
|
||||
fixers=fixers)
|
||||
|
||||
except ExplainInstall as e:
|
||||
display_explain_commands(e.commands)
|
||||
except UnidentifiedError:
|
||||
logging.info(
|
||||
'If there is a clear indication of a problem in the build '
|
||||
'log, please consider filing a request to update the patterns '
|
||||
'in buildlog-consultant at '
|
||||
'https://github.com/jelmer/buildlog-consultant/issues/new')
|
||||
return 1
|
||||
except DetailedFailure:
|
||||
if not args.verbose:
|
||||
logging.info(
|
||||
'Run with --verbose to get more information '
|
||||
'about steps taken to try to resolve error')
|
||||
logging.info(
|
||||
'Please consider filing a bug report at '
|
||||
'https://github.com/jelmer/ognibuild/issues/new')
|
||||
except (UnidentifiedError, DetailedFailure):
|
||||
return 1
|
||||
except NoBuildToolsFound:
|
||||
logging.info("No build tools found.")
|
||||
|
|
|
@ -15,28 +15,16 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from functools import partial
|
||||
|
||||
from .buildsystem import NoBuildToolsFound
|
||||
from .fix_build import iterate_with_build_fixers
|
||||
from .logs import NoLogManager
|
||||
|
||||
|
||||
BUILD_LOG_FILENAME = 'build.log'
|
||||
|
||||
|
||||
def run_build(session, buildsystems, resolver, fixers, log_manager=None):
|
||||
def run_build(session, buildsystems, resolver, fixers):
|
||||
# Some things want to write to the user's home directory,
|
||||
# e.g. pip caches in ~/.cache
|
||||
session.create_home()
|
||||
|
||||
if log_manager is None:
|
||||
log_manager = NoLogManager()
|
||||
|
||||
for buildsystem in buildsystems:
|
||||
iterate_with_build_fixers(
|
||||
fixers, log_manager.wrap(
|
||||
partial(buildsystem.build, session, resolver)))
|
||||
buildsystem.build(session, resolver, fixers)
|
||||
return
|
||||
|
||||
raise NoBuildToolsFound()
|
||||
|
|
|
@ -19,32 +19,65 @@
|
|||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional, List, Callable, Union, Tuple
|
||||
|
||||
from buildlog_consultant.common import (
|
||||
Problem,
|
||||
MissingPythonModule,
|
||||
MissingPythonDistribution,
|
||||
MissingCHeader,
|
||||
MissingPkgConfig,
|
||||
MissingCommand,
|
||||
MissingFile,
|
||||
MissingJavaScriptRuntime,
|
||||
MissingSprocketsFile,
|
||||
MissingGoPackage,
|
||||
MissingPerlFile,
|
||||
MissingPerlModule,
|
||||
MissingXmlEntity,
|
||||
MissingJDKFile,
|
||||
MissingJDK,
|
||||
MissingJRE,
|
||||
MissingNodeModule,
|
||||
MissingNodePackage,
|
||||
MissingPhpClass,
|
||||
MissingRubyGem,
|
||||
MissingLibrary,
|
||||
MissingSetupPyCommand,
|
||||
MissingCMakeComponents,
|
||||
MissingJavaClass,
|
||||
MissingCSharpCompiler,
|
||||
MissingRPackage,
|
||||
MissingRubyFile,
|
||||
MissingAutoconfMacro,
|
||||
MissingValaPackage,
|
||||
MissingBoostComponents,
|
||||
MissingXfceDependency,
|
||||
MissingHaskellDependencies,
|
||||
MissingVagueDependency,
|
||||
DhAddonLoadFailure,
|
||||
MissingMavenArtifacts,
|
||||
MissingIntrospectionTypelib,
|
||||
GnomeCommonMissing,
|
||||
MissingGnomeCommonDependency,
|
||||
UnknownCertificateAuthority,
|
||||
CMakeFilesMissing,
|
||||
MissingLibtool,
|
||||
MissingQt,
|
||||
MissingX11,
|
||||
MissingPerlPredeclared,
|
||||
MissingLatexFile,
|
||||
MissingCargoCrate,
|
||||
MissingStaticLibrary,
|
||||
)
|
||||
from buildlog_consultant.apt import UnsatisfiedAptDependencies
|
||||
|
||||
from . import OneOfRequirement
|
||||
from .fix_build import BuildFixer
|
||||
from .requirements import (
|
||||
Requirement,
|
||||
BinaryRequirement,
|
||||
PathRequirement,
|
||||
PkgConfigRequirement,
|
||||
CHeaderRequirement,
|
||||
JavaScriptRuntimeRequirement,
|
||||
ValaPackageRequirement,
|
||||
RubyGemRequirement,
|
||||
GoPackageRequirement,
|
||||
DhAddonRequirement,
|
||||
PhpClassRequirement,
|
||||
|
@ -59,7 +92,6 @@ from .requirements import (
|
|||
HaskellPackageRequirement,
|
||||
MavenArtifactRequirement,
|
||||
BoostComponentRequirement,
|
||||
KF5ComponentRequirement,
|
||||
GnomeCommonRequirement,
|
||||
JDKFileRequirement,
|
||||
JDKRequirement,
|
||||
|
@ -80,124 +112,86 @@ from .requirements import (
|
|||
LatexPackageRequirement,
|
||||
CargoCrateRequirement,
|
||||
StaticLibraryRequirement,
|
||||
GnulibDirectoryRequirement,
|
||||
LuaModuleRequirement,
|
||||
PHPExtensionRequirement,
|
||||
VcsControlDirectoryAccessRequirement,
|
||||
RubyGemRequirement,
|
||||
QtModuleRequirement,
|
||||
)
|
||||
from .resolver import UnsatisfiedRequirements
|
||||
|
||||
|
||||
def map_pytest_arguments_to_plugin(args):
|
||||
# TODO(jelmer): Map argument to PytestPluginRequirement
|
||||
return None
|
||||
|
||||
|
||||
ProblemToRequirementConverter = Callable[[Problem], Optional[Requirement]]
|
||||
|
||||
|
||||
PROBLEM_CONVERTERS: List[Union[
|
||||
Tuple[str, ProblemToRequirementConverter],
|
||||
Tuple[str, ProblemToRequirementConverter, str]]] = [
|
||||
('missing-file', lambda p: PathRequirement(p.path)),
|
||||
('command-missing', lambda p: BinaryRequirement(p.command)),
|
||||
('valac-cannot-compile', lambda p: VagueDependencyRequirement('valac'),
|
||||
'0.0.27'),
|
||||
('missing-cmake-files', lambda p: OneOfRequirement(
|
||||
[CMakefileRequirement(filename, p.version)
|
||||
for filename in p.filenames])),
|
||||
('missing-command-or-build-file', lambda p: BinaryRequirement(p.command)),
|
||||
('missing-pkg-config-package',
|
||||
lambda p: PkgConfigRequirement(p.module, p.minimum_version)),
|
||||
('missing-c-header', lambda p: CHeaderRequirement(p.header)),
|
||||
('missing-introspection-typelib',
|
||||
lambda p: IntrospectionTypelibRequirement(p.library)),
|
||||
('missing-python-module', lambda p: PythonModuleRequirement(
|
||||
p.module, python_version=p.python_version,
|
||||
minimum_version=p.minimum_version)),
|
||||
('missing-python-distribution', lambda p: PythonPackageRequirement(
|
||||
p.distribution, python_version=p.python_version,
|
||||
minimum_version=p.minimum_version)),
|
||||
('javascript-runtime-missing', lambda p: JavaScriptRuntimeRequirement()),
|
||||
('missing-node-module', lambda p: NodeModuleRequirement(p.module)),
|
||||
('missing-node-package', lambda p: NodePackageRequirement(p.package)),
|
||||
('missing-ruby-gem', lambda p: RubyGemRequirement(p.gem, p.version)),
|
||||
('missing-qt-modules', lambda p: QtModuleRequirement(p.modules[0]),
|
||||
'0.0.27'),
|
||||
('missing-php-class', lambda p: PhpClassRequirement(p.php_class)),
|
||||
('missing-r-package', lambda p: RPackageRequirement(
|
||||
p.package, p.minimum_version)),
|
||||
('missing-vague-dependency',
|
||||
lambda p: VagueDependencyRequirement(
|
||||
p.name, minimum_version=p.minimum_version)),
|
||||
('missing-c#-compiler', lambda p: BinaryRequirement("msc")),
|
||||
('missing-gnome-common', lambda p: GnomeCommonRequirement()),
|
||||
('missing-jdk', lambda p: JDKRequirement()),
|
||||
('missing-jre', lambda p: JRERequirement()),
|
||||
('missing-qt', lambda p: QTRequirement()),
|
||||
('missing-x11', lambda p: X11Requirement()),
|
||||
('missing-libtool', lambda p: LibtoolRequirement()),
|
||||
('missing-php-extension',
|
||||
lambda p: PHPExtensionRequirement(p.extension)),
|
||||
('missing-rust-compiler', lambda p: BinaryRequirement("rustc")),
|
||||
('missing-java-class', lambda p: JavaClassRequirement(p.classname)),
|
||||
('missing-go-package', lambda p: GoPackageRequirement(p.package)),
|
||||
('missing-autoconf-macro', lambda p: AutoconfMacroRequirement(p.macro)),
|
||||
('missing-vala-package', lambda p: ValaPackageRequirement(p.package)),
|
||||
('missing-lua-module', lambda p: LuaModuleRequirement(p.module)),
|
||||
('missing-jdk-file', lambda p: JDKFileRequirement(p.jdk_path, p.filename)),
|
||||
('missing-ruby-file', lambda p: RubyFileRequirement(p.filename)),
|
||||
('missing-library', lambda p: LibraryRequirement(p.library)),
|
||||
('missing-sprockets-file',
|
||||
lambda p: SprocketsFileRequirement(p.content_type, p.name)),
|
||||
('dh-addon-load-failure', lambda p: DhAddonRequirement(p.path)),
|
||||
('missing-xml-entity', lambda p: XmlEntityRequirement(p.url)),
|
||||
('missing-gnulib-directory',
|
||||
lambda p: GnulibDirectoryRequirement(p.directory)),
|
||||
('vcs-control-directory-needed',
|
||||
lambda p: VcsControlDirectoryAccessRequirement(p.vcs)),
|
||||
('missing-static-library',
|
||||
lambda p: StaticLibraryRequirement(p.library, p.filename)),
|
||||
('missing-perl-module',
|
||||
lambda p: PerlModuleRequirement(
|
||||
module=p.module, filename=p.filename, inc=p.inc)),
|
||||
('unknown-certificate-authority',
|
||||
lambda p: CertificateAuthorityRequirement(p.url)),
|
||||
('unsupported-pytest-arguments',
|
||||
lambda p: map_pytest_arguments_to_plugin(p.args), '0.0.27'),
|
||||
]
|
||||
|
||||
|
||||
def problem_to_upstream_requirement(
|
||||
problem: Problem) -> Optional[Requirement]: # noqa: C901
|
||||
for entry in PROBLEM_CONVERTERS:
|
||||
kind, fn = entry[:2]
|
||||
if kind == problem.kind:
|
||||
return fn(problem)
|
||||
if isinstance(problem, MissingCMakeComponents):
|
||||
if problem.name.lower() == 'boost':
|
||||
return OneOfRequirement(
|
||||
[BoostComponentRequirement(name)
|
||||
for name in problem.components])
|
||||
elif problem.name.lower() == 'kf5':
|
||||
return OneOfRequirement(
|
||||
[KF5ComponentRequirement(name) for name in problem.components])
|
||||
return None
|
||||
def problem_to_upstream_requirement(problem): # noqa: C901
|
||||
if isinstance(problem, MissingFile):
|
||||
return PathRequirement(problem.path)
|
||||
elif isinstance(problem, MissingCommand):
|
||||
return BinaryRequirement(problem.command)
|
||||
elif isinstance(problem, MissingPkgConfig):
|
||||
return PkgConfigRequirement(problem.module, problem.minimum_version)
|
||||
elif isinstance(problem, MissingCHeader):
|
||||
return CHeaderRequirement(problem.header)
|
||||
elif isinstance(problem, MissingIntrospectionTypelib):
|
||||
return IntrospectionTypelibRequirement(problem.library)
|
||||
elif isinstance(problem, MissingJavaScriptRuntime):
|
||||
return JavaScriptRuntimeRequirement()
|
||||
elif isinstance(problem, MissingRubyGem):
|
||||
return RubyGemRequirement(problem.gem, problem.version)
|
||||
elif isinstance(problem, MissingValaPackage):
|
||||
return ValaPackageRequirement(problem.package)
|
||||
elif isinstance(problem, MissingGoPackage):
|
||||
return GoPackageRequirement(problem.package)
|
||||
elif isinstance(problem, MissingBoostComponents):
|
||||
return [BoostComponentRequirement(name) for name in problem.components]
|
||||
elif isinstance(problem, DhAddonLoadFailure):
|
||||
return DhAddonRequirement(problem.path)
|
||||
elif isinstance(problem, MissingPhpClass):
|
||||
return PhpClassRequirement(problem.php_class)
|
||||
elif isinstance(problem, MissingRPackage):
|
||||
return RPackageRequirement(problem.package, problem.minimum_version)
|
||||
elif isinstance(problem, MissingNodeModule):
|
||||
return NodeModuleRequirement(problem.module)
|
||||
elif isinstance(problem, MissingStaticLibrary):
|
||||
return StaticLibraryRequirement(problem.library, problem.filename)
|
||||
elif isinstance(problem, MissingNodePackage):
|
||||
return NodePackageRequirement(problem.package)
|
||||
elif isinstance(problem, MissingLatexFile):
|
||||
if problem.filename.endswith('.sty'):
|
||||
return LatexPackageRequirement(problem.filename[:-4])
|
||||
return None
|
||||
elif isinstance(problem, MissingVagueDependency):
|
||||
return VagueDependencyRequirement(problem.name, minimum_version=problem.minimum_version)
|
||||
elif isinstance(problem, MissingLibrary):
|
||||
return LibraryRequirement(problem.library)
|
||||
elif isinstance(problem, MissingRubyFile):
|
||||
return RubyFileRequirement(problem.filename)
|
||||
elif isinstance(problem, MissingXmlEntity):
|
||||
return XmlEntityRequirement(problem.url)
|
||||
elif isinstance(problem, MissingSprocketsFile):
|
||||
return SprocketsFileRequirement(problem.content_type, problem.name)
|
||||
elif isinstance(problem, MissingJavaClass):
|
||||
return JavaClassRequirement(problem.classname)
|
||||
elif isinstance(problem, CMakeFilesMissing):
|
||||
return [CMakefileRequirement(filename) for filename in problem.filenames]
|
||||
elif isinstance(problem, MissingHaskellDependencies):
|
||||
return OneOfRequirement(
|
||||
[HaskellPackageRequirement.from_string(dep)
|
||||
for dep in problem.deps])
|
||||
return [HaskellPackageRequirement.from_string(dep) for dep in problem.deps]
|
||||
elif isinstance(problem, MissingMavenArtifacts):
|
||||
return OneOfRequirement([
|
||||
return [
|
||||
MavenArtifactRequirement.from_str(artifact)
|
||||
for artifact in problem.artifacts
|
||||
])
|
||||
]
|
||||
elif isinstance(problem, MissingCSharpCompiler):
|
||||
return BinaryRequirement("msc")
|
||||
elif isinstance(problem, GnomeCommonMissing):
|
||||
return GnomeCommonRequirement()
|
||||
elif isinstance(problem, MissingJDKFile):
|
||||
return JDKFileRequirement(problem.jdk_path, problem.filename)
|
||||
elif isinstance(problem, MissingJDK):
|
||||
return JDKRequirement()
|
||||
elif isinstance(problem, MissingJRE):
|
||||
return JRERequirement()
|
||||
elif isinstance(problem, MissingQt):
|
||||
return QTRequirement()
|
||||
elif isinstance(problem, MissingX11):
|
||||
return X11Requirement()
|
||||
elif isinstance(problem, MissingLibtool):
|
||||
return LibtoolRequirement()
|
||||
elif isinstance(problem, UnknownCertificateAuthority):
|
||||
return CertificateAuthorityRequirement(problem.url)
|
||||
elif isinstance(problem, MissingPerlPredeclared):
|
||||
ret = PerlPreDeclaredRequirement(problem.name)
|
||||
try:
|
||||
|
@ -216,20 +210,36 @@ def problem_to_upstream_requirement(
|
|||
return BinaryRequirement("glib-gettextize")
|
||||
else:
|
||||
logging.warning(
|
||||
"No known command for gnome-common dependency %s",
|
||||
problem.package
|
||||
"No known command for gnome-common dependency %s", problem.package
|
||||
)
|
||||
return None
|
||||
elif isinstance(problem, MissingXfceDependency):
|
||||
if problem.package == "gtk-doc":
|
||||
return BinaryRequirement("gtkdocize")
|
||||
else:
|
||||
logging.warning(
|
||||
"No known command for xfce dependency %s", problem.package)
|
||||
logging.warning("No known command for xfce dependency %s", problem.package)
|
||||
return None
|
||||
elif isinstance(problem, MissingPerlModule):
|
||||
return PerlModuleRequirement(
|
||||
module=problem.module, filename=problem.filename, inc=problem.inc
|
||||
)
|
||||
elif isinstance(problem, MissingPerlFile):
|
||||
return PerlFileRequirement(filename=problem.filename)
|
||||
elif problem.kind == 'unsatisfied-apt-dependencies':
|
||||
elif isinstance(problem, MissingAutoconfMacro):
|
||||
return AutoconfMacroRequirement(problem.macro)
|
||||
elif isinstance(problem, MissingPythonModule):
|
||||
return PythonModuleRequirement(
|
||||
problem.module,
|
||||
python_version=problem.python_version,
|
||||
minimum_version=problem.minimum_version,
|
||||
)
|
||||
elif isinstance(problem, MissingPythonDistribution):
|
||||
return PythonPackageRequirement(
|
||||
problem.distribution,
|
||||
python_version=problem.python_version,
|
||||
minimum_version=problem.minimum_version,
|
||||
)
|
||||
elif isinstance(problem, UnsatisfiedAptDependencies):
|
||||
from .resolver.apt import AptRequirement
|
||||
return AptRequirement(problem.relations)
|
||||
else:
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,25 +15,16 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from functools import partial
|
||||
|
||||
from .fix_build import iterate_with_build_fixers
|
||||
from .buildsystem import NoBuildToolsFound
|
||||
from .logs import NoLogManager
|
||||
|
||||
|
||||
def run_clean(session, buildsystems, resolver, fixers, log_manager=None):
|
||||
def run_clean(session, buildsystems, resolver, fixers):
|
||||
# Some things want to write to the user's home directory,
|
||||
# e.g. pip caches in ~/.cache
|
||||
session.create_home()
|
||||
|
||||
if log_manager is None:
|
||||
log_manager = NoLogManager()
|
||||
|
||||
for buildsystem in buildsystems:
|
||||
iterate_with_build_fixers(
|
||||
fixers, log_manager.wrap(
|
||||
partial(buildsystem.clean, session, resolver)))
|
||||
buildsystem.clean(session, resolver, fixers)
|
||||
return
|
||||
|
||||
raise NoBuildToolsFound()
|
||||
|
|
|
@ -29,8 +29,7 @@ def satisfy_build_deps(session: Session, tree, debian_path):
|
|||
deps.append(source[name].strip().strip(","))
|
||||
except KeyError:
|
||||
pass
|
||||
for name in ["Build-Conflicts", "Build-Conflicts-Indep",
|
||||
"Build-Conflicts-Arch"]:
|
||||
for name in ["Build-Conflicts", "Build-Conflicts-Indep", "Build-Conflicts-Arch"]:
|
||||
try:
|
||||
deps.append("Conflicts: " + source[name])
|
||||
except KeyError:
|
||||
|
|
|
@ -16,9 +16,8 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from debian.changelog import Version
|
||||
import logging
|
||||
from typing import List, Optional, Iterable
|
||||
from typing import List, Optional
|
||||
|
||||
import os
|
||||
from buildlog_consultant.apt import (
|
||||
|
@ -38,12 +37,7 @@ from .file_search import (
|
|||
def run_apt(
|
||||
session: Session, args: List[str], prefix: Optional[List[str]] = None
|
||||
) -> None:
|
||||
"""Run apt.
|
||||
|
||||
Raises:
|
||||
DetailedFailure: When a known error occurs
|
||||
UnidentifiedError: If an unknown error occurs
|
||||
"""
|
||||
"""Run apt."""
|
||||
if prefix is None:
|
||||
prefix = []
|
||||
args = prefix = ["apt", "-y"] + args
|
||||
|
@ -54,7 +48,7 @@ def run_apt(
|
|||
match, error = find_apt_get_failure(lines)
|
||||
if error is not None:
|
||||
raise DetailedFailure(retcode, args, error)
|
||||
while lines and lines[-1].rstrip('\n') == "":
|
||||
while lines and lines[-1] == "":
|
||||
lines.pop(-1)
|
||||
raise UnidentifiedError(retcode, args, lines, secondary=match)
|
||||
|
||||
|
@ -99,18 +93,13 @@ class AptManager(object):
|
|||
def package_exists(self, package):
|
||||
return package in self.apt_cache
|
||||
|
||||
def package_versions(self, package: str) -> Optional[Iterable[Version]]:
|
||||
try:
|
||||
return list(self.apt_cache[package].versions)
|
||||
except KeyError:
|
||||
return None
|
||||
def package_versions(self, package):
|
||||
return list(self.apt_cache[package].versions)
|
||||
|
||||
async def get_packages_for_paths(
|
||||
self, paths, regex: bool = False, case_insensitive: bool = False):
|
||||
def get_packages_for_paths(self, paths, regex=False, case_insensitive=False):
|
||||
logging.debug("Searching for packages containing %r", paths)
|
||||
return await get_packages_for_paths(
|
||||
paths, self.searchers(), regex=regex,
|
||||
case_insensitive=case_insensitive
|
||||
return get_packages_for_paths(
|
||||
paths, self.searchers(), regex=regex, case_insensitive=case_insensitive
|
||||
)
|
||||
|
||||
def missing(self, packages):
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
__all__ = [
|
||||
"get_build_architecture",
|
||||
"version_add_suffix",
|
||||
"add_dummy_changelog_entry",
|
||||
"build",
|
||||
"DetailedDebianBuildFailure",
|
||||
|
@ -25,22 +24,20 @@ __all__ = [
|
|||
]
|
||||
|
||||
from datetime import datetime
|
||||
from debmutate.changelog import ChangelogEditor
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from debian.changelog import Changelog, Version, ChangeBlock
|
||||
from debmutate.changelog import get_maintainer, ChangelogEditor
|
||||
from debmutate.reformatting import GeneratedFile
|
||||
from debian.changelog import Changelog
|
||||
from debmutate.changelog import get_maintainer
|
||||
|
||||
from breezy.mutabletree import MutableTree
|
||||
from breezy.plugins.debian.builder import BuildFailedError
|
||||
from breezy.tree import Tree
|
||||
from breezy.workingtree import WorkingTree
|
||||
|
||||
from buildlog_consultant.sbuild import (
|
||||
worker_failure_from_sbuild_log,
|
||||
|
@ -48,18 +45,10 @@ from buildlog_consultant.sbuild import (
|
|||
|
||||
from .. import DetailedFailure as DetailedFailure, UnidentifiedError
|
||||
|
||||
BUILD_LOG_FILENAME = 'build.log'
|
||||
|
||||
DEFAULT_BUILDER = "sbuild --no-clean-source"
|
||||
|
||||
|
||||
class ChangelogNotEditable(Exception):
|
||||
"""Changelog can not be edited."""
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
|
||||
class DetailedDebianBuildFailure(DetailedFailure):
|
||||
|
||||
def __init__(self, stage, phase, retcode, argv, error, description):
|
||||
|
@ -71,8 +60,7 @@ class DetailedDebianBuildFailure(DetailedFailure):
|
|||
|
||||
class UnidentifiedDebianBuildError(UnidentifiedError):
|
||||
|
||||
def __init__(self, stage, phase, retcode, argv, lines, description,
|
||||
secondary=None):
|
||||
def __init__(self, stage, phase, retcode, argv, lines, description, secondary=None):
|
||||
super(UnidentifiedDebianBuildError, self).__init__(
|
||||
retcode, argv, lines, secondary)
|
||||
self.stage = stage
|
||||
|
@ -87,12 +75,11 @@ class MissingChangesFile(Exception):
|
|||
self.filename = filename
|
||||
|
||||
|
||||
def find_changes_files(path: str, package: str, version: Version):
|
||||
non_epoch_version = version.upstream_version or ''
|
||||
def find_changes_files(path, package, version):
|
||||
non_epoch_version = version.upstream_version
|
||||
if version.debian_version is not None:
|
||||
non_epoch_version += "-%s" % version.debian_version
|
||||
c = re.compile('%s_%s_(.*).changes' % (
|
||||
re.escape(package), re.escape(non_epoch_version)))
|
||||
c = re.compile('%s_%s_(.*).changes' % (re.escape(package), re.escape(non_epoch_version)))
|
||||
for entry in os.scandir(path):
|
||||
m = c.match(entry.name)
|
||||
if m:
|
||||
|
@ -122,32 +109,15 @@ def control_files_in_root(tree: Tree, subpath: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def version_add_suffix(version: Version, suffix: str) -> Version:
|
||||
version = Version(str(version))
|
||||
|
||||
def add_suffix(v):
|
||||
m = re.fullmatch("(.*)(" + re.escape(suffix) + ")([0-9]+)", v)
|
||||
if m:
|
||||
return m.group(1) + m.group(2) + "%d" % (int(m.group(3)) + 1)
|
||||
else:
|
||||
return v + suffix + "1"
|
||||
if version.debian_revision:
|
||||
version.debian_revision = add_suffix(version.debian_revision)
|
||||
else:
|
||||
version.upstream_version = add_suffix(version.upstream_version)
|
||||
return version
|
||||
|
||||
|
||||
def add_dummy_changelog_entry(
|
||||
tree: MutableTree,
|
||||
subpath: str,
|
||||
suffix: str,
|
||||
suite: str,
|
||||
message: str,
|
||||
timestamp: Optional[datetime] = None,
|
||||
maintainer: Tuple[Optional[str], Optional[str]] = None,
|
||||
allow_reformatting: bool = True,
|
||||
) -> Version:
|
||||
timestamp=None,
|
||||
maintainer=None,
|
||||
):
|
||||
"""Add a dummy changelog entry to a package.
|
||||
|
||||
Args:
|
||||
|
@ -155,10 +125,18 @@ def add_dummy_changelog_entry(
|
|||
suffix: Suffix for the version
|
||||
suite: Debian suite
|
||||
message: Changelog message
|
||||
Returns:
|
||||
version of the newly added entry
|
||||
"""
|
||||
|
||||
def add_suffix(v, suffix):
|
||||
m = re.fullmatch(
|
||||
"(.*)(" + re.escape(suffix) + ")([0-9]+)",
|
||||
v,
|
||||
)
|
||||
if m:
|
||||
return m.group(1) + m.group(2) + "%d" % (int(m.group(3)) + 1)
|
||||
else:
|
||||
return v + suffix + "1"
|
||||
|
||||
if control_files_in_root(tree, subpath):
|
||||
path = os.path.join(subpath, "changelog")
|
||||
else:
|
||||
|
@ -167,38 +145,38 @@ def add_dummy_changelog_entry(
|
|||
maintainer = get_maintainer()
|
||||
if timestamp is None:
|
||||
timestamp = datetime.now()
|
||||
try:
|
||||
with ChangelogEditor(
|
||||
tree.abspath(path), # type: ignore
|
||||
allow_reformatting=allow_reformatting) as editor:
|
||||
version = version_add_suffix(editor[0].version, suffix)
|
||||
editor.auto_version(version, timestamp=timestamp)
|
||||
editor.add_entry(
|
||||
summary=[message], maintainer=maintainer, timestamp=timestamp,
|
||||
urgency='low')
|
||||
editor[0].distributions = suite
|
||||
return version
|
||||
except GeneratedFile as e:
|
||||
raise ChangelogNotEditable(path) from e
|
||||
with ChangelogEditor(tree.abspath(os.path.join(path))) as editor:
|
||||
version = editor[0].version
|
||||
if version.debian_revision:
|
||||
version.debian_revision = add_suffix(version.debian_revision, suffix)
|
||||
else:
|
||||
version.upstream_version = add_suffix(version.upstream_version, suffix)
|
||||
editor.auto_version(version, timestamp=timestamp)
|
||||
editor.add_entry(
|
||||
summary=[message], maintainer=maintainer, timestamp=timestamp, urgency='low')
|
||||
editor[0].distributions = suite
|
||||
|
||||
|
||||
def get_latest_changelog_entry(
|
||||
local_tree: WorkingTree, subpath: str = "") -> ChangeBlock:
|
||||
def get_latest_changelog_entry(local_tree, subpath=""):
|
||||
if control_files_in_root(local_tree, subpath):
|
||||
path = os.path.join(subpath, "changelog")
|
||||
else:
|
||||
path = os.path.join(subpath, "debian", "changelog")
|
||||
with local_tree.get_file(path) as f:
|
||||
cl = Changelog(f, max_blocks=1)
|
||||
return cl[0]
|
||||
return cl.package, cl.version
|
||||
|
||||
|
||||
def _builddeb_command(
|
||||
build_command: str = DEFAULT_BUILDER,
|
||||
result_dir: Optional[str] = None,
|
||||
apt_repository: Optional[str] = None,
|
||||
apt_repository_key: Optional[str] = None,
|
||||
extra_repositories: Optional[List[str]] = None):
|
||||
def build(
|
||||
local_tree,
|
||||
outf,
|
||||
build_command=DEFAULT_BUILDER,
|
||||
result_dir=None,
|
||||
distribution=None,
|
||||
subpath="",
|
||||
source_date_epoch=None,
|
||||
extra_repositories=None,
|
||||
):
|
||||
for repo in extra_repositories or []:
|
||||
build_command += " --extra-repository=" + shlex.quote(repo)
|
||||
args = [
|
||||
|
@ -209,34 +187,8 @@ def _builddeb_command(
|
|||
"--guess-upstream-branch-url",
|
||||
"--builder=%s" % build_command,
|
||||
]
|
||||
if apt_repository:
|
||||
args.append("--apt-repository=%s" % apt_repository)
|
||||
if apt_repository_key:
|
||||
args.append("--apt-repository-key=%s" % apt_repository_key)
|
||||
if result_dir:
|
||||
args.append("--result-dir=%s" % result_dir)
|
||||
return args
|
||||
|
||||
|
||||
def build(
|
||||
local_tree: WorkingTree,
|
||||
outf,
|
||||
build_command: str = DEFAULT_BUILDER,
|
||||
result_dir: Optional[str] = None,
|
||||
distribution: Optional[str] = None,
|
||||
subpath: str = "",
|
||||
source_date_epoch: Optional[int] = None,
|
||||
apt_repository: Optional[str] = None,
|
||||
apt_repository_key: Optional[str] = None,
|
||||
extra_repositories: Optional[List[str]] = None,
|
||||
):
|
||||
args = _builddeb_command(
|
||||
build_command=build_command,
|
||||
result_dir=result_dir,
|
||||
apt_repository=apt_repository,
|
||||
apt_repository_key=apt_repository_key,
|
||||
extra_repositories=extra_repositories)
|
||||
|
||||
outf.write("Running %r\n" % (build_command,))
|
||||
outf.flush()
|
||||
env = dict(os.environ.items())
|
||||
|
@ -247,25 +199,22 @@ def build(
|
|||
logging.info("Building debian packages, running %r.", build_command)
|
||||
try:
|
||||
subprocess.check_call(
|
||||
args, cwd=local_tree.abspath(subpath), stdout=outf, stderr=outf,
|
||||
env=env
|
||||
args, cwd=local_tree.abspath(subpath), stdout=outf, stderr=outf, env=env
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
raise BuildFailedError()
|
||||
|
||||
|
||||
def build_once(
|
||||
local_tree: WorkingTree,
|
||||
build_suite: str,
|
||||
output_directory: str,
|
||||
build_command: str,
|
||||
subpath: str = "",
|
||||
source_date_epoch: Optional[int] = None,
|
||||
apt_repository: Optional[str] = None,
|
||||
apt_repository_key: Optional[str] = None,
|
||||
extra_repositories: Optional[List[str]] = None
|
||||
local_tree,
|
||||
build_suite,
|
||||
output_directory,
|
||||
build_command,
|
||||
subpath="",
|
||||
source_date_epoch=None,
|
||||
extra_repositories=None
|
||||
):
|
||||
build_log_path = os.path.join(output_directory, BUILD_LOG_FILENAME)
|
||||
build_log_path = os.path.join(output_directory, "build.log")
|
||||
logging.debug("Writing build log to %s", build_log_path)
|
||||
try:
|
||||
with open(build_log_path, "w") as f:
|
||||
|
@ -277,8 +226,6 @@ def build_once(
|
|||
distribution=build_suite,
|
||||
subpath=subpath,
|
||||
source_date_epoch=source_date_epoch,
|
||||
apt_repository=apt_repository,
|
||||
apt_repository_key=apt_repository_key,
|
||||
extra_repositories=extra_repositories,
|
||||
)
|
||||
except BuildFailedError as e:
|
||||
|
@ -300,39 +247,27 @@ def build_once(
|
|||
[], sbuild_failure.description)
|
||||
|
||||
cl_entry = get_latest_changelog_entry(local_tree, subpath)
|
||||
if cl_entry.package is None:
|
||||
raise Exception('missing package in changelog entry')
|
||||
changes_names = []
|
||||
for kind, entry in find_changes_files(
|
||||
output_directory, cl_entry.package, cl_entry.version):
|
||||
for kind, entry in find_changes_files(output_directory, cl_entry.package, cl_entry.version):
|
||||
changes_names.append((entry.name))
|
||||
return (changes_names, cl_entry)
|
||||
|
||||
|
||||
class GitBuildpackageMissing(Exception):
|
||||
"""git-buildpackage is not installed"""
|
||||
|
||||
|
||||
def gbp_dch(path):
|
||||
try:
|
||||
subprocess.check_call(["gbp", "dch", "--ignore-branch"], cwd=path)
|
||||
except FileNotFoundError:
|
||||
raise GitBuildpackageMissing()
|
||||
subprocess.check_call(["gbp", "dch", "--ignore-branch"], cwd=path)
|
||||
|
||||
|
||||
def attempt_build(
|
||||
local_tree: WorkingTree,
|
||||
suffix: str,
|
||||
build_suite: str,
|
||||
output_directory: str,
|
||||
build_command: str,
|
||||
build_changelog_entry: Optional[str] = None,
|
||||
subpath: str = "",
|
||||
source_date_epoch: Optional[int] = None,
|
||||
run_gbp_dch: bool = False,
|
||||
apt_repository: Optional[str] = None,
|
||||
apt_repository_key: Optional[str] = None,
|
||||
extra_repositories: Optional[List[str]] = None
|
||||
local_tree,
|
||||
suffix,
|
||||
build_suite,
|
||||
output_directory,
|
||||
build_command,
|
||||
build_changelog_entry=None,
|
||||
subpath="",
|
||||
source_date_epoch=None,
|
||||
run_gbp_dch=False,
|
||||
extra_repositories=None
|
||||
):
|
||||
"""Attempt a build, with a custom distribution set.
|
||||
|
||||
|
@ -347,7 +282,7 @@ def attempt_build(
|
|||
source_date_epoch: Source date epoch to set
|
||||
Returns: Tuple with (changes_name, cl_version)
|
||||
"""
|
||||
if run_gbp_dch and not subpath and hasattr(local_tree.controldir, '_git'):
|
||||
if run_gbp_dch and not subpath:
|
||||
gbp_dch(local_tree.abspath(subpath))
|
||||
if build_changelog_entry is not None:
|
||||
add_dummy_changelog_entry(
|
||||
|
@ -360,7 +295,5 @@ def attempt_build(
|
|||
build_command,
|
||||
subpath,
|
||||
source_date_epoch=source_date_epoch,
|
||||
apt_repository=apt_repository,
|
||||
apt_repository_key=apt_repository_key,
|
||||
extra_repositories=extra_repositories,
|
||||
)
|
||||
|
|
|
@ -18,46 +18,43 @@
|
|||
"""Tie breaking by build deps."""
|
||||
|
||||
|
||||
from debian.deb822 import PkgRelation
|
||||
import logging
|
||||
|
||||
from breezy.plugins.debian.apt_repo import LocalApt, NoAptSources
|
||||
|
||||
|
||||
class BuildDependencyTieBreaker(object):
|
||||
def __init__(self, apt):
|
||||
self.apt = apt
|
||||
def __init__(self, rootdir):
|
||||
self.rootdir = rootdir
|
||||
self._counts = None
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.apt)
|
||||
return "%s(%r)" % (type(self).__name__, self.rootdir)
|
||||
|
||||
@classmethod
|
||||
def from_session(cls, session):
|
||||
return cls(LocalApt(session.location))
|
||||
return cls(session.location)
|
||||
|
||||
def _count(self):
|
||||
counts = {}
|
||||
with self.apt:
|
||||
for source in self.apt.iter_sources():
|
||||
for field in ['Build-Depends', 'Build-Depends-Indep',
|
||||
'Build-Depends-Arch']:
|
||||
for r in PkgRelation.parse_relations(
|
||||
source.get(field, '')):
|
||||
for p in r:
|
||||
counts.setdefault(p['name'], 0)
|
||||
counts[p['name']] += 1
|
||||
import apt_pkg
|
||||
|
||||
apt_pkg.init()
|
||||
apt_pkg.config.set("Dir", self.rootdir)
|
||||
apt_cache = apt_pkg.SourceRecords()
|
||||
apt_cache.restart()
|
||||
while apt_cache.step():
|
||||
try:
|
||||
for d in apt_cache.build_depends.values():
|
||||
for o in d:
|
||||
for p in o:
|
||||
counts.setdefault(p[0], 0)
|
||||
counts[p[0]] += 1
|
||||
except AttributeError:
|
||||
pass
|
||||
return counts
|
||||
|
||||
def __call__(self, reqs):
|
||||
if self._counts is None:
|
||||
try:
|
||||
self._counts = self._count()
|
||||
except NoAptSources:
|
||||
logging.warning(
|
||||
"No 'deb-src' in sources.list, "
|
||||
"unable to break build-depends")
|
||||
return None
|
||||
self._counts = self._count()
|
||||
by_count = {}
|
||||
for req in reqs:
|
||||
try:
|
||||
|
@ -83,5 +80,5 @@ if __name__ == "__main__":
|
|||
parser.add_argument("req", nargs="+")
|
||||
args = parser.parse_args()
|
||||
reqs = [AptRequirement.from_str(req) for req in args.req]
|
||||
tie_breaker = BuildDependencyTieBreaker(LocalApt())
|
||||
tie_breaker = BuildDependencyTieBreaker("/")
|
||||
print(tie_breaker(reqs))
|
||||
|
|
|
@ -17,13 +17,12 @@
|
|||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import apt_pkg
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from debian.deb822 import Release
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from typing import List, AsyncIterator
|
||||
from typing import Iterator, List
|
||||
import logging
|
||||
|
||||
|
||||
|
@ -33,15 +32,11 @@ from ..session import Session
|
|||
|
||||
class FileSearcher(object):
|
||||
def search_files(
|
||||
self, path: str, regex: bool = False,
|
||||
case_insensitive: bool = False) -> AsyncIterator[str]:
|
||||
self, path: str, regex: bool = False, case_insensitive: bool = False
|
||||
) -> Iterator[str]:
|
||||
raise NotImplementedError(self.search_files)
|
||||
|
||||
|
||||
class AptFileAccessError(Exception):
|
||||
"""Apt file access error."""
|
||||
|
||||
|
||||
class ContentsFileNotFound(Exception):
|
||||
"""The contents file was not found."""
|
||||
|
||||
|
@ -76,8 +71,7 @@ def contents_urls_from_sources_entry(source, arches, load_url):
|
|||
response = load_url(release_url)
|
||||
except FileNotFoundError as e:
|
||||
logging.warning(
|
||||
"Unable to download %s or %s: %s", inrelease_url,
|
||||
release_url, e
|
||||
"Unable to download %s or %s: %s", inrelease_url, release_url, e
|
||||
)
|
||||
return
|
||||
|
||||
|
@ -124,7 +118,7 @@ def _unwrap(f, ext):
|
|||
|
||||
|
||||
def load_direct_url(url):
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.error import HTTPError
|
||||
from urllib.request import urlopen, Request
|
||||
|
||||
for ext in [".xz", ".gz", ""]:
|
||||
|
@ -134,11 +128,7 @@ def load_direct_url(url):
|
|||
except HTTPError as e:
|
||||
if e.status == 404:
|
||||
continue
|
||||
raise AptFileAccessError(
|
||||
'Unable to access apt URL %s: %s' % (url + ext, e))
|
||||
except URLError as e:
|
||||
raise AptFileAccessError(
|
||||
'Unable to access apt URL %s: %s' % (url + ext, e))
|
||||
raise
|
||||
break
|
||||
else:
|
||||
raise FileNotFoundError(url)
|
||||
|
@ -197,7 +187,7 @@ class AptFileFileSearcher(FileSearcher):
|
|||
|
||||
@classmethod
|
||||
def from_session(cls, session):
|
||||
logging.debug('Using apt-file to search apt contents')
|
||||
logging.info('Using apt-file to search apt contents')
|
||||
if not os.path.exists(session.external_path(cls.CACHE_IS_EMPTY_PATH)):
|
||||
from .apt import AptManager
|
||||
AptManager.from_session(session).install(['apt-file'])
|
||||
|
@ -205,7 +195,7 @@ class AptFileFileSearcher(FileSearcher):
|
|||
session.check_call(['apt-file', 'update'], user='root')
|
||||
return cls(session)
|
||||
|
||||
async def search_files(self, path, regex=False, case_insensitive=False):
|
||||
def search_files(self, path, regex=False, case_insensitive=False):
|
||||
args = []
|
||||
if regex:
|
||||
args.append('-x')
|
||||
|
@ -214,17 +204,15 @@ class AptFileFileSearcher(FileSearcher):
|
|||
if case_insensitive:
|
||||
args.append('-i')
|
||||
args.append(path)
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
'/usr/bin/apt-file', 'search', *args,
|
||||
stdout=asyncio.subprocess.PIPE)
|
||||
(output, error) = await process.communicate(input=None)
|
||||
if process.returncode == 1:
|
||||
# No results
|
||||
return
|
||||
elif process.returncode == 3:
|
||||
raise Exception('apt-file cache is empty')
|
||||
elif process.returncode != 0:
|
||||
raise Exception("unexpected return code %d" % process.returncode)
|
||||
try:
|
||||
output = self.session.check_output(['/usr/bin/apt-file', 'search'] + args)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if e.returncode == 1:
|
||||
# No results
|
||||
return
|
||||
if e.returncode == 3:
|
||||
raise Exception('apt-file cache is empty')
|
||||
raise
|
||||
|
||||
for line in output.splitlines(False):
|
||||
pkg, path = line.split(b': ')
|
||||
|
@ -265,8 +253,7 @@ class RemoteContentsFileSearcher(FileSearcher):
|
|||
return load_url_with_cache(url, cache_dirs)
|
||||
|
||||
urls = list(
|
||||
contents_urls_from_sourceslist(
|
||||
sl, get_build_architecture(), load_url)
|
||||
contents_urls_from_sourceslist(sl, get_build_architecture(), load_url)
|
||||
)
|
||||
self._load_urls(urls, cache_dirs, load_url)
|
||||
|
||||
|
@ -290,8 +277,8 @@ class RemoteContentsFileSearcher(FileSearcher):
|
|||
return load_url_with_cache(url, cache_dirs)
|
||||
|
||||
urls = list(
|
||||
contents_urls_from_sourceslist(
|
||||
sl, get_build_architecture(), load_url))
|
||||
contents_urls_from_sourceslist(sl, get_build_architecture(), load_url)
|
||||
)
|
||||
self._load_urls(urls, cache_dirs, load_url)
|
||||
|
||||
def _load_urls(self, urls, cache_dirs, load_url):
|
||||
|
@ -299,16 +286,13 @@ class RemoteContentsFileSearcher(FileSearcher):
|
|||
try:
|
||||
f = load_url(url)
|
||||
self.load_file(f, url)
|
||||
except ConnectionResetError:
|
||||
logging.warning("Connection reset error retrieving %s", url)
|
||||
# TODO(jelmer): Retry?
|
||||
except ContentsFileNotFound:
|
||||
logging.warning("Unable to fetch contents file %s", url)
|
||||
|
||||
def __setitem__(self, path, package):
|
||||
self._db[path] = package
|
||||
|
||||
async def search_files(self, path, regex=False, case_insensitive=False):
|
||||
def search_files(self, path, regex=False, case_insensitive=False):
|
||||
path = path.lstrip("/").encode("utf-8", "surrogateescape")
|
||||
if case_insensitive and not regex:
|
||||
regex = True
|
||||
|
@ -354,9 +338,9 @@ class GeneratedFileSearcher(FileSearcher):
|
|||
(path, pkg) = line.strip().split(None, 1)
|
||||
self._db.append(path, pkg)
|
||||
|
||||
async def search_files(
|
||||
self, path: str, regex: bool = False,
|
||||
case_insensitive: bool = False):
|
||||
def search_files(
|
||||
self, path: str, regex: bool = False, case_insensitive: bool = False
|
||||
) -> Iterator[str]:
|
||||
for p, pkg in self._db:
|
||||
if regex:
|
||||
flags = 0
|
||||
|
@ -387,17 +371,16 @@ GENERATED_FILE_SEARCHER = GeneratedFileSearcher(
|
|||
)
|
||||
|
||||
|
||||
async def get_packages_for_paths(
|
||||
def get_packages_for_paths(
|
||||
paths: List[str],
|
||||
searchers: List[FileSearcher],
|
||||
regex: bool = False,
|
||||
case_insensitive: bool = False,
|
||||
) -> List[str]:
|
||||
candidates: List[str] = list()
|
||||
# TODO(jelmer): Combine these, perhaps by creating one gigantic regex?
|
||||
for path in paths:
|
||||
for searcher in searchers:
|
||||
async for pkg in searcher.search_files(
|
||||
for pkg in searcher.search_files(
|
||||
path, regex=regex, case_insensitive=case_insensitive
|
||||
):
|
||||
if pkg not in candidates:
|
||||
|
@ -410,10 +393,8 @@ def main(argv):
|
|||
from ..session.plain import PlainSession
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"path", help="Path to search for.", type=str, nargs="*")
|
||||
parser.add_argument(
|
||||
"--regex", "-x", help="Search for regex.", action="store_true")
|
||||
parser.add_argument("path", help="Path to search for.", type=str, nargs="*")
|
||||
parser.add_argument("--regex", "-x", help="Search for regex.", action="store_true")
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -422,14 +403,13 @@ def main(argv):
|
|||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
with PlainSession() as session:
|
||||
main_searcher = get_apt_contents_file_searcher(session)
|
||||
searchers = [main_searcher, GENERATED_FILE_SEARCHER]
|
||||
main_searcher = get_apt_contents_file_searcher(PlainSession())
|
||||
main_searcher.load_local()
|
||||
searchers = [main_searcher, GENERATED_FILE_SEARCHER]
|
||||
|
||||
packages = asyncio.run(get_packages_for_paths(
|
||||
args.path, searchers=searchers, regex=args.regex))
|
||||
for package in packages:
|
||||
print(package)
|
||||
packages = get_packages_for_paths(args.path, searchers=searchers, regex=args.regex)
|
||||
for package in packages:
|
||||
print(package)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -22,10 +22,10 @@ __all__ = [
|
|||
from functools import partial
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
from typing import List, Set, Optional, Type, Tuple
|
||||
from typing import List, Set, Optional, Type
|
||||
|
||||
from debian.deb822 import (
|
||||
Deb822,
|
||||
|
@ -34,8 +34,6 @@ from debian.deb822 import (
|
|||
|
||||
from breezy.commit import PointlessCommit, NullCommitReporter
|
||||
from breezy.tree import Tree
|
||||
from breezy.workingtree import WorkingTree
|
||||
|
||||
from debmutate.changelog import ChangelogEditor
|
||||
from debmutate.control import (
|
||||
ensure_relation,
|
||||
|
@ -52,7 +50,49 @@ from debmutate.reformatting import (
|
|||
GeneratedFile,
|
||||
)
|
||||
|
||||
from breezy.workspace import reset_tree
|
||||
try:
|
||||
from breezy.workspace import reset_tree
|
||||
except ImportError: # breezy < 3.2
|
||||
|
||||
def delete_items(deletables, dry_run=False):
|
||||
"""Delete files in the deletables iterable"""
|
||||
import errno
|
||||
import shutil
|
||||
|
||||
def onerror(function, path, excinfo):
|
||||
"""Show warning for errors seen by rmtree."""
|
||||
# Handle only permission error while removing files.
|
||||
# Other errors are re-raised.
|
||||
if function is not os.remove or excinfo[1].errno != errno.EACCES:
|
||||
raise
|
||||
logging.warning("unable to remove %s" % path)
|
||||
|
||||
for path, subp in deletables:
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path, onerror=onerror)
|
||||
else:
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError as e:
|
||||
# We handle only permission error here
|
||||
if e.errno != errno.EACCES:
|
||||
raise e
|
||||
logging.warning('unable to remove "%s": %s.', path, e.strerror)
|
||||
|
||||
def reset_tree(local_tree, subpath=""):
|
||||
from breezy.transform import revert
|
||||
from breezy.clean_tree import iter_deletables
|
||||
|
||||
revert(
|
||||
local_tree,
|
||||
local_tree.branch.basis_tree(),
|
||||
[subpath] if subpath not in (".", "") else None,
|
||||
)
|
||||
deletables = list(
|
||||
iter_deletables(local_tree, unknown=True, ignored=False, detritus=False)
|
||||
)
|
||||
delete_items(deletables)
|
||||
|
||||
|
||||
from debmutate._rules import (
|
||||
dh_invoke_add_with,
|
||||
|
@ -73,21 +113,18 @@ from buildlog_consultant.common import (
|
|||
)
|
||||
from buildlog_consultant.sbuild import (
|
||||
DebcargoUnacceptablePredicate,
|
||||
DebcargoUnacceptableComparator,
|
||||
)
|
||||
|
||||
from .build import (
|
||||
DetailedDebianBuildFailure,
|
||||
UnidentifiedDebianBuildError,
|
||||
)
|
||||
from ..logs import rotate_logfile
|
||||
from ..buildlog import problem_to_upstream_requirement
|
||||
from ..fix_build import BuildFixer, resolve_error
|
||||
from ..resolver.apt import (
|
||||
AptRequirement,
|
||||
)
|
||||
from .apt import AptManager
|
||||
from .build import attempt_build, DEFAULT_BUILDER, BUILD_LOG_FILENAME
|
||||
from .build import attempt_build, DEFAULT_BUILDER
|
||||
|
||||
|
||||
DEFAULT_MAX_ITERATIONS = 10
|
||||
|
@ -113,9 +150,7 @@ class DebianPackagingContext(object):
|
|||
def abspath(self, *parts):
|
||||
return self.tree.abspath(os.path.join(self.subpath, *parts))
|
||||
|
||||
def commit(
|
||||
self, summary: str,
|
||||
update_changelog: Optional[bool] = None) -> bool:
|
||||
def commit(self, summary: str, update_changelog: Optional[bool] = None) -> bool:
|
||||
if update_changelog is None:
|
||||
update_changelog = self.update_changelog
|
||||
with self.tree.lock_write():
|
||||
|
@ -179,11 +214,6 @@ def add_dependency(context, phase, requirement: AptRequirement):
|
|||
return add_test_dependency(context, phase[1], requirement)
|
||||
elif phase[0] == "build":
|
||||
return add_build_dependency(context, requirement)
|
||||
elif phase[0] == "buildenv":
|
||||
# TODO(jelmer): Actually, we probably just want to install it on the
|
||||
# host system?
|
||||
logging.warning("Unknown phase %r", phase)
|
||||
return False
|
||||
else:
|
||||
logging.warning("Unknown phase %r", phase)
|
||||
return False
|
||||
|
@ -201,19 +231,16 @@ def add_build_dependency(context, requirement: AptRequirement):
|
|||
raise CircularDependency(binary["Package"])
|
||||
for rel in requirement.relations:
|
||||
updater.source["Build-Depends"] = ensure_relation(
|
||||
updater.source.get("Build-Depends", ""),
|
||||
PkgRelation.str([rel])
|
||||
updater.source.get("Build-Depends", ""), PkgRelation.str([rel])
|
||||
)
|
||||
except FormattingUnpreservable as e:
|
||||
logging.info(
|
||||
"Unable to edit %s in a way that preserves formatting.", e.path)
|
||||
logging.info("Unable to edit %s in a way that preserves formatting.", e.path)
|
||||
return False
|
||||
|
||||
desc = requirement.pkg_relation_str()
|
||||
|
||||
if not updater.changed:
|
||||
logging.info(
|
||||
"Giving up; build dependency %s was already present.", desc)
|
||||
logging.info("Giving up; dependency %s was already present.", desc)
|
||||
return False
|
||||
|
||||
logging.info("Adding build dependency: %s", desc)
|
||||
|
@ -245,18 +272,13 @@ def add_test_dependency(context, testname, requirement):
|
|||
control.get("Depends", ""), PkgRelation.str([rel])
|
||||
)
|
||||
except FormattingUnpreservable as e:
|
||||
logging.info(
|
||||
"Unable to edit %s in a way that preserves formatting.", e.path)
|
||||
logging.info("Unable to edit %s in a way that preserves formatting.", e.path)
|
||||
return False
|
||||
if not updater.changed:
|
||||
return False
|
||||
|
||||
desc = requirement.pkg_relation_str()
|
||||
|
||||
if not updater.changed:
|
||||
logging.info(
|
||||
"Giving up; dependency %s for test %s was already present.",
|
||||
desc, testname)
|
||||
return False
|
||||
|
||||
logging.info("Adding dependency to test %s: %s", testname, desc)
|
||||
return context.commit(
|
||||
"Add missing dependency for test %s on %s." % (testname, desc),
|
||||
|
@ -266,8 +288,7 @@ def add_test_dependency(context, testname, requirement):
|
|||
def targeted_python_versions(tree: Tree, subpath: str) -> List[str]:
|
||||
with tree.get_file(os.path.join(subpath, "debian/control")) as f:
|
||||
control = Deb822(f)
|
||||
build_depends = PkgRelation.parse_relations(
|
||||
control.get("Build-Depends", ""))
|
||||
build_depends = PkgRelation.parse_relations(control.get("Build-Depends", ""))
|
||||
all_build_deps: Set[str] = set()
|
||||
for or_deps in build_depends:
|
||||
all_build_deps.update(or_dep["name"] for or_dep in or_deps)
|
||||
|
@ -291,7 +312,7 @@ def python_tie_breaker(tree, subpath, reqs):
|
|||
return True
|
||||
if pkg.startswith("lib%s-" % python_version):
|
||||
return True
|
||||
if pkg == r'lib%s-dev' % python_version:
|
||||
if re.match(r'lib%s\.[0-9]-dev' % python_version, pkg):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -316,8 +337,7 @@ def retry_apt_failure(error, phase, apt, context):
|
|||
def enable_dh_autoreconf(context, phase):
|
||||
# Debhelper >= 10 depends on dh-autoreconf and enables autoreconf by
|
||||
# default.
|
||||
debhelper_compat_version = get_debhelper_compat_level(
|
||||
context.tree.abspath("."))
|
||||
debhelper_compat_version = get_debhelper_compat_level(context.tree.abspath("."))
|
||||
if debhelper_compat_version is not None and debhelper_compat_version < 10:
|
||||
|
||||
def add_with_autoreconf(line, target):
|
||||
|
@ -336,8 +356,9 @@ def enable_dh_autoreconf(context, phase):
|
|||
|
||||
|
||||
def fix_missing_configure(error, phase, context):
|
||||
if (not context.tree.has_filename("configure.ac")
|
||||
and not context.tree.has_filename("configure.in")):
|
||||
if not context.tree.has_filename("configure.ac") and not context.tree.has_filename(
|
||||
"configure.in"
|
||||
):
|
||||
return False
|
||||
|
||||
return enable_dh_autoreconf(context, phase)
|
||||
|
@ -412,7 +433,7 @@ def fix_missing_makefile_pl(error, phase, context):
|
|||
return False
|
||||
|
||||
|
||||
def debcargo_coerce_unacceptable_prerelease(error, phase, context):
|
||||
def coerce_unacceptable_predicate(error, phase, context):
|
||||
from debmutate.debcargo import DebcargoEditor
|
||||
with DebcargoEditor(context.abspath('debian/debcargo.toml')) as editor:
|
||||
editor['allow_prerelease_deps'] = True
|
||||
|
@ -440,8 +461,7 @@ class SimpleBuildFixer(BuildFixer):
|
|||
|
||||
|
||||
class DependencyBuildFixer(BuildFixer):
|
||||
def __init__(self, packaging_context, apt_resolver,
|
||||
problem_cls: Type[Problem], fn):
|
||||
def __init__(self, packaging_context, apt_resolver, problem_cls: Type[Problem], fn):
|
||||
self.context = packaging_context
|
||||
self.apt_resolver = apt_resolver
|
||||
self._problem_cls = problem_cls
|
||||
|
@ -461,47 +481,32 @@ class DependencyBuildFixer(BuildFixer):
|
|||
return self._fn(problem, phase, self.apt_resolver, self.context)
|
||||
|
||||
|
||||
def versioned_package_fixers(session, packaging_context, apt: AptManager):
|
||||
def versioned_package_fixers(session, packaging_context, apt):
|
||||
return [
|
||||
PgBuildExtOutOfDateControlFixer(packaging_context, session, apt),
|
||||
SimpleBuildFixer(
|
||||
packaging_context, MissingConfigure, fix_missing_configure),
|
||||
SimpleBuildFixer(packaging_context, MissingConfigure, fix_missing_configure),
|
||||
SimpleBuildFixer(
|
||||
packaging_context, MissingAutomakeInput, fix_missing_automake_input
|
||||
),
|
||||
SimpleBuildFixer(
|
||||
packaging_context, MissingConfigStatusInput,
|
||||
fix_missing_config_status_input
|
||||
packaging_context, MissingConfigStatusInput, fix_missing_config_status_input
|
||||
),
|
||||
SimpleBuildFixer(
|
||||
packaging_context, MissingPerlFile, fix_missing_makefile_pl),
|
||||
SimpleBuildFixer(
|
||||
packaging_context, DebcargoUnacceptablePredicate,
|
||||
debcargo_coerce_unacceptable_prerelease),
|
||||
SimpleBuildFixer(
|
||||
packaging_context, DebcargoUnacceptableComparator,
|
||||
debcargo_coerce_unacceptable_prerelease),
|
||||
SimpleBuildFixer(packaging_context, MissingPerlFile, fix_missing_makefile_pl),
|
||||
SimpleBuildFixer(packaging_context, DebcargoUnacceptablePredicate, coerce_unacceptable_predicate),
|
||||
]
|
||||
|
||||
|
||||
def apt_fixers(apt: AptManager, packaging_context,
|
||||
dep_server_url: Optional[str] = None) -> List[BuildFixer]:
|
||||
def apt_fixers(apt, packaging_context) -> List[BuildFixer]:
|
||||
from ..resolver.apt import AptResolver
|
||||
from .udd import popcon_tie_breaker
|
||||
from .build_deps import BuildDependencyTieBreaker
|
||||
|
||||
apt_tie_breakers = [
|
||||
partial(python_tie_breaker, packaging_context.tree,
|
||||
packaging_context.subpath),
|
||||
partial(python_tie_breaker, packaging_context.tree, packaging_context.subpath),
|
||||
BuildDependencyTieBreaker.from_session(apt.session),
|
||||
popcon_tie_breaker,
|
||||
]
|
||||
resolver: AptResolver
|
||||
if dep_server_url:
|
||||
from ..resolver.dep_server import DepServerAptResolver
|
||||
resolver = DepServerAptResolver(apt, dep_server_url, apt_tie_breakers)
|
||||
else:
|
||||
resolver = AptResolver(apt, apt_tie_breakers)
|
||||
resolver = AptResolver(apt, apt_tie_breakers)
|
||||
return [
|
||||
DependencyBuildFixer(
|
||||
packaging_context, apt, AptFetchFailure, retry_apt_failure
|
||||
|
@ -510,49 +515,38 @@ def apt_fixers(apt: AptManager, packaging_context,
|
|||
]
|
||||
|
||||
|
||||
def default_fixers(
|
||||
local_tree: WorkingTree,
|
||||
subpath: str, apt: AptManager,
|
||||
committer: Optional[str] = None,
|
||||
update_changelog: Optional[bool] = None,
|
||||
dep_server_url: Optional[str] = None):
|
||||
def default_fixers(local_tree, subpath, apt, committer=None, update_changelog=None):
|
||||
packaging_context = DebianPackagingContext(
|
||||
local_tree, subpath, committer, update_changelog,
|
||||
commit_reporter=NullCommitReporter()
|
||||
)
|
||||
return (versioned_package_fixers(apt.session, packaging_context, apt)
|
||||
+ apt_fixers(apt, packaging_context, dep_server_url))
|
||||
return versioned_package_fixers(apt.session, packaging_context, apt) + apt_fixers(
|
||||
apt, packaging_context
|
||||
)
|
||||
|
||||
|
||||
def build_incrementally(
|
||||
local_tree: WorkingTree,
|
||||
apt: AptManager,
|
||||
suffix: str,
|
||||
build_suite: str,
|
||||
output_directory: str,
|
||||
build_command: str,
|
||||
local_tree,
|
||||
apt,
|
||||
suffix,
|
||||
build_suite,
|
||||
output_directory,
|
||||
build_command,
|
||||
build_changelog_entry,
|
||||
committer: Optional[str] = None,
|
||||
max_iterations: int = DEFAULT_MAX_ITERATIONS,
|
||||
subpath: str = "",
|
||||
committer=None,
|
||||
max_iterations=DEFAULT_MAX_ITERATIONS,
|
||||
subpath="",
|
||||
source_date_epoch=None,
|
||||
update_changelog: bool = True,
|
||||
apt_repository: Optional[str] = None,
|
||||
apt_repository_key: Optional[str] = None,
|
||||
extra_repositories: Optional[List[str]] = None,
|
||||
fixers: Optional[List[BuildFixer]] = None,
|
||||
run_gbp_dch: Optional[bool] = None,
|
||||
dep_server_url: Optional[str] = None,
|
||||
update_changelog=True,
|
||||
extra_repositories=None,
|
||||
fixers=None
|
||||
):
|
||||
fixed_errors: List[Tuple[Problem, str]] = []
|
||||
fixed_errors = []
|
||||
if fixers is None:
|
||||
fixers = default_fixers(
|
||||
local_tree, subpath, apt, committer=committer,
|
||||
update_changelog=update_changelog,
|
||||
dep_server_url=dep_server_url)
|
||||
update_changelog=update_changelog)
|
||||
logging.info("Using fixers: %r", fixers)
|
||||
if run_gbp_dch is None:
|
||||
run_gbp_dch = (update_changelog is False)
|
||||
while True:
|
||||
try:
|
||||
return attempt_build(
|
||||
|
@ -564,9 +558,7 @@ def build_incrementally(
|
|||
build_changelog_entry,
|
||||
subpath=subpath,
|
||||
source_date_epoch=source_date_epoch,
|
||||
run_gbp_dch=run_gbp_dch,
|
||||
apt_repository=apt_repository,
|
||||
apt_repository_key=apt_repository_key,
|
||||
run_gbp_dch=(update_changelog is False),
|
||||
extra_repositories=extra_repositories,
|
||||
)
|
||||
except UnidentifiedDebianBuildError:
|
||||
|
@ -577,19 +569,15 @@ def build_incrementally(
|
|||
logging.info("No relevant context, not making any changes.")
|
||||
raise
|
||||
if (e.error, e.phase) in fixed_errors:
|
||||
logging.warning(
|
||||
"Error was still not fixed on second try. Giving up.")
|
||||
logging.warning("Error was still not fixed on second try. Giving up.")
|
||||
raise
|
||||
if (max_iterations is not None
|
||||
and len(fixed_errors) > max_iterations):
|
||||
logging.warning(
|
||||
"Last fix did not address the issue. Giving up.")
|
||||
if max_iterations is not None and len(fixed_errors) > max_iterations:
|
||||
logging.warning("Last fix did not address the issue. Giving up.")
|
||||
raise
|
||||
reset_tree(local_tree, subpath=subpath)
|
||||
try:
|
||||
if not resolve_error(e.error, e.phase, fixers):
|
||||
logging.warning(
|
||||
"Failed to resolve error %r. Giving up.", e.error)
|
||||
logging.warning("Failed to resolve error %r. Giving up.", e.error)
|
||||
raise
|
||||
except GeneratedFile:
|
||||
logging.warning(
|
||||
|
@ -600,71 +588,71 @@ def build_incrementally(
|
|||
raise e
|
||||
except CircularDependency:
|
||||
logging.warning(
|
||||
"Unable to fix %r; it would introduce a circular "
|
||||
"dependency.",
|
||||
"Unable to fix %r; it would introduce a circular " "dependency.",
|
||||
e.error,
|
||||
)
|
||||
raise e
|
||||
fixed_errors.append((e.error, e.phase))
|
||||
rotate_logfile(os.path.join(output_directory, BUILD_LOG_FILENAME))
|
||||
if os.path.exists(os.path.join(output_directory, "build.log")):
|
||||
i = 1
|
||||
while os.path.exists(
|
||||
os.path.join(output_directory, "build.log.%d" % i)
|
||||
):
|
||||
i += 1
|
||||
target_path = os.path.join(output_directory, "build.log.%d" % i)
|
||||
os.rename(os.path.join(output_directory, "build.log"), target_path)
|
||||
logging.debug("Storing build log at %s", target_path)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser("ognibuild.debian.fix_build")
|
||||
modifications = parser.add_argument_group('Modifications')
|
||||
modifications.add_argument(
|
||||
"--suffix", type=str, help="Suffix to use for test builds.",
|
||||
default="fixbuild1"
|
||||
parser.add_argument(
|
||||
"--suffix", type=str, help="Suffix to use for test builds.", default="fixbuild1"
|
||||
)
|
||||
modifications.add_argument(
|
||||
parser.add_argument(
|
||||
"--suite", type=str, help="Suite to target.", default="unstable"
|
||||
)
|
||||
modifications.add_argument(
|
||||
"--committer", type=str, help="Committer string (name and email)",
|
||||
default=None
|
||||
parser.add_argument(
|
||||
"--output-directory", type=str, help="Output directory.", default=None
|
||||
)
|
||||
modifications.add_argument(
|
||||
parser.add_argument(
|
||||
"--committer", type=str, help="Committer string (name and email)", default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"--build-command",
|
||||
type=str,
|
||||
help="Build command",
|
||||
default=(DEFAULT_BUILDER + " -A -s -v"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-update-changelog",
|
||||
action="store_false",
|
||||
default=None,
|
||||
dest="update_changelog",
|
||||
help="do not update the changelog",
|
||||
)
|
||||
modifications.add_argument(
|
||||
parser.add_argument(
|
||||
'--max-iterations',
|
||||
type=int,
|
||||
default=DEFAULT_MAX_ITERATIONS,
|
||||
help='Maximum number of issues to attempt to fix before giving up.')
|
||||
parser.add_argument(
|
||||
"--update-changelog",
|
||||
action="store_true",
|
||||
dest="update_changelog",
|
||||
help="force updating of the changelog",
|
||||
default=None,
|
||||
)
|
||||
build_behaviour = parser.add_argument_group('Build Behaviour')
|
||||
build_behaviour.add_argument(
|
||||
"--output-directory", type=str, help="Output directory.", default=None
|
||||
)
|
||||
build_behaviour.add_argument(
|
||||
"--build-command",
|
||||
type=str,
|
||||
help="Build command",
|
||||
default=(DEFAULT_BUILDER + " -A -s -v"),
|
||||
)
|
||||
|
||||
build_behaviour.add_argument(
|
||||
'--max-iterations',
|
||||
type=int,
|
||||
default=DEFAULT_MAX_ITERATIONS,
|
||||
help='Maximum number of issues to attempt to fix before giving up.')
|
||||
build_behaviour.add_argument("--schroot", type=str, help="chroot to use.")
|
||||
parser.add_argument(
|
||||
"--dep-server-url", type=str,
|
||||
help="ognibuild dep server to use",
|
||||
default=os.environ.get('OGNIBUILD_DEPS'))
|
||||
parser.add_argument("--schroot", type=str, help="chroot to use.")
|
||||
parser.add_argument("--verbose", action="store_true", help="Be verbose")
|
||||
|
||||
args = parser.parse_args()
|
||||
from breezy.workingtree import WorkingTree
|
||||
import breezy.git # noqa: F401
|
||||
import breezy.bzr # noqa: F401
|
||||
from .apt import AptManager
|
||||
from ..session.plain import PlainSession
|
||||
from ..session.schroot import SchrootSession
|
||||
import tempfile
|
||||
|
@ -681,10 +669,6 @@ def main(argv=None):
|
|||
logging.info("Using output directory %s", output_directory)
|
||||
else:
|
||||
output_directory = args.output_directory
|
||||
if not os.path.isdir(output_directory):
|
||||
parser.error(
|
||||
'output directory %s is not a directory'
|
||||
% output_directory)
|
||||
|
||||
tree = WorkingTree.open(".")
|
||||
if args.schroot:
|
||||
|
@ -708,7 +692,6 @@ def main(argv=None):
|
|||
committer=args.committer,
|
||||
update_changelog=args.update_changelog,
|
||||
max_iterations=args.max_iterations,
|
||||
dep_server_url=args.dep_server_url,
|
||||
)
|
||||
except DetailedDebianBuildFailure as e:
|
||||
if e.phase is None:
|
||||
|
@ -718,21 +701,6 @@ def main(argv=None):
|
|||
else:
|
||||
phase = "%s (%s)" % (e.phase[0], e.phase[1])
|
||||
logging.fatal("Error during %s: %s", phase, e.error)
|
||||
if not args.output_directory:
|
||||
xdg_cache_dir = os.environ.get(
|
||||
'XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
|
||||
buildlogs_dir = os.path.join(
|
||||
xdg_cache_dir, 'ognibuild', 'buildlogs')
|
||||
os.makedirs(buildlogs_dir, exist_ok=True)
|
||||
target_log_file = os.path.join(
|
||||
buildlogs_dir,
|
||||
'%s-%s.log' % (
|
||||
os.path.basename(getattr(tree, 'basedir', 'build')),
|
||||
time.strftime('%Y-%m-%d_%H%M%s')))
|
||||
shutil.copy(
|
||||
os.path.join(output_directory, 'build.log'),
|
||||
target_log_file)
|
||||
logging.info('Build log available in %s', target_log_file)
|
||||
return 1
|
||||
except UnidentifiedDebianBuildError as e:
|
||||
if e.phase is None:
|
||||
|
|
|
@ -35,8 +35,7 @@ class UDD(object):
|
|||
def get_most_popular(self, packages):
|
||||
cursor = self._conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT package FROM popcon "
|
||||
"WHERE package IN %s ORDER BY insts DESC LIMIT 1",
|
||||
"SELECT package FROM popcon WHERE package IN %s ORDER BY insts DESC LIMIT 1",
|
||||
(tuple(packages),),
|
||||
)
|
||||
return cursor.fetchone()[0]
|
||||
|
@ -55,8 +54,7 @@ def popcon_tie_breaker(candidates):
|
|||
names = {list(c.package_names())[0]: c for c in candidates}
|
||||
winner = udd.get_most_popular(list(names.keys()))
|
||||
if winner is None:
|
||||
logging.warning(
|
||||
"No relevant popcon information found, not ranking by popcon")
|
||||
logging.warning("No relevant popcon information found, not ranking by popcon")
|
||||
return None
|
||||
logging.info("Picked winner using popcon")
|
||||
return names[winner]
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2022 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
# encoding: utf-8
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp_openmetrics import setup_metrics
|
||||
|
||||
from . import Requirement, UnknownRequirementFamily
|
||||
from .debian.apt import AptManager
|
||||
from .resolver.apt import resolve_requirement_apt
|
||||
|
||||
SUPPORTED_RELEASES = ['unstable', 'sid']
|
||||
|
||||
|
||||
routes = web.RouteTableDef()
|
||||
|
||||
|
||||
@routes.get('/health', name='health')
|
||||
async def handle_health(request):
|
||||
return web.Response(text='ok')
|
||||
|
||||
|
||||
@routes.get('/families', name='families')
|
||||
async def handle_families(request):
|
||||
return web.json_response(list(Requirement._JSON_DESERIALIZERS.keys()))
|
||||
|
||||
|
||||
@routes.post('/resolve-apt', name='resolve-apt')
|
||||
async def handle_apt(request):
|
||||
js = await request.json()
|
||||
try:
|
||||
req_js = js['requirement']
|
||||
except KeyError:
|
||||
raise web.HTTPBadRequest(text="json missing 'requirement' key")
|
||||
release = js.get('release')
|
||||
if release and release not in SUPPORTED_RELEASES:
|
||||
return web.json_response(
|
||||
{"reason": "unsupported-release", "release": release},
|
||||
status=404)
|
||||
try:
|
||||
req = Requirement.from_json(req_js)
|
||||
except UnknownRequirementFamily as e:
|
||||
return web.json_response(
|
||||
{"reason": "family-unknown", "family": e.family}, status=404)
|
||||
apt_reqs = await resolve_requirement_apt(request.app['apt_mgr'], req)
|
||||
return web.json_response([r.pkg_relation_str() for r in apt_reqs])
|
||||
|
||||
|
||||
@routes.get('/resolve-apt/{release}/{family}:{arg}', name='resolve-apt-simple')
|
||||
async def handle_apt_simple(request):
|
||||
if request.match_info['release'] not in SUPPORTED_RELEASES:
|
||||
return web.json_response(
|
||||
{"reason": "unsupported-release",
|
||||
"release": request.match_info['release']},
|
||||
status=404)
|
||||
try:
|
||||
req = Requirement.from_json(
|
||||
(request.match_info['family'], request.match_info['arg']))
|
||||
except UnknownRequirementFamily as e:
|
||||
return web.json_response(
|
||||
{"reason": "family-unknown", "family": e.family}, status=404)
|
||||
apt_reqs = await resolve_requirement_apt(request.app['apt_mgr'], req)
|
||||
return web.json_response([r.pkg_relation_str() for r in apt_reqs])
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--listen-address', type=str, help='Listen address')
|
||||
parser.add_argument('--schroot', type=str, help='Schroot session to use')
|
||||
parser.add_argument('--port', type=str, help='Listen port', default=9934)
|
||||
parser.add_argument('--debug', action='store_true')
|
||||
parser.add_argument(
|
||||
"--gcp-logging", action='store_true', help='Use Google cloud logging.')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.gcp_logging:
|
||||
import google.cloud.logging
|
||||
client = google.cloud.logging.Client()
|
||||
client.get_default_handler()
|
||||
client.setup_logging()
|
||||
else:
|
||||
if args.debug:
|
||||
log_level = logging.DEBUG
|
||||
else:
|
||||
log_level = logging.INFO
|
||||
|
||||
logging.basicConfig(
|
||||
level=log_level,
|
||||
format="[%(asctime)s] %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S")
|
||||
|
||||
if args.schroot:
|
||||
from .session.schroot import SchrootSession
|
||||
session = SchrootSession(args.schroot)
|
||||
else:
|
||||
from .session.plain import PlainSession
|
||||
session = PlainSession()
|
||||
with session:
|
||||
app = web.Application()
|
||||
app.router.add_routes(routes)
|
||||
app['apt_mgr'] = AptManager.from_session(session)
|
||||
setup_metrics(app)
|
||||
|
||||
web.run_app(app, host=args.listen_address, port=args.port)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
|
@ -18,19 +18,18 @@
|
|||
__all__ = [
|
||||
"UnidentifiedError",
|
||||
"DetailedFailure",
|
||||
"run_dist",
|
||||
"create_dist_schroot",
|
||||
"create_dist",
|
||||
"dist",
|
||||
"create_dist_schroot",
|
||||
]
|
||||
|
||||
import errno
|
||||
from functools import partial
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional, List
|
||||
|
||||
from debian.deb822 import Deb822
|
||||
|
||||
from breezy.tree import Tree
|
||||
from breezy.workingtree import WorkingTree
|
||||
|
||||
|
@ -38,78 +37,16 @@ from buildlog_consultant.common import (
|
|||
NoSpaceOnDevice,
|
||||
)
|
||||
|
||||
from debian.deb822 import Deb822
|
||||
|
||||
|
||||
from . import DetailedFailure, UnidentifiedError
|
||||
from .dist_catcher import DistNoTarball
|
||||
from .fix_build import iterate_with_build_fixers
|
||||
from .logs import LogManager, NoLogManager
|
||||
from .buildsystem import NoBuildToolsFound
|
||||
from .resolver import auto_resolver
|
||||
from .session import Session
|
||||
from .session.schroot import SchrootSession
|
||||
|
||||
|
||||
DIST_LOG_FILENAME = 'dist.log'
|
||||
|
||||
|
||||
def run_dist(session, buildsystems, resolver, fixers, target_directory,
|
||||
quiet=False, log_manager=None):
|
||||
# Some things want to write to the user's home directory,
|
||||
# e.g. pip caches in ~/.cache
|
||||
session.create_home()
|
||||
|
||||
logging.info('Using dependency resolver: %s', resolver)
|
||||
|
||||
if log_manager is None:
|
||||
log_manager = NoLogManager()
|
||||
|
||||
for buildsystem in buildsystems:
|
||||
filename = iterate_with_build_fixers(fixers, log_manager.wrap(
|
||||
partial(buildsystem.dist, session, resolver, target_directory,
|
||||
quiet=quiet)))
|
||||
return filename
|
||||
|
||||
raise NoBuildToolsFound()
|
||||
|
||||
|
||||
def dist(session, export_directory, reldir, target_dir, log_manager, *,
|
||||
version: Optional[str] = None, quiet=False):
|
||||
from .fix_build import BuildFixer
|
||||
from .buildsystem import detect_buildsystems
|
||||
from .buildlog import InstallFixer
|
||||
from .fixers import (
|
||||
GitIdentityFixer,
|
||||
MissingGoSumEntryFixer,
|
||||
SecretGpgKeyFixer,
|
||||
UnexpandedAutoconfMacroFixer,
|
||||
GnulibDirectoryFixer,
|
||||
)
|
||||
|
||||
if version:
|
||||
# TODO(jelmer): Shouldn't include backend-specific code here
|
||||
os.environ['SETUPTOOLS_SCM_PRETEND_VERSION'] = version
|
||||
|
||||
# TODO(jelmer): use scan_buildsystems to also look in subdirectories
|
||||
buildsystems = list(detect_buildsystems(export_directory))
|
||||
resolver = auto_resolver(session)
|
||||
fixers: List[BuildFixer] = [
|
||||
UnexpandedAutoconfMacroFixer(session, resolver),
|
||||
GnulibDirectoryFixer(session),
|
||||
MissingGoSumEntryFixer(session)]
|
||||
|
||||
fixers.append(InstallFixer(resolver))
|
||||
|
||||
if session.is_temporary:
|
||||
# Only muck about with temporary sessions
|
||||
fixers.extend([
|
||||
GitIdentityFixer(session),
|
||||
SecretGpgKeyFixer(session),
|
||||
])
|
||||
|
||||
session.chdir(reldir)
|
||||
|
||||
def run_dist(session, buildsystems, resolver, fixers, target_directory, quiet=False):
|
||||
# Some things want to write to the user's home directory,
|
||||
# e.g. pip caches in ~/.cache
|
||||
session.create_home()
|
||||
|
@ -117,34 +54,31 @@ def dist(session, export_directory, reldir, target_dir, log_manager, *,
|
|||
logging.info('Using dependency resolver: %s', resolver)
|
||||
|
||||
for buildsystem in buildsystems:
|
||||
filename = iterate_with_build_fixers(fixers, log_manager.wrap(
|
||||
partial(
|
||||
buildsystem.dist, session, resolver, target_dir,
|
||||
quiet=quiet)))
|
||||
filename = buildsystem.dist(
|
||||
session, resolver, fixers, target_directory, quiet=quiet
|
||||
)
|
||||
return filename
|
||||
|
||||
raise NoBuildToolsFound()
|
||||
|
||||
|
||||
# This is the function used by debianize()
|
||||
def create_dist(
|
||||
session: Session,
|
||||
tree: Tree,
|
||||
target_dir: str,
|
||||
include_controldir: bool = True,
|
||||
subdir: Optional[str] = None,
|
||||
log_manager: Optional[LogManager] = None,
|
||||
version: Optional[str] = None,
|
||||
cleanup: bool = False,
|
||||
) -> Optional[str]:
|
||||
"""Create a dist tarball for a tree.
|
||||
from .buildsystem import detect_buildsystems
|
||||
from .buildlog import InstallFixer
|
||||
from .fix_build import BuildFixer
|
||||
from .fixers import (
|
||||
GitIdentityFixer,
|
||||
SecretGpgKeyFixer,
|
||||
UnexpandedAutoconfMacroFixer,
|
||||
)
|
||||
|
||||
Args:
|
||||
session: session to run it
|
||||
tree: Tree object to work in
|
||||
target_dir: Directory to write tarball into
|
||||
include_controldir: Whether to include the version control directory
|
||||
subdir: subdirectory in the tree to operate in
|
||||
"""
|
||||
if subdir is None:
|
||||
subdir = "package"
|
||||
try:
|
||||
|
@ -156,11 +90,19 @@ def create_dist(
|
|||
raise DetailedFailure(1, ["mkdtemp"], NoSpaceOnDevice())
|
||||
raise
|
||||
|
||||
if log_manager is None:
|
||||
log_manager = NoLogManager()
|
||||
# TODO(jelmer): use scan_buildsystems to also look in subdirectories
|
||||
buildsystems = list(detect_buildsystems(export_directory))
|
||||
resolver = auto_resolver(session)
|
||||
fixers: List[BuildFixer] = [UnexpandedAutoconfMacroFixer(session, resolver)]
|
||||
|
||||
return dist(session, export_directory, reldir, target_dir,
|
||||
log_manager=log_manager, version=version)
|
||||
fixers.append(InstallFixer(resolver))
|
||||
|
||||
if session.is_temporary:
|
||||
# Only muck about with temporary sessions
|
||||
fixers.extend([GitIdentityFixer(session), SecretGpgKeyFixer(session)])
|
||||
|
||||
session.chdir(reldir)
|
||||
return run_dist(session, buildsystems, resolver, fixers, target_dir)
|
||||
|
||||
|
||||
def create_dist_schroot(
|
||||
|
@ -171,35 +113,30 @@ def create_dist_schroot(
|
|||
packaging_subpath: Optional[str] = None,
|
||||
include_controldir: bool = True,
|
||||
subdir: Optional[str] = None,
|
||||
log_manager: Optional[LogManager] = None,
|
||||
cleanup: bool = False,
|
||||
) -> Optional[str]:
|
||||
"""Create a dist tarball for a tree.
|
||||
|
||||
Args:
|
||||
session: session to run it
|
||||
tree: Tree object to work in
|
||||
target_dir: Directory to write tarball into
|
||||
include_controldir: Whether to include the version control directory
|
||||
subdir: subdirectory in the tree to operate in
|
||||
"""
|
||||
with SchrootSession(chroot) as session:
|
||||
if packaging_tree is not None:
|
||||
from .debian import satisfy_build_deps
|
||||
|
||||
satisfy_build_deps(session, packaging_tree, packaging_subpath)
|
||||
return create_dist(
|
||||
session, tree, target_dir,
|
||||
include_controldir=include_controldir, subdir=subdir,
|
||||
log_manager=log_manager)
|
||||
session,
|
||||
tree,
|
||||
target_dir,
|
||||
include_controldir=include_controldir,
|
||||
subdir=subdir,
|
||||
cleanup=cleanup,
|
||||
)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
import breezy.bzr # noqa: F401
|
||||
import breezy.git # noqa: F401
|
||||
from breezy.export import export
|
||||
|
||||
parser = argparse.ArgumentParser(argv)
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--chroot",
|
||||
default="unstable-amd64-sbuild",
|
||||
|
@ -220,12 +157,8 @@ def main(argv=None):
|
|||
"--target-directory", type=str, default="..", help="Target directory"
|
||||
)
|
||||
parser.add_argument("--verbose", action="store_true", help="Be verbose")
|
||||
parser.add_argument("--mode", choices=["auto", "vcs", "buildsystem"],
|
||||
type=str,
|
||||
help="Mechanism to use to create buildsystem")
|
||||
parser.add_argument(
|
||||
"--include-controldir", action="store_true",
|
||||
help="Clone rather than export."
|
||||
"--include-controldir", action="store_true", help="Clone rather than export."
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
@ -236,10 +169,6 @@ def main(argv=None):
|
|||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||
|
||||
tree = WorkingTree.open(args.directory)
|
||||
|
||||
packaging_tree: Optional[WorkingTree]
|
||||
subdir: Optional[str]
|
||||
|
||||
if args.packaging_directory:
|
||||
packaging_tree = WorkingTree.open(args.packaging_directory)
|
||||
with packaging_tree.lock_read():
|
||||
|
@ -250,47 +179,30 @@ def main(argv=None):
|
|||
packaging_tree = None
|
||||
subdir = None
|
||||
|
||||
if args.mode == 'vcs':
|
||||
try:
|
||||
ret = create_dist_schroot(
|
||||
tree,
|
||||
subdir=subdir,
|
||||
target_dir=os.path.abspath(args.target_directory),
|
||||
packaging_tree=packaging_tree,
|
||||
chroot=args.chroot,
|
||||
include_controldir=args.include_controldir,
|
||||
)
|
||||
except (NoBuildToolsFound, NotImplementedError):
|
||||
logging.info("No build tools found, falling back to simple export.")
|
||||
export(tree, "dist.tar.gz", "tgz", None)
|
||||
elif args.mode in ('auto', 'buildsystem'):
|
||||
try:
|
||||
ret = create_dist_schroot(
|
||||
tree,
|
||||
subdir=subdir,
|
||||
target_dir=os.path.abspath(args.target_directory),
|
||||
packaging_tree=packaging_tree,
|
||||
chroot=args.chroot,
|
||||
include_controldir=args.include_controldir,
|
||||
)
|
||||
except NoBuildToolsFound:
|
||||
if args.mode == 'buildsystem':
|
||||
logging.fatal('No build tools found, unable to create tarball')
|
||||
return 1
|
||||
logging.info(
|
||||
"No build tools found, falling back to simple export.")
|
||||
export(tree, "dist.tar.gz", "tgz", None)
|
||||
except NotImplementedError:
|
||||
if args.mode == 'buildsystem':
|
||||
logging.fatal('Unable to ask buildsystem for tarball')
|
||||
return 1
|
||||
logging.info(
|
||||
"Build system does not support dist tarball creation, "
|
||||
"falling back to simple export."
|
||||
)
|
||||
export(tree, "dist.tar.gz", "tgz", None)
|
||||
except UnidentifiedError as e:
|
||||
logging.fatal("Unidentified error: %r", e.lines)
|
||||
return 1
|
||||
except DetailedFailure as e:
|
||||
logging.fatal("Identified error during dist creation: %s", e.error)
|
||||
return 1
|
||||
except DistNoTarball:
|
||||
logging.fatal("dist operation did not create a tarball")
|
||||
return 1
|
||||
else:
|
||||
logging.info("Created %s", ret)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
except NotImplementedError:
|
||||
logging.info(
|
||||
"Build system does not support dist tarball creation, "
|
||||
"falling back to simple export."
|
||||
)
|
||||
export(tree, "dist.tar.gz", "tgz", None)
|
||||
except UnidentifiedError as e:
|
||||
logging.fatal("Unidentified error: %r", e.lines)
|
||||
except DetailedFailure as e:
|
||||
logging.fatal("Identified error during dist creation: %s", e.error)
|
||||
except DistNoTarball:
|
||||
logging.fatal("dist operation did not create a tarball")
|
||||
else:
|
||||
logging.info("Created %s", ret)
|
||||
sys.exit(0)
|
||||
|
|
|
@ -54,8 +54,7 @@ class DistCatcher(object):
|
|||
@classmethod
|
||||
def default(cls, directory):
|
||||
return cls(
|
||||
[os.path.join(directory, "dist"), directory,
|
||||
os.path.join(directory, "..")]
|
||||
[os.path.join(directory, "dist"), directory, os.path.join(directory, "..")]
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
|
@ -88,23 +87,19 @@ class DistCatcher(object):
|
|||
continue
|
||||
if len(possible_new) == 1:
|
||||
entry = possible_new[0]
|
||||
logging.info(
|
||||
"Found new tarball %s in %s.", entry.name, directory)
|
||||
logging.info("Found new tarball %s in %s.", entry.name, directory)
|
||||
self.files.append(entry.path)
|
||||
return entry.name
|
||||
elif len(possible_new) > 1:
|
||||
logging.warning(
|
||||
"Found multiple tarballs %r in %s.", possible_new,
|
||||
directory
|
||||
"Found multiple tarballs %r in %s.", possible_new, directory
|
||||
)
|
||||
self.files.extend([entry.path for entry in possible_new])
|
||||
return possible_new[0].name
|
||||
|
||||
if len(possible_updated) == 1:
|
||||
entry = possible_updated[0]
|
||||
logging.info(
|
||||
"Found updated tarball %s in %s.", entry.name,
|
||||
directory)
|
||||
logging.info("Found updated tarball %s in %s.", entry.name, directory)
|
||||
self.files.append(entry.path)
|
||||
return entry.name
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import List, Tuple, Callable, Optional, TypeVar
|
||||
from typing import List, Tuple, Callable, Any, Optional
|
||||
|
||||
from buildlog_consultant import Problem
|
||||
from buildlog_consultant.common import (
|
||||
|
@ -29,14 +29,6 @@ from . import DetailedFailure, UnidentifiedError
|
|||
from .session import Session, run_with_tee
|
||||
|
||||
|
||||
# Number of attempts to fix a build before giving up.
|
||||
DEFAULT_LIMIT = 200
|
||||
|
||||
|
||||
class FixerLimitReached(Exception):
|
||||
"""The maximum number of fixes has been reached."""
|
||||
|
||||
|
||||
class BuildFixer(object):
|
||||
"""Build fixer."""
|
||||
|
||||
|
@ -52,11 +44,7 @@ class BuildFixer(object):
|
|||
return self._fix(problem, phase)
|
||||
|
||||
|
||||
def run_detecting_problems(
|
||||
session: Session, args: List[str], check_success=None,
|
||||
quiet=False, **kwargs) -> List[str]:
|
||||
if not quiet:
|
||||
logging.info('Running %r', args)
|
||||
def run_detecting_problems(session: Session, args: List[str], check_success=None, **kwargs):
|
||||
if check_success is None:
|
||||
def check_success(retcode, contents):
|
||||
return (retcode == 0)
|
||||
|
@ -75,26 +63,17 @@ def run_detecting_problems(
|
|||
logging.warning("Build failed with unidentified error:")
|
||||
logging.warning("%s", match.line.rstrip("\n"))
|
||||
else:
|
||||
logging.warning(
|
||||
"Build failed and unable to find cause. Giving up.")
|
||||
logging.warning("Build failed and unable to find cause. Giving up.")
|
||||
raise UnidentifiedError(retcode, args, lines, secondary=match)
|
||||
raise DetailedFailure(retcode, args, error)
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
def iterate_with_build_fixers(
|
||||
fixers: List[BuildFixer],
|
||||
cb: Callable[[], T], limit=DEFAULT_LIMIT) -> T:
|
||||
def iterate_with_build_fixers(fixers: List[BuildFixer], cb: Callable[[], Any]):
|
||||
"""Call cb() until there are no more DetailedFailures we can fix.
|
||||
|
||||
Args:
|
||||
fixers: List of fixers to use to resolve issues
|
||||
cb: Callable to run the build
|
||||
limit: Maximum number of fixing attempts before giving up
|
||||
"""
|
||||
attempts = 0
|
||||
fixed_errors = []
|
||||
while True:
|
||||
to_resolve = []
|
||||
|
@ -107,13 +86,9 @@ def iterate_with_build_fixers(
|
|||
logging.info("Identified error: %r", f.error)
|
||||
if f.error in fixed_errors:
|
||||
logging.warning(
|
||||
"Failed to resolve error %r, it persisted. Giving up.",
|
||||
f.error
|
||||
"Failed to resolve error %r, it persisted. Giving up.", f.error
|
||||
)
|
||||
raise f
|
||||
attempts += 1
|
||||
if limit is not None and limit <= attempts:
|
||||
raise FixerLimitReached(limit)
|
||||
try:
|
||||
resolved = resolve_error(f.error, None, fixers=fixers)
|
||||
except DetailedFailure as n:
|
||||
|
@ -125,25 +100,23 @@ def iterate_with_build_fixers(
|
|||
else:
|
||||
if not resolved:
|
||||
logging.warning(
|
||||
"Failed to find resolution for error %r. Giving up.",
|
||||
f.error
|
||||
"Failed to find resolution for error %r. Giving up.", f.error
|
||||
)
|
||||
raise f
|
||||
fixed_errors.append(f.error)
|
||||
|
||||
|
||||
def run_with_build_fixers(
|
||||
fixers: Optional[List[BuildFixer]], session: Session, args: List[str],
|
||||
quiet: bool = False, **kwargs
|
||||
) -> List[str]:
|
||||
session: Session, args: List[str], fixers: Optional[List[BuildFixer]], **kwargs
|
||||
):
|
||||
if fixers is None:
|
||||
fixers = []
|
||||
return iterate_with_build_fixers(
|
||||
fixers,
|
||||
partial(run_detecting_problems, session, args, quiet=quiet, **kwargs))
|
||||
fixers, partial(run_detecting_problems, session, args, **kwargs)
|
||||
)
|
||||
|
||||
|
||||
def resolve_error(error, phase, fixers) -> bool:
|
||||
def resolve_error(error, phase, fixers):
|
||||
relevant_fixers = []
|
||||
for fixer in fixers:
|
||||
if fixer.can_fix(error):
|
||||
|
|
|
@ -21,10 +21,8 @@ from typing import Tuple
|
|||
from buildlog_consultant import Problem
|
||||
from buildlog_consultant.common import (
|
||||
MissingGitIdentity,
|
||||
MissingGoSumEntry,
|
||||
MissingSecretGpgKey,
|
||||
MissingAutoconfMacro,
|
||||
MissingGnulibDirectory,
|
||||
)
|
||||
from ognibuild.requirements import AutoconfMacroRequirement
|
||||
from ognibuild.resolver import UnsatisfiedRequirements
|
||||
|
@ -32,18 +30,6 @@ from ognibuild.resolver import UnsatisfiedRequirements
|
|||
from .fix_build import BuildFixer
|
||||
|
||||
|
||||
class GnulibDirectoryFixer(BuildFixer):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
def can_fix(self, problem: Problem):
|
||||
return isinstance(problem, MissingGnulibDirectory)
|
||||
|
||||
def _fix(self, problem: Problem, phase: Tuple[str, ...]):
|
||||
self.session.check_call(["./gnulib.sh"])
|
||||
return True
|
||||
|
||||
|
||||
class GitIdentityFixer(BuildFixer):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
@ -91,26 +77,6 @@ Passphrase: ""
|
|||
return False
|
||||
|
||||
|
||||
class MissingGoSumEntryFixer(BuildFixer):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
def __repr__(self):
|
||||
return "%s()" % (type(self).__name__)
|
||||
|
||||
def __str__(self):
|
||||
return "missing go.sum entry fixer"
|
||||
|
||||
def can_fix(self, error):
|
||||
return isinstance(error, MissingGoSumEntry)
|
||||
|
||||
def _fix(self, error, phase):
|
||||
from .fix_build import run_detecting_problems
|
||||
run_detecting_problems(
|
||||
self.session, ["go", "mod", "download", error.package])
|
||||
return True
|
||||
|
||||
|
||||
class UnexpandedAutoconfMacroFixer(BuildFixer):
|
||||
def __init__(self, session, resolver):
|
||||
self.session = session
|
||||
|
|
|
@ -21,13 +21,11 @@ def run_info(session, buildsystems, fixers=None):
|
|||
print("%r:" % buildsystem)
|
||||
deps = {}
|
||||
try:
|
||||
for kind, dep in buildsystem.get_declared_dependencies(
|
||||
session, fixers=fixers):
|
||||
for kind, dep in buildsystem.get_declared_dependencies(session, fixers=fixers):
|
||||
deps.setdefault(kind, []).append(dep)
|
||||
except NotImplementedError:
|
||||
print(
|
||||
"\tUnable to detect declared dependencies for this type of "
|
||||
"build system"
|
||||
"\tUnable to detect declared dependencies for this type of build system"
|
||||
)
|
||||
if deps:
|
||||
print("\tDeclared dependencies:")
|
||||
|
@ -37,11 +35,9 @@ def run_info(session, buildsystems, fixers=None):
|
|||
print("\t\t\t%s" % dep)
|
||||
print("")
|
||||
try:
|
||||
outputs = list(buildsystem.get_declared_outputs(
|
||||
session, fixers=fixers))
|
||||
outputs = list(buildsystem.get_declared_outputs(session, fixers=fixers))
|
||||
except NotImplementedError:
|
||||
print("\tUnable to detect declared outputs for this type of "
|
||||
"build system")
|
||||
print("\tUnable to detect declared outputs for this type of build system")
|
||||
outputs = []
|
||||
if outputs:
|
||||
print("\tDeclared outputs:")
|
||||
|
|
|
@ -15,34 +15,21 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from functools import partial
|
||||
from .buildsystem import NoBuildToolsFound, InstallTarget
|
||||
from typing import Optional
|
||||
|
||||
from .buildsystem import NoBuildToolsFound, InstallTarget
|
||||
from .fix_build import iterate_with_build_fixers
|
||||
from .logs import NoLogManager
|
||||
|
||||
|
||||
def run_install(
|
||||
session, buildsystems, resolver, fixers, *, user: bool = False,
|
||||
prefix: Optional[str] = None, log_manager=None):
|
||||
def run_install(session, buildsystems, resolver, fixers, user: bool = False, prefix: Optional[str] = None):
|
||||
# Some things want to write to the user's home directory,
|
||||
# e.g. pip caches in ~/.cache
|
||||
session.create_home()
|
||||
|
||||
if log_manager is None:
|
||||
log_manager = NoLogManager()
|
||||
|
||||
install_target = InstallTarget()
|
||||
install_target.user = user
|
||||
install_target.prefix = prefix
|
||||
|
||||
for buildsystem in buildsystems:
|
||||
iterate_with_build_fixers(
|
||||
fixers,
|
||||
log_manager.wrap(
|
||||
partial(buildsystem.install, session, resolver,
|
||||
install_target)))
|
||||
buildsystem.install(session, resolver, fixers, install_target)
|
||||
return
|
||||
|
||||
raise NoBuildToolsFound()
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2018 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from contextlib import contextmanager
|
||||
import subprocess
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
@contextmanager
|
||||
def copy_output(output_log: str, tee: bool = False):
|
||||
old_stdout = os.dup(sys.stdout.fileno())
|
||||
old_stderr = os.dup(sys.stderr.fileno())
|
||||
if tee:
|
||||
p = subprocess.Popen(["tee", output_log], stdin=subprocess.PIPE)
|
||||
newfd = p.stdin
|
||||
else:
|
||||
newfd = open(output_log, 'wb')
|
||||
os.dup2(newfd.fileno(), sys.stdout.fileno()) # type: ignore
|
||||
os.dup2(newfd.fileno(), sys.stderr.fileno()) # type: ignore
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
os.dup2(old_stdout, sys.stdout.fileno())
|
||||
os.dup2(old_stderr, sys.stderr.fileno())
|
||||
if newfd is not None:
|
||||
newfd.close()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def redirect_output(to_file):
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
old_stdout = os.dup(sys.stdout.fileno())
|
||||
old_stderr = os.dup(sys.stderr.fileno())
|
||||
os.dup2(to_file.fileno(), sys.stdout.fileno()) # type: ignore
|
||||
os.dup2(to_file.fileno(), sys.stderr.fileno()) # type: ignore
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
os.dup2(old_stdout, sys.stdout.fileno())
|
||||
os.dup2(old_stderr, sys.stderr.fileno())
|
||||
|
||||
|
||||
def rotate_logfile(source_path: str) -> None:
|
||||
if os.path.exists(source_path):
|
||||
(directory_path, name) = os.path.split(source_path)
|
||||
i = 1
|
||||
while os.path.exists(
|
||||
os.path.join(directory_path, "%s.%d" % (name, i))):
|
||||
i += 1
|
||||
target_path = os.path.join(directory_path, "%s.%d" % (name, i))
|
||||
os.rename(source_path, target_path)
|
||||
logging.debug("Storing previous build log at %s", target_path)
|
||||
|
||||
|
||||
class LogManager(object):
|
||||
|
||||
def wrap(self, fn):
|
||||
raise NotImplementedError(self.wrap)
|
||||
|
||||
|
||||
class DirectoryLogManager(LogManager):
|
||||
|
||||
def __init__(self, path, mode):
|
||||
self.path = path
|
||||
self.mode = mode
|
||||
|
||||
def wrap(self, fn):
|
||||
def _run(*args, **kwargs):
|
||||
rotate_logfile(self.path)
|
||||
if self.mode == 'copy':
|
||||
with copy_output(self.path, tee=True):
|
||||
return fn(*args, **kwargs)
|
||||
elif self.mode == 'redirect':
|
||||
with copy_output(self.path, tee=False):
|
||||
return fn(*args, **kwargs)
|
||||
else:
|
||||
raise NotImplementedError(self.mode)
|
||||
return _run
|
||||
|
||||
|
||||
class NoLogManager(LogManager):
|
||||
|
||||
def wrap(self, fn):
|
||||
return fn
|
|
@ -26,19 +26,16 @@ from . import Requirement
|
|||
|
||||
class PythonPackageRequirement(Requirement):
|
||||
|
||||
family = "python-package"
|
||||
|
||||
package: str
|
||||
|
||||
def __init__(
|
||||
self, package, python_version=None, specs=None,
|
||||
minimum_version=None):
|
||||
def __init__(self, package, python_version=None, specs=None, minimum_version=None):
|
||||
super(PythonPackageRequirement, self).__init__("python-package")
|
||||
self.package = package
|
||||
self.python_version = python_version
|
||||
if minimum_version is not None:
|
||||
specs = [(">=", minimum_version)]
|
||||
if specs is None:
|
||||
specs = []
|
||||
if minimum_version is not None:
|
||||
specs.append((">=", minimum_version))
|
||||
self.specs = specs
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -56,29 +53,11 @@ class PythonPackageRequirement(Requirement):
|
|||
return "python package: %s" % (self.package,)
|
||||
|
||||
@classmethod
|
||||
def from_requirement_str(cls, text, python_version=None):
|
||||
def from_requirement_str(cls, text):
|
||||
from requirements.requirement import Requirement
|
||||
|
||||
req = Requirement.parse(text)
|
||||
return cls(
|
||||
package=req.name, specs=req.specs, python_version=python_version)
|
||||
|
||||
def requirement_str(self):
|
||||
if self.specs:
|
||||
return '%s;%s' % (
|
||||
self.package, ','.join([''.join(s) for s in self.specs]))
|
||||
return self.package
|
||||
|
||||
@classmethod
|
||||
def _from_json(cls, js):
|
||||
if isinstance(js, str):
|
||||
return cls.from_requirement_str(js)
|
||||
return cls.from_requirement_str(js[0], python_version=js[1])
|
||||
|
||||
def _json(self):
|
||||
if self.python_version:
|
||||
return [self.requirement_str(), self.python_version]
|
||||
return self.requirement_str()
|
||||
return cls(package=req.name, specs=req.specs)
|
||||
|
||||
def met(self, session):
|
||||
if self.python_version == "cpython3":
|
||||
|
@ -95,8 +74,7 @@ class PythonPackageRequirement(Requirement):
|
|||
raise NotImplementedError
|
||||
text = self.package + ",".join(["".join(spec) for spec in self.specs])
|
||||
p = session.Popen(
|
||||
[cmd, "-c",
|
||||
"import pkg_resources; pkg_resources.require(%r)" % text],
|
||||
[cmd, "-c", "import pkg_resources; pkg_resources.require(%r)" % text],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
@ -104,33 +82,16 @@ class PythonPackageRequirement(Requirement):
|
|||
return p.returncode == 0
|
||||
|
||||
|
||||
Requirement.register_json(PythonPackageRequirement)
|
||||
|
||||
|
||||
class LatexPackageRequirement(Requirement):
|
||||
|
||||
family = "latex-package"
|
||||
|
||||
def __init__(self, package: str):
|
||||
self.package = package
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.package)
|
||||
|
||||
def _json(self):
|
||||
return self.package
|
||||
|
||||
def _from_json(cls, package):
|
||||
return cls(package)
|
||||
|
||||
|
||||
Requirement.register_json(LatexPackageRequirement)
|
||||
|
||||
|
||||
class PhpPackageRequirement(Requirement):
|
||||
|
||||
family = "php-package"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
package: str,
|
||||
|
@ -143,13 +104,6 @@ class PhpPackageRequirement(Requirement):
|
|||
self.min_version = min_version
|
||||
self.max_version = max_version
|
||||
|
||||
def _json(self):
|
||||
return (self.package, self.channel, self.min_version, self.max_version)
|
||||
|
||||
@classmethod
|
||||
def _from_json(cls, js):
|
||||
return cls(*js)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, %r, %r, %r)" % (
|
||||
type(self).__name__,
|
||||
|
@ -160,24 +114,14 @@ class PhpPackageRequirement(Requirement):
|
|||
)
|
||||
|
||||
|
||||
Requirement.register_json(PhpPackageRequirement)
|
||||
|
||||
|
||||
class BinaryRequirement(Requirement):
|
||||
|
||||
family = "binary"
|
||||
binary_name: str
|
||||
|
||||
def __init__(self, binary_name):
|
||||
super(BinaryRequirement, self).__init__("binary")
|
||||
self.binary_name = binary_name
|
||||
|
||||
def _json(self):
|
||||
return self.binary_name
|
||||
|
||||
@classmethod
|
||||
def _from_json(cls, js):
|
||||
return cls(js)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.binary_name)
|
||||
|
||||
|
@ -191,54 +135,14 @@ class BinaryRequirement(Requirement):
|
|||
return p.returncode == 0
|
||||
|
||||
|
||||
Requirement.register_json(BinaryRequirement)
|
||||
|
||||
|
||||
class PHPExtensionRequirement(Requirement):
|
||||
|
||||
family = "php-extension"
|
||||
extension: str
|
||||
|
||||
def __init__(self, extension: str):
|
||||
self.extension = extension
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.extension)
|
||||
|
||||
|
||||
class PytestPluginRequirement(Requirement):
|
||||
|
||||
family = "pytest-plugin"
|
||||
|
||||
plugin: str
|
||||
|
||||
def __init__(self, plugin: str):
|
||||
self.plugin = plugin
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.plugin)
|
||||
|
||||
|
||||
class VcsControlDirectoryAccessRequirement(Requirement):
|
||||
|
||||
vcs: List[str]
|
||||
family = "vcs-access"
|
||||
|
||||
def __init__(self, vcs):
|
||||
self.vcs = vcs
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.vcs)
|
||||
|
||||
|
||||
class PerlModuleRequirement(Requirement):
|
||||
|
||||
module: str
|
||||
filename: Optional[str]
|
||||
inc: Optional[List[str]]
|
||||
family = "perl-module"
|
||||
|
||||
def __init__(self, module, filename=None, inc=None):
|
||||
super(PerlModuleRequirement, self).__init__("perl-module")
|
||||
self.module = module
|
||||
self.filename = filename
|
||||
self.inc = inc
|
||||
|
@ -254,10 +158,10 @@ class PerlModuleRequirement(Requirement):
|
|||
class VagueDependencyRequirement(Requirement):
|
||||
|
||||
name: str
|
||||
family = "vague"
|
||||
minimum_version: Optional[str] = None
|
||||
|
||||
def __init__(self, name, minimum_version=None):
|
||||
super(VagueDependencyRequirement, self).__init__("vague")
|
||||
self.name = name
|
||||
self.minimum_version = minimum_version
|
||||
|
||||
|
@ -265,26 +169,19 @@ class VagueDependencyRequirement(Requirement):
|
|||
if " " not in self.name:
|
||||
yield BinaryRequirement(self.name)
|
||||
yield LibraryRequirement(self.name)
|
||||
yield PkgConfigRequirement(
|
||||
self.name, minimum_version=self.minimum_version)
|
||||
yield PkgConfigRequirement(self.name, minimum_version=self.minimum_version)
|
||||
if self.name.lower() != self.name:
|
||||
yield BinaryRequirement(self.name.lower())
|
||||
yield LibraryRequirement(self.name.lower())
|
||||
yield PkgConfigRequirement(
|
||||
self.name.lower(), minimum_version=self.minimum_version)
|
||||
try:
|
||||
from .resolver.apt import AptRequirement
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
yield PkgConfigRequirement(self.name.lower(), minimum_version=self.minimum_version)
|
||||
from .resolver.apt import AptRequirement
|
||||
|
||||
yield AptRequirement.simple(self.name.lower(), minimum_version=self.minimum_version)
|
||||
if self.name.lower().startswith('lib'):
|
||||
devname = '%s-dev' % self.name.lower()
|
||||
else:
|
||||
yield AptRequirement.simple(
|
||||
self.name.lower(), minimum_version=self.minimum_version)
|
||||
if self.name.lower().startswith('lib'):
|
||||
devname = '%s-dev' % self.name.lower()
|
||||
else:
|
||||
devname = 'lib%s-dev' % self.name.lower()
|
||||
yield AptRequirement.simple(
|
||||
devname, minimum_version=self.minimum_version)
|
||||
devname = 'lib%s-dev' % self.name.lower()
|
||||
yield AptRequirement.simple(devname, minimum_version=self.minimum_version)
|
||||
|
||||
def met(self, session):
|
||||
for x in self.expand():
|
||||
|
@ -295,36 +192,19 @@ class VagueDependencyRequirement(Requirement):
|
|||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.name)
|
||||
|
||||
def __str__(self):
|
||||
if self.minimum_version:
|
||||
return "%s >= %s" % (self.name, self.minimum_version)
|
||||
return self.name
|
||||
|
||||
|
||||
class NodePackageRequirement(Requirement):
|
||||
|
||||
package: str
|
||||
family = "npm-package"
|
||||
|
||||
def __init__(self, package):
|
||||
super(NodePackageRequirement, self).__init__("npm-package")
|
||||
self.package = package
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.package)
|
||||
|
||||
|
||||
class LuaModuleRequirement(Requirement):
|
||||
|
||||
module: str
|
||||
family = "lua-module"
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.module)
|
||||
|
||||
|
||||
class PerlPreDeclaredRequirement(Requirement):
|
||||
|
||||
name: str
|
||||
|
@ -347,9 +227,8 @@ class PerlPreDeclaredRequirement(Requirement):
|
|||
'auto_set_bugtracker': 'Module::Install::Bugtracker',
|
||||
}
|
||||
|
||||
family = "perl-predeclared"
|
||||
|
||||
def __init__(self, name):
|
||||
super(PerlPreDeclaredRequirement, self).__init__("perl-predeclared")
|
||||
self.name = name
|
||||
|
||||
def lookup_module(self):
|
||||
|
@ -363,9 +242,9 @@ class PerlPreDeclaredRequirement(Requirement):
|
|||
class NodeModuleRequirement(Requirement):
|
||||
|
||||
module: str
|
||||
family = "npm-module"
|
||||
|
||||
def __init__(self, module):
|
||||
super(NodeModuleRequirement, self).__init__("npm-module")
|
||||
self.module = module
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -376,45 +255,41 @@ class CargoCrateRequirement(Requirement):
|
|||
|
||||
crate: str
|
||||
features: Set[str]
|
||||
api_version: Optional[str]
|
||||
minimum_version: Optional[str]
|
||||
family = "cargo-crate"
|
||||
version: Optional[str]
|
||||
|
||||
def __init__(self, crate, features=None, api_version=None,
|
||||
minimum_version=None):
|
||||
def __init__(self, crate, features=None, version=None):
|
||||
super(CargoCrateRequirement, self).__init__("cargo-crate")
|
||||
self.crate = crate
|
||||
if features is None:
|
||||
features = set()
|
||||
self.features = features
|
||||
self.api_version = api_version
|
||||
self.minimum_version = minimum_version
|
||||
self.version = version
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, features=%r, api_version=%r, minimum_version=%r)" % (
|
||||
return "%s(%r, features=%r, version=%r)" % (
|
||||
type(self).__name__,
|
||||
self.crate,
|
||||
self.features,
|
||||
self.api_version,
|
||||
self.minimum_version,
|
||||
self.version,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
ret = "cargo crate: %s %s" % (
|
||||
self.crate,
|
||||
self.api_version or "")
|
||||
if self.features:
|
||||
ret += " (%s)" % (", ".join(sorted(self.features)))
|
||||
if self.minimum_version:
|
||||
ret += " (>= %s)" % self.minimum_version
|
||||
return ret
|
||||
return "cargo crate: %s %s (%s)" % (
|
||||
self.crate,
|
||||
self.version or "",
|
||||
", ".join(sorted(self.features)),
|
||||
)
|
||||
else:
|
||||
return "cargo crate: %s %s" % (self.crate, self.version or "")
|
||||
|
||||
|
||||
class PkgConfigRequirement(Requirement):
|
||||
|
||||
module: str
|
||||
family = "pkg-config"
|
||||
|
||||
def __init__(self, module, minimum_version=None):
|
||||
super(PkgConfigRequirement, self).__init__("pkg-config")
|
||||
self.module = module
|
||||
self.minimum_version = minimum_version
|
||||
|
||||
|
@ -426,9 +301,9 @@ class PkgConfigRequirement(Requirement):
|
|||
class PathRequirement(Requirement):
|
||||
|
||||
path: str
|
||||
family = "path"
|
||||
|
||||
def __init__(self, path):
|
||||
super(PathRequirement, self).__init__("path")
|
||||
self.path = path
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -438,9 +313,9 @@ class PathRequirement(Requirement):
|
|||
class CHeaderRequirement(Requirement):
|
||||
|
||||
header: str
|
||||
family = "c-header"
|
||||
|
||||
def __init__(self, header):
|
||||
super(CHeaderRequirement, self).__init__("c-header")
|
||||
self.header = header
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -448,15 +323,16 @@ class CHeaderRequirement(Requirement):
|
|||
|
||||
|
||||
class JavaScriptRuntimeRequirement(Requirement):
|
||||
family = "javascript-runtime"
|
||||
def __init__(self):
|
||||
super(JavaScriptRuntimeRequirement, self).__init__("javascript-runtime")
|
||||
|
||||
|
||||
class ValaPackageRequirement(Requirement):
|
||||
|
||||
package: str
|
||||
family = "vala"
|
||||
|
||||
def __init__(self, package: str):
|
||||
super(ValaPackageRequirement, self).__init__("vala")
|
||||
self.package = package
|
||||
|
||||
|
||||
|
@ -464,9 +340,9 @@ class RubyGemRequirement(Requirement):
|
|||
|
||||
gem: str
|
||||
minimum_version: Optional[str]
|
||||
family = "gem"
|
||||
|
||||
def __init__(self, gem: str, minimum_version: Optional[str]):
|
||||
super(RubyGemRequirement, self).__init__("gem")
|
||||
self.gem = gem
|
||||
self.minimum_version = minimum_version
|
||||
|
||||
|
@ -475,16 +351,12 @@ class GoPackageRequirement(Requirement):
|
|||
|
||||
package: str
|
||||
version: Optional[str]
|
||||
family = "go-package"
|
||||
|
||||
def __init__(self, package: str, version: Optional[str] = None):
|
||||
super(GoPackageRequirement, self).__init__("go-package")
|
||||
self.package = package
|
||||
self.version = version
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, version=%r)" % (
|
||||
type(self).__name__, self.package, self.version)
|
||||
|
||||
def __str__(self):
|
||||
if self.version:
|
||||
return "go package: %s (= %s)" % (self.package, self.version)
|
||||
|
@ -494,9 +366,9 @@ class GoPackageRequirement(Requirement):
|
|||
class GoRequirement(Requirement):
|
||||
|
||||
version: Optional[str]
|
||||
family = "go"
|
||||
|
||||
def __init__(self, version: Optional[str] = None):
|
||||
super(GoRequirement, self).__init__("go")
|
||||
self.version = version
|
||||
|
||||
def __str__(self):
|
||||
|
@ -506,18 +378,18 @@ class GoRequirement(Requirement):
|
|||
class DhAddonRequirement(Requirement):
|
||||
|
||||
path: str
|
||||
family = "dh-addon"
|
||||
|
||||
def __init__(self, path: str):
|
||||
super(DhAddonRequirement, self).__init__("dh-addon")
|
||||
self.path = path
|
||||
|
||||
|
||||
class PhpClassRequirement(Requirement):
|
||||
|
||||
php_class: str
|
||||
family = "php-class"
|
||||
|
||||
def __init__(self, php_class: str):
|
||||
super(PhpClassRequirement, self).__init__("php-class")
|
||||
self.php_class = php_class
|
||||
|
||||
|
||||
|
@ -525,9 +397,9 @@ class RPackageRequirement(Requirement):
|
|||
|
||||
package: str
|
||||
minimum_version: Optional[str]
|
||||
family = "r-package"
|
||||
|
||||
def __init__(self, package: str, minimum_version: Optional[str] = None):
|
||||
super(RPackageRequirement, self).__init__("r-package")
|
||||
self.package = package
|
||||
self.minimum_version = minimum_version
|
||||
|
||||
|
@ -540,8 +412,7 @@ class RPackageRequirement(Requirement):
|
|||
|
||||
def __str__(self):
|
||||
if self.minimum_version:
|
||||
return "R package: %s (>= %s)" % (
|
||||
self.package, self.minimum_version)
|
||||
return "R package: %s (>= %s)" % (self.package, self.minimum_version)
|
||||
else:
|
||||
return "R package: %s" % (self.package,)
|
||||
|
||||
|
@ -561,9 +432,9 @@ class OctavePackageRequirement(Requirement):
|
|||
|
||||
package: str
|
||||
minimum_version: Optional[str]
|
||||
family = "octave-package"
|
||||
|
||||
def __init__(self, package: str, minimum_version: Optional[str] = None):
|
||||
super(OctavePackageRequirement, self).__init__("octave-package")
|
||||
self.package = package
|
||||
self.minimum_version = minimum_version
|
||||
|
||||
|
@ -576,8 +447,7 @@ class OctavePackageRequirement(Requirement):
|
|||
|
||||
def __str__(self):
|
||||
if self.minimum_version:
|
||||
return "Octave package: %s (>= %s)" % (
|
||||
self.package, self.minimum_version)
|
||||
return "Octave package: %s (>= %s)" % (self.package, self.minimum_version)
|
||||
else:
|
||||
return "Octave package: %s" % (self.package,)
|
||||
|
||||
|
@ -596,9 +466,9 @@ class OctavePackageRequirement(Requirement):
|
|||
class LibraryRequirement(Requirement):
|
||||
|
||||
library: str
|
||||
family = "lib"
|
||||
|
||||
def __init__(self, library: str):
|
||||
super(LibraryRequirement, self).__init__("lib")
|
||||
self.library = library
|
||||
|
||||
|
||||
|
@ -606,9 +476,9 @@ class StaticLibraryRequirement(Requirement):
|
|||
|
||||
library: str
|
||||
filename: str
|
||||
family = "static-lib"
|
||||
|
||||
def __init__(self, library: str, filename: str):
|
||||
super(StaticLibraryRequirement, self).__init__("static-lib")
|
||||
self.library = library
|
||||
self.filename = filename
|
||||
|
||||
|
@ -616,18 +486,18 @@ class StaticLibraryRequirement(Requirement):
|
|||
class RubyFileRequirement(Requirement):
|
||||
|
||||
filename: str
|
||||
family = "ruby-file"
|
||||
|
||||
def __init__(self, filename: str):
|
||||
super(RubyFileRequirement, self).__init__("ruby-file")
|
||||
self.filename = filename
|
||||
|
||||
|
||||
class XmlEntityRequirement(Requirement):
|
||||
|
||||
url: str
|
||||
family = "xml-entity"
|
||||
|
||||
def __init__(self, url: str):
|
||||
super(XmlEntityRequirement, self).__init__("xml-entity")
|
||||
self.url = url
|
||||
|
||||
|
||||
|
@ -635,9 +505,9 @@ class SprocketsFileRequirement(Requirement):
|
|||
|
||||
content_type: str
|
||||
name: str
|
||||
family = "sprockets-file"
|
||||
|
||||
def __init__(self, content_type: str, name: str):
|
||||
super(SprocketsFileRequirement, self).__init__("sprockets-file")
|
||||
self.content_type = content_type
|
||||
self.name = name
|
||||
|
||||
|
@ -645,29 +515,27 @@ class SprocketsFileRequirement(Requirement):
|
|||
class JavaClassRequirement(Requirement):
|
||||
|
||||
classname: str
|
||||
family = "java-class"
|
||||
|
||||
def __init__(self, classname: str):
|
||||
super(JavaClassRequirement, self).__init__("java-class")
|
||||
self.classname = classname
|
||||
|
||||
|
||||
class CMakefileRequirement(Requirement):
|
||||
|
||||
filename: str
|
||||
version: Optional[str]
|
||||
family = "cmake-file"
|
||||
|
||||
def __init__(self, filename: str, version=None):
|
||||
def __init__(self, filename: str):
|
||||
super(CMakefileRequirement, self).__init__("cmake-file")
|
||||
self.filename = filename
|
||||
self.version = version
|
||||
|
||||
|
||||
class HaskellPackageRequirement(Requirement):
|
||||
|
||||
package: str
|
||||
family = "haskell-package"
|
||||
|
||||
def __init__(self, package: str, specs=None):
|
||||
super(HaskellPackageRequirement, self).__init__("haskell-package")
|
||||
self.package = package
|
||||
self.specs = specs
|
||||
|
||||
|
@ -683,9 +551,9 @@ class MavenArtifactRequirement(Requirement):
|
|||
artifact_id: str
|
||||
version: Optional[str]
|
||||
kind: Optional[str]
|
||||
family = "maven-artifact"
|
||||
|
||||
def __init__(self, group_id, artifact_id, version=None, kind=None):
|
||||
super(MavenArtifactRequirement, self).__init__("maven-artifact")
|
||||
self.group_id = group_id
|
||||
self.artifact_id = artifact_id
|
||||
self.version = version
|
||||
|
@ -698,11 +566,6 @@ class MavenArtifactRequirement(Requirement):
|
|||
self.version,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(group_id=%r, artifact_id=%r, version=%r, kind=%r)" % (
|
||||
type(self).__name__, self.group_id, self.artifact_id,
|
||||
self.version, self.kind)
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, text):
|
||||
return cls.from_tuple(text.split(":"))
|
||||
|
@ -724,16 +587,17 @@ class MavenArtifactRequirement(Requirement):
|
|||
|
||||
|
||||
class GnomeCommonRequirement(Requirement):
|
||||
family = "gnome-common"
|
||||
def __init__(self):
|
||||
super(GnomeCommonRequirement, self).__init__("gnome-common")
|
||||
|
||||
|
||||
class JDKFileRequirement(Requirement):
|
||||
|
||||
jdk_path: str
|
||||
filename: str
|
||||
family = "jdk-file"
|
||||
|
||||
def __init__(self, jdk_path: str, filename: str):
|
||||
super(JDKFileRequirement, self).__init__("jdk-file")
|
||||
self.jdk_path = jdk_path
|
||||
self.filename = filename
|
||||
|
||||
|
@ -743,70 +607,55 @@ class JDKFileRequirement(Requirement):
|
|||
|
||||
|
||||
class JDKRequirement(Requirement):
|
||||
family = "jdk"
|
||||
def __init__(self):
|
||||
super(JDKRequirement, self).__init__("jdk")
|
||||
|
||||
|
||||
class JRERequirement(Requirement):
|
||||
family = "jre"
|
||||
|
||||
|
||||
class QtModuleRequirement(Requirement):
|
||||
family = "qt-module"
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
def __init__(self):
|
||||
super(JRERequirement, self).__init__("jre")
|
||||
|
||||
|
||||
class QTRequirement(Requirement):
|
||||
family = "qt"
|
||||
def __init__(self):
|
||||
super(QTRequirement, self).__init__("qt")
|
||||
|
||||
|
||||
class X11Requirement(Requirement):
|
||||
family = "x11"
|
||||
def __init__(self):
|
||||
super(X11Requirement, self).__init__("x11")
|
||||
|
||||
|
||||
class CertificateAuthorityRequirement(Requirement):
|
||||
family = "ca-cert"
|
||||
|
||||
def __init__(self, url):
|
||||
super(CertificateAuthorityRequirement, self).__init__("ca-cert")
|
||||
self.url = url
|
||||
|
||||
|
||||
class PerlFileRequirement(Requirement):
|
||||
|
||||
filename: str
|
||||
family = "perl-file"
|
||||
|
||||
def __init__(self, filename: str):
|
||||
super(PerlFileRequirement, self).__init__("perl-file")
|
||||
self.filename = filename
|
||||
|
||||
|
||||
class AutoconfMacroRequirement(Requirement):
|
||||
|
||||
family = "autoconf-macro"
|
||||
macro: str
|
||||
|
||||
def __init__(self, macro: str):
|
||||
super(AutoconfMacroRequirement, self).__init__("autoconf-macro")
|
||||
self.macro = macro
|
||||
|
||||
def _json(self):
|
||||
return self.macro
|
||||
|
||||
@classmethod
|
||||
def _from_json(cls, macro):
|
||||
return cls(macro)
|
||||
|
||||
|
||||
Requirement.register_json(AutoconfMacroRequirement)
|
||||
|
||||
|
||||
class LibtoolRequirement(Requirement):
|
||||
family = "libtool"
|
||||
def __init__(self):
|
||||
super(LibtoolRequirement, self).__init__("libtool")
|
||||
|
||||
|
||||
class IntrospectionTypelibRequirement(Requirement):
|
||||
family = "introspection-type-lib"
|
||||
|
||||
def __init__(self, library):
|
||||
self.library = library
|
||||
|
||||
|
@ -816,9 +665,9 @@ class PythonModuleRequirement(Requirement):
|
|||
module: str
|
||||
python_version: Optional[str]
|
||||
minimum_version: Optional[str]
|
||||
family = "python-module"
|
||||
|
||||
def __init__(self, module, python_version=None, minimum_version=None):
|
||||
super(PythonModuleRequirement, self).__init__("python-module")
|
||||
self.module = module
|
||||
self.python_version = python_version
|
||||
self.minimum_version = minimum_version
|
||||
|
@ -853,25 +702,7 @@ class PythonModuleRequirement(Requirement):
|
|||
class BoostComponentRequirement(Requirement):
|
||||
|
||||
name: str
|
||||
family = "boost-component"
|
||||
|
||||
def __init__(self, name):
|
||||
super(BoostComponentRequirement, self).__init__("boost-component")
|
||||
self.name = name
|
||||
|
||||
|
||||
class KF5ComponentRequirement(Requirement):
|
||||
|
||||
name: str
|
||||
family = "kf5-component"
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class GnulibDirectoryRequirement(Requirement):
|
||||
|
||||
directory: str
|
||||
family = "gnulib"
|
||||
|
||||
def __init__(self, directory):
|
||||
self.directory = directory
|
||||
|
|
|
@ -18,11 +18,8 @@
|
|||
|
||||
import logging
|
||||
import subprocess
|
||||
from typing import Optional, List, Type
|
||||
|
||||
from .. import UnidentifiedError, Requirement
|
||||
from .. import UnidentifiedError
|
||||
from ..fix_build import run_detecting_problems
|
||||
from ..session import Session
|
||||
|
||||
|
||||
class UnsatisfiedRequirements(Exception):
|
||||
|
@ -31,22 +28,13 @@ class UnsatisfiedRequirements(Exception):
|
|||
|
||||
|
||||
class Resolver(object):
|
||||
|
||||
name: str
|
||||
|
||||
def __init__(self, session, user_local):
|
||||
raise NotImplementedError(self.__init__)
|
||||
|
||||
def install(self, requirements: List[Requirement]):
|
||||
def install(self, requirements):
|
||||
raise NotImplementedError(self.install)
|
||||
|
||||
def resolve(self, requirement: Requirement) -> Optional[Requirement]:
|
||||
def resolve(self, requirement):
|
||||
raise NotImplementedError(self.resolve)
|
||||
|
||||
def resolve_all(self, requirement: Requirement) -> List[Requirement]:
|
||||
raise NotImplementedError(self.resolve_all)
|
||||
|
||||
def explain(self, requirements: List[Requirement]):
|
||||
def explain(self, requirements):
|
||||
raise NotImplementedError(self.explain)
|
||||
|
||||
def env(self):
|
||||
|
@ -54,15 +42,13 @@ class Resolver(object):
|
|||
|
||||
|
||||
class CPANResolver(Resolver):
|
||||
name = "cpan"
|
||||
|
||||
def __init__(self, session, user_local=False, skip_tests=True):
|
||||
self.session = session
|
||||
self.user_local = user_local
|
||||
self.skip_tests = skip_tests
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return "cpan"
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.session)
|
||||
|
@ -123,8 +109,7 @@ class TlmgrResolver(Resolver):
|
|||
self.repository = repository
|
||||
|
||||
def __str__(self):
|
||||
if (self.repository.startswith('http://')
|
||||
or self.repository.startswith('https://')):
|
||||
if self.repository.startswith('http://') or self.repository.startswith('https://'):
|
||||
return 'tlmgr(%r)' % self.repository
|
||||
else:
|
||||
return self.repository
|
||||
|
@ -169,8 +154,7 @@ class TlmgrResolver(Resolver):
|
|||
try:
|
||||
run_detecting_problems(self.session, cmd, user=user)
|
||||
except UnidentifiedError as e:
|
||||
if ("tlmgr: user mode not initialized, "
|
||||
"please read the documentation!") in e.lines:
|
||||
if "tlmgr: user mode not initialized, please read the documentation!" in e.lines:
|
||||
self.session.check_call(['tlmgr', 'init-usertree'])
|
||||
else:
|
||||
raise
|
||||
|
@ -179,7 +163,6 @@ class TlmgrResolver(Resolver):
|
|||
|
||||
|
||||
class CTANResolver(TlmgrResolver):
|
||||
name = "ctan"
|
||||
|
||||
def __init__(self, session, user_local=False):
|
||||
super(CTANResolver, self).__init__(
|
||||
|
@ -187,16 +170,13 @@ class CTANResolver(TlmgrResolver):
|
|||
|
||||
|
||||
class RResolver(Resolver):
|
||||
|
||||
name: str
|
||||
|
||||
def __init__(self, session, repos, user_local=False):
|
||||
self.session = session
|
||||
self.repos = repos
|
||||
self.user_local = user_local
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return "cran"
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r, %r)" % (type(self).__name__, self.session, self.repos)
|
||||
|
@ -241,14 +221,12 @@ class RResolver(Resolver):
|
|||
|
||||
|
||||
class OctaveForgeResolver(Resolver):
|
||||
name = "octave-forge"
|
||||
|
||||
def __init__(self, session, user_local=False):
|
||||
self.session = session
|
||||
self.user_local = user_local
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return "octave-forge"
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.session)
|
||||
|
@ -289,8 +267,6 @@ class OctaveForgeResolver(Resolver):
|
|||
|
||||
|
||||
class CRANResolver(RResolver):
|
||||
name = "cran"
|
||||
|
||||
def __init__(self, session, user_local=False):
|
||||
super(CRANResolver, self).__init__(
|
||||
session, "http://cran.r-project.org", user_local=user_local
|
||||
|
@ -298,25 +274,19 @@ class CRANResolver(RResolver):
|
|||
|
||||
|
||||
class BioconductorResolver(RResolver):
|
||||
name = "bioconductor"
|
||||
|
||||
def __init__(self, session, user_local=False):
|
||||
super(BioconductorResolver, self).__init__(
|
||||
session, "https://hedgehog.fhcrc.org/bioconductor",
|
||||
user_local=user_local
|
||||
session, "https://hedgehog.fhcrc.org/bioconductor", user_local=user_local
|
||||
)
|
||||
|
||||
|
||||
class HackageResolver(Resolver):
|
||||
|
||||
name = "hackage"
|
||||
|
||||
def __init__(self, session, user_local=False):
|
||||
self.session = session
|
||||
self.user_local = user_local
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return "hackage"
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.session)
|
||||
|
@ -325,8 +295,7 @@ class HackageResolver(Resolver):
|
|||
extra_args = []
|
||||
if self.user_local:
|
||||
extra_args.append("--user")
|
||||
return (["cabal", "install"] + extra_args
|
||||
+ [req.package for req in reqs])
|
||||
return ["cabal", "install"] + extra_args + [req.package for req in reqs]
|
||||
|
||||
def install(self, requirements):
|
||||
from ..requirements import HaskellPackageRequirement
|
||||
|
@ -360,15 +329,12 @@ class HackageResolver(Resolver):
|
|||
|
||||
|
||||
class PypiResolver(Resolver):
|
||||
|
||||
name = "pypi"
|
||||
|
||||
def __init__(self, session, user_local=False):
|
||||
self.session = session
|
||||
self.user_local = user_local
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return "pypi"
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.session)
|
||||
|
@ -414,15 +380,12 @@ class PypiResolver(Resolver):
|
|||
|
||||
|
||||
class GoResolver(Resolver):
|
||||
|
||||
name = "go"
|
||||
|
||||
def __init__(self, session, user_local):
|
||||
self.session = session
|
||||
self.user_local = user_local
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return "go"
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.session)
|
||||
|
@ -463,26 +426,17 @@ NPM_COMMAND_PACKAGES = {
|
|||
"del-cli": "del-cli",
|
||||
"husky": "husky",
|
||||
"cross-env": "cross-env",
|
||||
"xo": "xo",
|
||||
"standard": "standard",
|
||||
"jshint": "jshint",
|
||||
"if-node-version": "if-node-version",
|
||||
"babel-cli": "babel",
|
||||
"c8": "c8",
|
||||
"prettier-standard": "prettier-standard",
|
||||
}
|
||||
|
||||
|
||||
class NpmResolver(Resolver):
|
||||
name = "npm"
|
||||
|
||||
def __init__(self, session, user_local=False):
|
||||
self.session = session
|
||||
self.user_local = user_local
|
||||
# TODO(jelmer): Handle user_local
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return "npm"
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%r)" % (type(self).__name__, self.session)
|
||||
|
@ -518,10 +472,7 @@ class NpmResolver(Resolver):
|
|||
if not isinstance(requirement, NodePackageRequirement):
|
||||
missing.append(requirement)
|
||||
continue
|
||||
cmd = ["npm", "install"]
|
||||
if not self.user_local:
|
||||
cmd.append('-g')
|
||||
cmd.append(requirement.package)
|
||||
cmd = ["npm", "-g", "install", requirement.package]
|
||||
logging.info("npm: running %r", cmd)
|
||||
run_detecting_problems(self.session, cmd, user=user)
|
||||
if missing:
|
||||
|
@ -578,7 +529,7 @@ class StackedResolver(Resolver):
|
|||
raise UnsatisfiedRequirements(requirements)
|
||||
|
||||
|
||||
NATIVE_RESOLVER_CLS: List[Type[Resolver]] = [
|
||||
NATIVE_RESOLVER_CLS = [
|
||||
CPANResolver,
|
||||
CTANResolver,
|
||||
PypiResolver,
|
||||
|
@ -592,70 +543,24 @@ NATIVE_RESOLVER_CLS: List[Type[Resolver]] = [
|
|||
|
||||
|
||||
def native_resolvers(session, user_local):
|
||||
return StackedResolver(
|
||||
[kls(session, user_local) for kls in NATIVE_RESOLVER_CLS])
|
||||
return StackedResolver([kls(session, user_local) for kls in NATIVE_RESOLVER_CLS])
|
||||
|
||||
|
||||
def select_resolvers(session, user_local, resolvers,
|
||||
dep_server_url=None) -> Optional[Resolver]:
|
||||
selected = []
|
||||
for resolver in resolvers:
|
||||
for kls in NATIVE_RESOLVER_CLS:
|
||||
if kls.name == resolver:
|
||||
selected.append(kls(session, user_local))
|
||||
break
|
||||
else:
|
||||
if resolver == 'native':
|
||||
selected.extend([
|
||||
kls(session, user_local) for kls in NATIVE_RESOLVER_CLS])
|
||||
elif resolver == 'apt':
|
||||
if user_local:
|
||||
raise NotImplementedError(
|
||||
'user local not supported for apt')
|
||||
if dep_server_url:
|
||||
from .dep_server import DepServerAptResolver
|
||||
selected.append(DepServerAptResolver.from_session(
|
||||
session, dep_server_url))
|
||||
else:
|
||||
from .apt import AptResolver
|
||||
selected.append(AptResolver.from_session(session))
|
||||
else:
|
||||
raise KeyError(resolver)
|
||||
if len(selected) == 0:
|
||||
return None
|
||||
if len(selected) == 1:
|
||||
return selected[0]
|
||||
return StackedResolver(selected)
|
||||
|
||||
|
||||
def auto_resolver(session: Session, explain: bool = False,
|
||||
system_wide: Optional[bool] = None,
|
||||
dep_server_url: Optional[str] = None):
|
||||
def auto_resolver(session, explain=False):
|
||||
# if session is SchrootSession or if we're root, use apt
|
||||
from .apt import AptResolver
|
||||
from ..session.schroot import SchrootSession
|
||||
from ..session import get_user
|
||||
|
||||
user = get_user(session)
|
||||
resolvers = []
|
||||
if system_wide is None:
|
||||
# TODO(jelmer): Check VIRTUAL_ENV, and prioritize PypiResolver if
|
||||
# present?
|
||||
if isinstance(session, SchrootSession) or user == "root" or explain:
|
||||
system_wide = True
|
||||
else:
|
||||
system_wide = False
|
||||
if system_wide:
|
||||
try:
|
||||
from .apt import AptResolver
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
else:
|
||||
if dep_server_url:
|
||||
from .dep_server import DepServerAptResolver
|
||||
resolvers.append(
|
||||
DepServerAptResolver.from_session(session, dep_server_url))
|
||||
else:
|
||||
resolvers.append(AptResolver.from_session(session))
|
||||
resolvers.extend([kls(session, not system_wide)
|
||||
for kls in NATIVE_RESOLVER_CLS])
|
||||
# TODO(jelmer): Check VIRTUAL_ENV, and prioritize PypiResolver if
|
||||
# present?
|
||||
if isinstance(session, SchrootSession) or user == "root" or explain:
|
||||
user_local = False
|
||||
else:
|
||||
user_local = True
|
||||
if not user_local:
|
||||
resolvers.append(AptResolver.from_session(session))
|
||||
resolvers.extend([kls(session, user_local) for kls in NATIVE_RESOLVER_CLS])
|
||||
return StackedResolver(resolvers)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,88 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
# Copyright (C) 2022 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from aiohttp import (
|
||||
ClientSession,
|
||||
ClientConnectorError,
|
||||
ClientResponseError,
|
||||
ServerDisconnectedError,
|
||||
)
|
||||
from yarl import URL
|
||||
|
||||
|
||||
from .. import Requirement, USER_AGENT
|
||||
from ..debian.apt import AptManager
|
||||
from .apt import AptRequirement, AptResolver
|
||||
|
||||
|
||||
class DepServerError(Exception):
|
||||
|
||||
def __init__(self, inner):
|
||||
self.inner = inner
|
||||
|
||||
|
||||
async def resolve_apt_requirement_dep_server(
|
||||
url: str, req: Requirement) -> List[AptRequirement]:
|
||||
"""Resolve a requirement to an APT requirement with a dep server.
|
||||
|
||||
Args:
|
||||
url: Dep server URL
|
||||
req: Requirement to resolve
|
||||
Returns:
|
||||
List of Apt requirements.
|
||||
"""
|
||||
async with ClientSession() as session:
|
||||
try:
|
||||
async with session.post(URL(url) / "resolve-apt", headers={
|
||||
'User-Agent': USER_AGENT},
|
||||
json={'requirement': req.json()},
|
||||
raise_for_status=True) as resp:
|
||||
return [
|
||||
AptRequirement._from_json(e) for e in await resp.json()]
|
||||
except (ClientConnectorError, ClientResponseError,
|
||||
ServerDisconnectedError) as e:
|
||||
logging.warning('Unable to contact dep server: %r', e)
|
||||
raise DepServerError(e)
|
||||
|
||||
|
||||
class DepServerAptResolver(AptResolver):
|
||||
def __init__(self, apt, dep_server_url, tie_breakers=None):
|
||||
super(DepServerAptResolver, self).__init__(
|
||||
apt, tie_breakers=tie_breakers)
|
||||
self.dep_server_url = dep_server_url
|
||||
|
||||
@classmethod
|
||||
def from_session(cls, session, dep_server_url, tie_breakers=None):
|
||||
return cls(
|
||||
AptManager.from_session(session), dep_server_url,
|
||||
tie_breakers=tie_breakers)
|
||||
|
||||
def resolve_all(self, req: Requirement):
|
||||
try:
|
||||
req.json()
|
||||
except NotImplementedError:
|
||||
return super(DepServerAptResolver, self).resolve_all(req)
|
||||
try:
|
||||
return asyncio.run(
|
||||
resolve_apt_requirement_dep_server(self.dep_server_url, req))
|
||||
except DepServerError:
|
||||
logging.warning('Falling back to resolving error locally')
|
||||
return super(DepServerAptResolver, self).resolve_all(req)
|
|
@ -69,14 +69,12 @@ class Session(object):
|
|||
raise NotImplementedError(self.check_output)
|
||||
|
||||
def Popen(
|
||||
self, argv, cwd: Optional[str] = None, user: Optional[str] = None,
|
||||
**kwargs
|
||||
self, argv, cwd: Optional[str] = None, user: Optional[str] = None, **kwargs
|
||||
):
|
||||
raise NotImplementedError(self.Popen)
|
||||
|
||||
def call(
|
||||
self, argv: List[str], cwd: Optional[str] = None,
|
||||
user: Optional[str] = None
|
||||
self, argv: List[str], cwd: Optional[str] = None, user: Optional[str] = None
|
||||
):
|
||||
raise NotImplementedError(self.call)
|
||||
|
||||
|
@ -102,26 +100,17 @@ class Session(object):
|
|||
def external_path(self, path: str) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def rmtree(self, path: str) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
is_temporary: bool
|
||||
|
||||
|
||||
class SessionSetupFailure(Exception):
|
||||
"""Session failed to be set up."""
|
||||
|
||||
def __init__(self, reason, errlines=None):
|
||||
self.reason = reason
|
||||
self.errlines = errlines
|
||||
|
||||
|
||||
def run_with_tee(session: Session,
|
||||
args: List[str], **kwargs) -> Tuple[int, List[str]]:
|
||||
def run_with_tee(session: Session, args: List[str], **kwargs):
|
||||
if "stdin" not in kwargs:
|
||||
kwargs["stdin"] = subprocess.DEVNULL
|
||||
p = session.Popen(
|
||||
args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
||||
p = session.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
||||
contents = []
|
||||
while p.poll() is None:
|
||||
line = p.stdout.readline()
|
||||
|
@ -132,8 +121,7 @@ def run_with_tee(session: Session,
|
|||
|
||||
|
||||
def get_user(session):
|
||||
return session.check_output(
|
||||
["sh", "-c", "echo $USER"], cwd="/").decode().strip()
|
||||
return session.check_output(["echo", "$USER"], cwd="/").decode().strip()
|
||||
|
||||
|
||||
def which(session, name):
|
||||
|
|
|
@ -20,7 +20,6 @@ from . import Session, NoSessionOpen, SessionAlreadyOpen
|
|||
|
||||
import contextlib
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Optional, Dict, List
|
||||
|
@ -73,8 +72,7 @@ class PlainSession(Session):
|
|||
close_fds: bool = True,
|
||||
):
|
||||
argv = self._prepend_user(user, argv)
|
||||
return subprocess.check_call(
|
||||
argv, cwd=cwd, env=env, close_fds=close_fds)
|
||||
return subprocess.check_call(argv, cwd=cwd, env=env, close_fds=close_fds)
|
||||
|
||||
def check_output(
|
||||
self,
|
||||
|
@ -86,19 +84,13 @@ class PlainSession(Session):
|
|||
argv = self._prepend_user(user, argv)
|
||||
return subprocess.check_output(argv, cwd=cwd, env=env)
|
||||
|
||||
def Popen(
|
||||
self, args, stdout=None, stderr=None, stdin=None, user=None,
|
||||
cwd=None, env=None):
|
||||
def Popen(self, args, stdout=None, stderr=None, stdin=None, user=None, cwd=None, env=None):
|
||||
args = self._prepend_user(user, args)
|
||||
return subprocess.Popen(
|
||||
args, stdout=stdout, stderr=stderr, stdin=stdin, cwd=cwd, env=env)
|
||||
return subprocess.Popen(args, stdout=stdout, stderr=stderr, stdin=stdin, cwd=cwd, env=env)
|
||||
|
||||
def exists(self, path):
|
||||
return os.path.exists(path)
|
||||
|
||||
def rmtree(self, path):
|
||||
return shutil.rmtree(path)
|
||||
|
||||
def scandir(self, path):
|
||||
return os.scandir(path)
|
||||
|
||||
|
|
|
@ -66,38 +66,25 @@ class SchrootSession(Session):
|
|||
if line.startswith(b"E: "):
|
||||
logging.error("%s", line[3:].decode(errors="replace"))
|
||||
logging.warning(
|
||||
"Failed to close schroot session %s, leaving stray.",
|
||||
self.session_id
|
||||
"Failed to close schroot session %s, leaving stray.", self.session_id
|
||||
)
|
||||
self.session_id = None
|
||||
return False
|
||||
self.session_id = None
|
||||
self._location = None
|
||||
return True
|
||||
|
||||
def __enter__(self) -> "Session":
|
||||
if self.session_id is not None:
|
||||
raise SessionAlreadyOpen(self)
|
||||
stderr = tempfile.TemporaryFile()
|
||||
try:
|
||||
self.session_id = (
|
||||
subprocess.check_output(
|
||||
["schroot", "-c", self.chroot, "-b"], stderr=stderr)
|
||||
subprocess.check_output(["schroot", "-c", self.chroot, "-b"])
|
||||
.strip()
|
||||
.decode()
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
stderr.seek(0)
|
||||
errlines = stderr.readlines()
|
||||
if len(errlines) == 1:
|
||||
raise SessionSetupFailure(
|
||||
errlines[0].rstrip().decode(), errlines=errlines)
|
||||
elif len(errlines) == 0:
|
||||
raise SessionSetupFailure(
|
||||
"No output from schroot", errlines=errlines)
|
||||
else:
|
||||
raise SessionSetupFailure(
|
||||
errlines[-1].decode(), errlines=errlines)
|
||||
# TODO(jelmer): Capture stderr and forward in SessionSetupFailure
|
||||
raise SessionSetupFailure()
|
||||
logging.info(
|
||||
"Opened schroot session %s (from %s)", self.session_id, self.chroot
|
||||
)
|
||||
|
@ -169,28 +156,24 @@ class SchrootSession(Session):
|
|||
env: Optional[Dict[str, str]] = None,
|
||||
) -> bytes:
|
||||
try:
|
||||
return subprocess.check_output(
|
||||
self._run_argv(argv, cwd, user, env=env))
|
||||
return subprocess.check_output(self._run_argv(argv, cwd, user, env=env))
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise subprocess.CalledProcessError(e.returncode, argv)
|
||||
|
||||
def Popen(
|
||||
self, argv, cwd: Optional[str] = None, user: Optional[str] = None,
|
||||
**kwargs
|
||||
self, argv, cwd: Optional[str] = None, user: Optional[str] = None, **kwargs
|
||||
):
|
||||
return subprocess.Popen(self._run_argv(argv, cwd, user), **kwargs)
|
||||
|
||||
def call(
|
||||
self, argv: List[str], cwd: Optional[str] = None,
|
||||
user: Optional[str] = None
|
||||
self, argv: List[str], cwd: Optional[str] = None, user: Optional[str] = None
|
||||
):
|
||||
return subprocess.call(self._run_argv(argv, cwd, user))
|
||||
|
||||
def create_home(self) -> None:
|
||||
"""Create the user's home directory."""
|
||||
home = (
|
||||
self.check_output(
|
||||
["sh", "-c", "echo $HOME"], cwd="/").decode().rstrip("\n")
|
||||
self.check_output(["sh", "-c", "echo $HOME"], cwd="/").decode().rstrip("\n")
|
||||
)
|
||||
user = (
|
||||
self.check_output(["sh", "-c", "echo $LOGNAME"], cwd="/")
|
||||
|
@ -206,8 +189,7 @@ class SchrootSession(Session):
|
|||
return os.path.join(self.location, path.lstrip("/"))
|
||||
if self._cwd is None:
|
||||
raise ValueError("no cwd set")
|
||||
return os.path.join(
|
||||
self.location, os.path.join(self._cwd, path).lstrip("/"))
|
||||
return os.path.join(self.location, os.path.join(self._cwd, path).lstrip("/"))
|
||||
|
||||
def exists(self, path: str) -> bool:
|
||||
fullpath = self.external_path(path)
|
||||
|
@ -221,17 +203,13 @@ class SchrootSession(Session):
|
|||
fullpath = self.external_path(path)
|
||||
return os.mkdir(fullpath)
|
||||
|
||||
def rmtree(self, path: str):
|
||||
import shutil
|
||||
fullpath = self.external_path(path)
|
||||
return shutil.rmtree(fullpath)
|
||||
|
||||
def setup_from_vcs(
|
||||
self, tree, include_controldir: Optional[bool] = None, subdir="package"
|
||||
):
|
||||
from ..vcs import dupe_vcs_tree, export_vcs_tree
|
||||
|
||||
build_dir = os.path.join(self.location, "build")
|
||||
|
||||
directory = tempfile.mkdtemp(dir=build_dir)
|
||||
reldir = "/" + os.path.relpath(directory, self.location)
|
||||
|
||||
|
@ -250,7 +228,7 @@ class SchrootSession(Session):
|
|||
directory = tempfile.mkdtemp(dir=build_dir)
|
||||
reldir = "/" + os.path.relpath(directory, self.location)
|
||||
export_directory = os.path.join(directory, subdir)
|
||||
shutil.copytree(path, export_directory, symlinks=True)
|
||||
shutil.copytree(path, export_directory, dirs_exist_ok=True)
|
||||
return export_directory, os.path.join(reldir, subdir)
|
||||
|
||||
is_temporary = True
|
||||
|
|
|
@ -15,25 +15,16 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from functools import partial
|
||||
|
||||
from .buildsystem import NoBuildToolsFound
|
||||
from .fix_build import iterate_with_build_fixers
|
||||
from .logs import NoLogManager
|
||||
|
||||
|
||||
def run_test(session, buildsystems, resolver, fixers, log_manager=None):
|
||||
def run_test(session, buildsystems, resolver, fixers):
|
||||
# Some things want to write to the user's home directory,
|
||||
# e.g. pip caches in ~/.cache
|
||||
session.create_home()
|
||||
|
||||
if log_manager is None:
|
||||
log_manager = NoLogManager()
|
||||
|
||||
for buildsystem in buildsystems:
|
||||
iterate_with_build_fixers(
|
||||
fixers, log_manager.wrap(
|
||||
partial(buildsystem.test, session, resolver)))
|
||||
buildsystem.test(session, resolver, fixers)
|
||||
return
|
||||
|
||||
raise NoBuildToolsFound()
|
||||
|
|
|
@ -23,13 +23,10 @@ import unittest
|
|||
|
||||
def test_suite():
|
||||
names = [
|
||||
'buildlog',
|
||||
'logs',
|
||||
"debian_build",
|
||||
]
|
||||
if os.path.exists("/usr/bin/dpkg-architecture"):
|
||||
names.append("debian_build")
|
||||
names.append("debian_fix_build")
|
||||
names.append("resolver_apt")
|
||||
module_names = ["tests.test_" + name for name in names]
|
||||
module_names = ["ognibuild.tests.test_" + name for name in names]
|
||||
loader = unittest.TestLoader()
|
||||
return loader.loadTestsFromNames(module_names)
|
|
@ -17,17 +17,8 @@
|
|||
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
|
||||
from debian.changelog import Version
|
||||
|
||||
from ognibuild.debian.build import (
|
||||
add_dummy_changelog_entry,
|
||||
get_build_architecture,
|
||||
version_add_suffix,
|
||||
_builddeb_command,
|
||||
DEFAULT_BUILDER,
|
||||
)
|
||||
from ..debian.build import add_dummy_changelog_entry, get_build_architecture
|
||||
|
||||
from breezy.tests import TestCaseWithTransport, TestCase
|
||||
|
||||
|
@ -159,43 +150,3 @@ class BuildArchitectureTests(TestCase):
|
|||
|
||||
def test_is_str(self):
|
||||
self.assertIsInstance(get_build_architecture(), str)
|
||||
|
||||
|
||||
class VersionAddSuffixTests(TestCase):
|
||||
|
||||
def test_native(self):
|
||||
self.assertEqual(
|
||||
Version('1.0~jan+lint4'),
|
||||
version_add_suffix(Version('1.0~jan+lint3'), '~jan+lint'))
|
||||
self.assertEqual(
|
||||
Version('1.0~jan+lint1'),
|
||||
version_add_suffix(Version('1.0'), '~jan+lint'))
|
||||
|
||||
def test_normal(self):
|
||||
self.assertEqual(
|
||||
Version('1.0-1~jan+lint4'),
|
||||
version_add_suffix(Version('1.0-1~jan+lint3'), '~jan+lint'))
|
||||
self.assertEqual(
|
||||
Version('1.0-1~jan+lint1'),
|
||||
version_add_suffix(Version('1.0-1'), '~jan+lint'))
|
||||
self.assertEqual(
|
||||
Version('0.0.12-1~jan+lint1'),
|
||||
version_add_suffix(Version('0.0.12-1'), '~jan+lint'))
|
||||
self.assertEqual(
|
||||
Version('0.0.12-1~jan+unchanged1~jan+lint1'),
|
||||
version_add_suffix(
|
||||
Version('0.0.12-1~jan+unchanged1'), '~jan+lint'))
|
||||
|
||||
|
||||
class BuilddebCommandTests(TestCase):
|
||||
|
||||
def test_simple(self):
|
||||
self.assertEqual(
|
||||
[sys.executable, "-m", "breezy", "builddeb",
|
||||
"--guess-upstream-branch-url", "--builder=" + DEFAULT_BUILDER],
|
||||
_builddeb_command())
|
||||
self.assertEqual(
|
||||
[sys.executable, "-m", "breezy", "builddeb",
|
||||
"--guess-upstream-branch-url", "--builder=" + DEFAULT_BUILDER,
|
||||
"--result-dir=/tmp/blah"],
|
||||
_builddeb_command(result_dir="/tmp/blah"))
|
|
@ -29,15 +29,13 @@ from buildlog_consultant.common import (
|
|||
MissingRubyGem,
|
||||
MissingValaPackage,
|
||||
)
|
||||
from ognibuild.debian.apt import AptManager, FileSearcher
|
||||
from ognibuild.debian.fix_build import (
|
||||
from ..debian.apt import AptManager, FileSearcher
|
||||
from ..debian.fix_build import (
|
||||
resolve_error,
|
||||
versioned_package_fixers,
|
||||
apt_fixers,
|
||||
DebianPackagingContext,
|
||||
add_build_dependency,
|
||||
)
|
||||
from ognibuild.resolver.apt import AptRequirement
|
||||
from breezy.commit import NullCommitReporter
|
||||
from breezy.tests import TestCaseWithTransport
|
||||
|
||||
|
@ -46,7 +44,7 @@ class DummyAptSearcher(FileSearcher):
|
|||
def __init__(self, files):
|
||||
self._apt_files = files
|
||||
|
||||
async def search_files(self, path, regex=False, case_insensitive=False):
|
||||
def search_files(self, path, regex=False, case_insensitive=False):
|
||||
for p, pkg in sorted(self._apt_files.items()):
|
||||
if case_insensitive:
|
||||
flags = re.I
|
||||
|
@ -99,7 +97,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
self._apt_files = {}
|
||||
|
||||
def resolve(self, error, context=("build",)):
|
||||
from ognibuild.session.plain import PlainSession
|
||||
from ..session.plain import PlainSession
|
||||
|
||||
session = PlainSession()
|
||||
apt = AptManager(session)
|
||||
|
@ -111,8 +109,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
update_changelog=True,
|
||||
commit_reporter=NullCommitReporter(),
|
||||
)
|
||||
fixers = versioned_package_fixers(
|
||||
session, context, apt) + apt_fixers(apt, context)
|
||||
fixers = versioned_package_fixers(session, context, apt) + apt_fixers(apt, context)
|
||||
return resolve_error(error, ("build",), fixers)
|
||||
|
||||
def get_build_deps(self):
|
||||
|
@ -121,8 +118,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
|
||||
def test_missing_command_unknown(self):
|
||||
self._apt_files = {}
|
||||
self.assertFalse(self.resolve(
|
||||
MissingCommand("acommandthatdoesnotexist")))
|
||||
self.assertFalse(self.resolve(MissingCommand("acommandthatdoesnotexist")))
|
||||
|
||||
def test_missing_command_brz(self):
|
||||
self._apt_files = {
|
||||
|
@ -134,8 +130,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
self.overrideEnv("DEBFULLNAME", "Jelmer Vernooij")
|
||||
self.assertTrue(self.resolve(MissingCommand("brz")))
|
||||
self.assertEqual("libc6, brz", self.get_build_deps())
|
||||
rev = self.tree.branch.repository.get_revision(
|
||||
self.tree.branch.last_revision())
|
||||
rev = self.tree.branch.repository.get_revision(self.tree.branch.last_revision())
|
||||
self.assertEqual("Add missing build dependency on brz.\n", rev.message)
|
||||
self.assertFalse(self.resolve(MissingCommand("brz")))
|
||||
self.assertEqual("libc6, brz", self.get_build_deps())
|
||||
|
@ -158,12 +153,10 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
def test_missing_ruby_file_from_gem(self):
|
||||
self._apt_files = {
|
||||
"/usr/share/rubygems-integration/all/gems/activesupport-"
|
||||
"5.2.3/lib/active_support/core_ext/string/strip.rb":
|
||||
"ruby-activesupport"
|
||||
"5.2.3/lib/active_support/core_ext/string/strip.rb": "ruby-activesupport"
|
||||
}
|
||||
self.assertTrue(
|
||||
self.resolve(MissingRubyFile(
|
||||
"active_support/core_ext/string/strip"))
|
||||
self.resolve(MissingRubyFile("active_support/core_ext/string/strip"))
|
||||
)
|
||||
self.assertEqual("libc6, ruby-activesupport", self.get_build_deps())
|
||||
|
||||
|
@ -180,8 +173,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
self.assertEqual("libc6, ruby-bio (>= 2.0.3)", self.get_build_deps())
|
||||
|
||||
def test_missing_perl_module(self):
|
||||
self._apt_files = {
|
||||
"/usr/share/perl5/App/cpanminus/fatscript.pm": "cpanminus"}
|
||||
self._apt_files = {"/usr/share/perl5/App/cpanminus/fatscript.pm": "cpanminus"}
|
||||
self.assertTrue(
|
||||
self.resolve(
|
||||
MissingPerlModule(
|
||||
|
@ -208,34 +200,28 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
|
||||
def test_missing_pkg_config(self):
|
||||
self._apt_files = {
|
||||
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc":
|
||||
"libxcb-xfixes0-dev"
|
||||
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc": "libxcb-xfixes0-dev"
|
||||
}
|
||||
self.assertTrue(self.resolve(MissingPkgConfig("xcb-xfixes")))
|
||||
self.assertEqual("libc6, libxcb-xfixes0-dev", self.get_build_deps())
|
||||
|
||||
def test_missing_pkg_config_versioned(self):
|
||||
self._apt_files = {
|
||||
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc":
|
||||
"libxcb-xfixes0-dev"
|
||||
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc": "libxcb-xfixes0-dev"
|
||||
}
|
||||
self.assertTrue(self.resolve(MissingPkgConfig("xcb-xfixes", "1.0")))
|
||||
self.assertEqual(
|
||||
"libc6, libxcb-xfixes0-dev (>= 1.0)", self.get_build_deps())
|
||||
self.assertEqual("libc6, libxcb-xfixes0-dev (>= 1.0)", self.get_build_deps())
|
||||
|
||||
def test_missing_python_module(self):
|
||||
self._apt_files = {
|
||||
"/usr/lib/python3/dist-packages/m2r.py": "python3-m2r"}
|
||||
self._apt_files = {"/usr/lib/python3/dist-packages/m2r.py": "python3-m2r"}
|
||||
self.assertTrue(self.resolve(MissingPythonModule("m2r")))
|
||||
self.assertEqual("libc6, python3-m2r", self.get_build_deps())
|
||||
|
||||
def test_missing_go_package(self):
|
||||
self._apt_files = {
|
||||
"/usr/share/gocode/src/github.com/chzyer/readline/utils_test.go":
|
||||
"golang-github-chzyer-readline-dev",
|
||||
"/usr/share/gocode/src/github.com/chzyer/readline/utils_test.go": "golang-github-chzyer-readline-dev",
|
||||
}
|
||||
self.assertTrue(self.resolve(
|
||||
MissingGoPackage("github.com/chzyer/readline")))
|
||||
self.assertTrue(self.resolve(MissingGoPackage("github.com/chzyer/readline")))
|
||||
self.assertEqual(
|
||||
"libc6, golang-github-chzyer-readline-dev", self.get_build_deps()
|
||||
)
|
||||
|
@ -246,63 +232,3 @@ blah (0.1) UNRELEASED; urgency=medium
|
|||
}
|
||||
self.assertTrue(self.resolve(MissingValaPackage("posix")))
|
||||
self.assertEqual("libc6, valac-0.48-vapi", self.get_build_deps())
|
||||
|
||||
|
||||
class AddBuildDependencyTests(TestCaseWithTransport):
|
||||
|
||||
def setUp(self):
|
||||
super(AddBuildDependencyTests, self).setUp()
|
||||
self.tree = self.make_branch_and_tree(".")
|
||||
self.build_tree_contents(
|
||||
[
|
||||
("debian/",),
|
||||
(
|
||||
"debian/control",
|
||||
"""\
|
||||
Source: blah
|
||||
Build-Depends: libc6
|
||||
|
||||
Package: python-blah
|
||||
Depends: ${python3:Depends}
|
||||
Description: A python package
|
||||
Foo
|
||||
""",
|
||||
),
|
||||
(
|
||||
"debian/changelog",
|
||||
"""\
|
||||
blah (0.1) UNRELEASED; urgency=medium
|
||||
|
||||
* Initial release. (Closes: #XXXXXX)
|
||||
|
||||
-- Jelmer Vernooij <jelmer@debian.org> Sat, 04 Apr 2020 14:12:13 +0000
|
||||
""",
|
||||
),
|
||||
]
|
||||
)
|
||||
self.tree.add(["debian", "debian/control", "debian/changelog"])
|
||||
self.tree.commit("Initial commit")
|
||||
self.context = DebianPackagingContext(
|
||||
self.tree,
|
||||
subpath="",
|
||||
committer="ognibuild <ognibuild@jelmer.uk>",
|
||||
update_changelog=True,
|
||||
commit_reporter=NullCommitReporter(),
|
||||
)
|
||||
|
||||
def test_already_present(self):
|
||||
requirement = AptRequirement.simple('libc6')
|
||||
self.assertFalse(add_build_dependency(self.context, requirement))
|
||||
|
||||
def test_basic(self):
|
||||
requirement = AptRequirement.simple('foo')
|
||||
self.assertTrue(add_build_dependency(self.context, requirement))
|
||||
self.assertFileEqual("""\
|
||||
Source: blah
|
||||
Build-Depends: libc6, foo
|
||||
|
||||
Package: python-blah
|
||||
Depends: ${python3:Depends}
|
||||
Description: A python package
|
||||
Foo
|
||||
""", 'debian/control')
|
|
@ -1,253 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
# Copyright (C) 2020-2021 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Any
|
||||
from debian.changelog import Version
|
||||
import logging
|
||||
import re
|
||||
|
||||
from . import Requirement
|
||||
from .requirements import (
|
||||
CargoCrateRequirement,
|
||||
GoPackageRequirement,
|
||||
PythonPackageRequirement,
|
||||
)
|
||||
from .resolver.apt import AptRequirement, OneOfRequirement
|
||||
|
||||
|
||||
@dataclass
|
||||
class UpstreamInfo:
|
||||
name: Optional[str]
|
||||
buildsystem: Optional[str] = None
|
||||
branch_url: Optional[str] = None
|
||||
branch_subpath: Optional[str] = None
|
||||
tarball_url: Optional[str] = None
|
||||
version: Optional[str] = None
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def json(self):
|
||||
return {
|
||||
'name': self.name,
|
||||
'buildsystem': self.buildsystem,
|
||||
'branch_url': self.branch_url,
|
||||
'branch_subpath': self.branch_subpath,
|
||||
'tarball_url': self.tarball_url,
|
||||
'version': self.version
|
||||
}
|
||||
|
||||
|
||||
def go_base_name(package):
|
||||
(hostname, path) = package.split('/', 1)
|
||||
if hostname == "github.com":
|
||||
hostname = "github"
|
||||
if hostname == "gopkg.in":
|
||||
hostname = "gopkg"
|
||||
path = path.rstrip('/').replace("/", "-")
|
||||
if path.endswith('.git'):
|
||||
path = path[:-4]
|
||||
return (hostname + path).replace("_", "-").lower()
|
||||
|
||||
|
||||
def load_crate_info(crate):
|
||||
import urllib.error
|
||||
from urllib.request import urlopen, Request
|
||||
import json
|
||||
http_url = 'https://crates.io/api/v1/crates/%s' % crate
|
||||
headers = {'User-Agent': 'debianize', 'Accept': 'application/json'}
|
||||
http_contents = urlopen(Request(http_url, headers=headers)).read()
|
||||
try:
|
||||
return json.loads(http_contents)
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
logging.warning('No crate %r', crate)
|
||||
return None
|
||||
raise
|
||||
|
||||
|
||||
def find_python_package_upstream(requirement):
|
||||
import urllib.error
|
||||
from urllib.request import urlopen, Request
|
||||
import json
|
||||
http_url = 'https://pypi.org/pypi/%s/json' % requirement.package
|
||||
headers = {'User-Agent': 'ognibuild', 'Accept': 'application/json'}
|
||||
try:
|
||||
http_contents = urlopen(
|
||||
Request(http_url, headers=headers)).read()
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
logging.warning('No pypi project %r', requirement.package)
|
||||
return None
|
||||
raise
|
||||
pypi_data = json.loads(http_contents)
|
||||
upstream_branch = None
|
||||
for name, url in pypi_data['info']['project_urls'].items():
|
||||
if name.lower() in ('github', 'repository'):
|
||||
upstream_branch = url
|
||||
tarball_url = None
|
||||
for url_data in pypi_data['urls']:
|
||||
if url_data.get('package_type') == 'sdist':
|
||||
tarball_url = url_data['url']
|
||||
return UpstreamInfo(
|
||||
branch_url=upstream_branch, branch_subpath='',
|
||||
name='python-%s' % pypi_data['info']['name'],
|
||||
tarball_url=tarball_url)
|
||||
|
||||
|
||||
def find_go_package_upstream(requirement):
|
||||
if requirement.package.startswith('github.com/'):
|
||||
return UpstreamInfo(
|
||||
name='golang-%s' % go_base_name(requirement.package),
|
||||
branch_url='https://%s' % '/'.join(
|
||||
requirement.package.split('/')[:3]),
|
||||
branch_subpath='')
|
||||
|
||||
|
||||
def find_cargo_crate_upstream(requirement):
|
||||
import semver
|
||||
from debmutate.debcargo import semver_pair
|
||||
data = load_crate_info(requirement.crate)
|
||||
if data is None:
|
||||
return None
|
||||
upstream_branch = data['crate']['repository']
|
||||
name = 'rust-' + data['crate']['name'].replace('_', '-')
|
||||
version = None
|
||||
if requirement.api_version is not None:
|
||||
for version_info in data['versions']:
|
||||
if (not version_info['num'].startswith(
|
||||
requirement.api_version + '.')
|
||||
and not version_info['num'] == requirement.api_version):
|
||||
continue
|
||||
if version is None:
|
||||
version = semver.VersionInfo.parse(version_info['num'])
|
||||
else:
|
||||
version = semver.max_ver(
|
||||
version, semver.VersionInfo.parse(version_info['num']))
|
||||
if version is None:
|
||||
logging.warning(
|
||||
'Unable to find version of crate %s '
|
||||
'that matches API version %s',
|
||||
name, requirement.api_version)
|
||||
else:
|
||||
name += '-' + semver_pair(str(version))
|
||||
return UpstreamInfo(
|
||||
branch_url=upstream_branch, branch_subpath=None,
|
||||
name=name, version=str(version) if version else None,
|
||||
metadata={'X-Cargo-Crate': data['crate']['name']},
|
||||
buildsystem='cargo')
|
||||
|
||||
|
||||
def apt_to_cargo_requirement(m, rels):
|
||||
name = m.group(1)
|
||||
api_version = m.group(2)
|
||||
if m.group(3):
|
||||
features = set(m.group(3)[1:].split('-'))
|
||||
else:
|
||||
features = set()
|
||||
if not rels:
|
||||
minimum_version = None
|
||||
elif len(rels) == 1 and rels[0][0] == '>=':
|
||||
minimum_version = Version(rels[0][1]).upstream_version
|
||||
else:
|
||||
logging.warning('Unable to parse Debian version %r', rels)
|
||||
minimum_version = None
|
||||
|
||||
return CargoCrateRequirement(
|
||||
name, api_version=api_version,
|
||||
features=features, minimum_version=minimum_version)
|
||||
|
||||
|
||||
def apt_to_python_requirement(m, rels):
|
||||
name = m.group(2)
|
||||
python_version = m.group(1)
|
||||
if not rels:
|
||||
minimum_version = None
|
||||
elif len(rels) == 1 and rels[0][0] == '>=':
|
||||
minimum_version = Version(rels[0][1]).upstream_version
|
||||
else:
|
||||
logging.warning('Unable to parse Debian version %r', rels)
|
||||
minimum_version = None
|
||||
return PythonPackageRequirement(
|
||||
name, python_version=(python_version or None),
|
||||
minimum_version=minimum_version)
|
||||
|
||||
|
||||
def apt_to_go_requirement(m, rels):
|
||||
parts = m.group(1).split('-')
|
||||
if parts[0] == 'github':
|
||||
parts[0] = 'github.com'
|
||||
if parts[0] == 'gopkg':
|
||||
parts[0] = 'gopkg.in'
|
||||
if not rels:
|
||||
version = None
|
||||
elif len(rels) == 1 and rels[0][0] == '=':
|
||||
version = Version(rels[0][1]).upstream_version
|
||||
else:
|
||||
logging.warning('Unable to parse Debian version %r', rels)
|
||||
version = None
|
||||
return GoPackageRequirement('/'.join(parts), version=version)
|
||||
|
||||
|
||||
BINARY_PACKAGE_UPSTREAM_MATCHERS = [
|
||||
(r'librust-(.*)-([^-+]+)(\+.*?)-dev', apt_to_cargo_requirement),
|
||||
(r'python([0-9.]*)-(.*)', apt_to_python_requirement),
|
||||
(r'golang-(.*)-dev', apt_to_go_requirement),
|
||||
]
|
||||
|
||||
|
||||
_BINARY_PACKAGE_UPSTREAM_MATCHERS = [
|
||||
(re.compile(r), fn) for (r, fn) in BINARY_PACKAGE_UPSTREAM_MATCHERS]
|
||||
|
||||
|
||||
def find_apt_upstream(requirement: AptRequirement) -> Optional[UpstreamInfo]:
|
||||
for option in requirement.relations:
|
||||
for rel in option:
|
||||
for matcher, fn in _BINARY_PACKAGE_UPSTREAM_MATCHERS:
|
||||
m = matcher.fullmatch(rel['name'])
|
||||
if m:
|
||||
upstream_requirement = fn(
|
||||
m, [rel['version']] if rel['version'] else [])
|
||||
return find_upstream(upstream_requirement)
|
||||
|
||||
logging.warning(
|
||||
'Unable to map binary package name %s to upstream',
|
||||
rel['name'])
|
||||
return None
|
||||
|
||||
|
||||
def find_or_upstream(requirement: OneOfRequirement) -> Optional[UpstreamInfo]:
|
||||
for req in requirement.elements:
|
||||
info = find_upstream(req)
|
||||
if info is not None:
|
||||
return info
|
||||
return None
|
||||
|
||||
|
||||
UPSTREAM_FINDER = {
|
||||
'python-package': find_python_package_upstream,
|
||||
'go-package': find_go_package_upstream,
|
||||
'cargo-crate': find_cargo_crate_upstream,
|
||||
'apt': find_apt_upstream,
|
||||
'or': find_or_upstream,
|
||||
}
|
||||
|
||||
|
||||
def find_upstream(requirement: Requirement) -> Optional[UpstreamInfo]:
|
||||
try:
|
||||
return UPSTREAM_FINDER[requirement.family](requirement)
|
||||
except KeyError:
|
||||
return None
|
|
@ -43,8 +43,7 @@ def dupe_vcs_tree(tree, directory):
|
|||
tree = tree.basis_tree()
|
||||
try:
|
||||
result = tree._repository.controldir.sprout(
|
||||
directory, create_tree_if_local=True,
|
||||
revision_id=tree.get_revision_id()
|
||||
directory, create_tree_if_local=True, revision_id=tree.get_revision_id()
|
||||
)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOSPC:
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
14
releaser.conf
Normal file
14
releaser.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
name: "ognibuild"
|
||||
timeout_days: 5
|
||||
tag_name: "v$VERSION"
|
||||
verify_command: "python3 setup.py test"
|
||||
update_version {
|
||||
path: "setup.py"
|
||||
match: "^ version=\"(.*)\",$"
|
||||
new_line: " version=\"$VERSION\","
|
||||
}
|
||||
update_version {
|
||||
path: "ognibuild/__init__.py"
|
||||
match: "^__version__ = \\((.*)\\)$"
|
||||
new_line: "__version__ = $TUPLED_VERSION"
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import argparse
|
||||
from contextlib import ExitStack
|
||||
import logging
|
||||
import sys
|
||||
from typing import Dict, List
|
||||
|
||||
from ognibuild.buildsystem import NoBuildToolsFound, detect_buildsystems
|
||||
from ognibuild.requirements import Requirement
|
||||
from ognibuild.resolver.apt import AptResolver
|
||||
from ognibuild.session.plain import PlainSession
|
||||
|
||||
parser = argparse.ArgumentParser('report-apt-deps-status')
|
||||
parser.add_argument('directory', type=str, default='.', nargs='?')
|
||||
parser.add_argument(
|
||||
'--detailed', action='store_true', help='Show detailed analysis')
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(format='%(message)s', level=logging.INFO)
|
||||
|
||||
session = PlainSession()
|
||||
with ExitStack() as es:
|
||||
es.enter_context(session)
|
||||
session.chdir(args.directory)
|
||||
resolver = AptResolver.from_session(session)
|
||||
|
||||
try:
|
||||
bss = list(detect_buildsystems(args.directory))
|
||||
except NoBuildToolsFound:
|
||||
logging.fatal('No build tools found')
|
||||
sys.exit(1)
|
||||
logging.debug("Detected buildsystems: %s", ", ".join(map(str, bss)))
|
||||
deps: Dict[str, List[Requirement]] = {}
|
||||
for buildsystem in bss:
|
||||
try:
|
||||
declared_reqs = buildsystem.get_declared_dependencies(session, [])
|
||||
for stage, req in declared_reqs:
|
||||
deps.setdefault(stage, []).append(req)
|
||||
except NotImplementedError:
|
||||
logging.warning(
|
||||
'Unable to get dependencies from buildsystem %r, skipping',
|
||||
buildsystem)
|
||||
continue
|
||||
|
||||
if args.detailed:
|
||||
for stage, reqs in deps.items():
|
||||
logging.info("Stage: %s", stage)
|
||||
for req in reqs:
|
||||
apt_req = resolver.resolve(req)
|
||||
logging.info("%s: %s", req, apt_req.pkg_relation_str())
|
||||
logging.info('')
|
||||
else:
|
||||
build_depends = []
|
||||
test_depends = []
|
||||
run_depends = []
|
||||
unresolved = []
|
||||
for stage, reqs in deps.items():
|
||||
for req in reqs:
|
||||
apt_req = resolver.resolve(req)
|
||||
if apt_req is None:
|
||||
unresolved.append(req)
|
||||
elif stage == 'core':
|
||||
build_depends.append(apt_req)
|
||||
run_depends.append(apt_req)
|
||||
elif stage == 'build':
|
||||
build_depends.append(apt_req)
|
||||
elif stage == 'test':
|
||||
test_depends.append(apt_req)
|
||||
else:
|
||||
raise NotImplementedError('stage %s not supported' % stage)
|
||||
if build_depends:
|
||||
logging.info(
|
||||
'Build-Depends: %s',
|
||||
', '.join([d.pkg_relation_str() for d in build_depends]))
|
||||
if test_depends:
|
||||
logging.info(
|
||||
'Test-Depends: %s',
|
||||
', '.join([d.pkg_relation_str() for d in test_depends]))
|
||||
if run_depends:
|
||||
logging.info(
|
||||
'Depends: %s',
|
||||
', '.join([d.pkg_relation_str() for d in run_depends]))
|
||||
if unresolved:
|
||||
sys.stdout.write('\n')
|
||||
logging.warning(
|
||||
'Unable to find apt packages for the following dependencies:')
|
||||
for req in unresolved:
|
||||
logging.warning('* %s', req)
|
62
setup.cfg
62
setup.cfg
|
@ -1,65 +1,13 @@
|
|||
[metadata]
|
||||
name = ognibuild
|
||||
description = Detect and run any build system
|
||||
version = attr:ognibuild.__version__
|
||||
maintainer = Jelmer Vernooij
|
||||
maintainer_email = jelmer@jelmer.uk
|
||||
license = GNU GPLv2 or later
|
||||
url = https://jelmer.uk/code/ognibuild
|
||||
classifiers =
|
||||
Development Status :: 4 - Beta
|
||||
License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
Operating System :: POSIX
|
||||
|
||||
[options]
|
||||
packages =
|
||||
ognibuild
|
||||
ognibuild.debian
|
||||
ognibuild.resolver
|
||||
ognibuild.session
|
||||
scripts = scripts/report-apt-deps-status
|
||||
install_requires =
|
||||
breezy>=3.2
|
||||
buildlog-consultant>=0.0.21
|
||||
requirements-parser
|
||||
toml
|
||||
setuptools
|
||||
ruamel.yaml
|
||||
tests_require =
|
||||
testtools
|
||||
types-toml
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
ogni=ognibuild.__main__:main
|
||||
deb-fix-build=ognibuild.debian.fix_build:main
|
||||
|
||||
[options.extras_require]
|
||||
dev =
|
||||
testtools
|
||||
debian =
|
||||
debmutate
|
||||
python_debian
|
||||
python_apt
|
||||
brz-debian
|
||||
lz4
|
||||
remote =
|
||||
breezy
|
||||
dulwich
|
||||
dep_server =
|
||||
aiohttp
|
||||
aiohttp-openmetrics
|
||||
gcp = google-cloud-logging
|
||||
|
||||
[flake8]
|
||||
banned-modules = silver-platter = Should not use silver-platter
|
||||
exclude = build,.eggs/
|
||||
|
||||
[mypy]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
|
||||
|
|
41
setup.py
41
setup.py
|
@ -1,3 +1,40 @@
|
|||
#!/usr/bin/python3
|
||||
#!/usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
|
||||
from setuptools import setup
|
||||
setup()
|
||||
|
||||
|
||||
setup(name="ognibuild",
|
||||
description="Detect and run any build system",
|
||||
version="0.0.7",
|
||||
maintainer="Jelmer Vernooij",
|
||||
maintainer_email="jelmer@jelmer.uk",
|
||||
license="GNU GPLv2 or later",
|
||||
url="https://jelmer.uk/code/ognibuild",
|
||||
packages=['ognibuild', 'ognibuild.tests', 'ognibuild.debian', 'ognibuild.resolver', 'ognibuild.session'],
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'License :: OSI Approved :: '
|
||||
'GNU General Public License v2 or later (GPLv2+)',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Operating System :: POSIX',
|
||||
],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"ogni=ognibuild.__main__:main",
|
||||
"deb-fix-build=ognibuild.debian.fix_build:main",
|
||||
]
|
||||
},
|
||||
install_requires=[
|
||||
'breezy',
|
||||
'buildlog-consultant>=0.0.10',
|
||||
'requirements-parser',
|
||||
],
|
||||
extras_require={
|
||||
'debian': ['debmutate', 'python_debian', 'python_apt'],
|
||||
},
|
||||
tests_require=['python_debian', 'buildlog-consultant', 'breezy', 'testtools'],
|
||||
test_suite='ognibuild.tests.test_suite',
|
||||
)
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2022 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from ognibuild.buildlog import PROBLEM_CONVERTERS
|
||||
|
||||
from buildlog_consultant import (
|
||||
problem_clses,
|
||||
__version__ as buildlog_consultant_version,
|
||||
)
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestProblemsExists(TestCase):
|
||||
|
||||
def test_exist(self):
|
||||
for entry in PROBLEM_CONVERTERS:
|
||||
if len(entry) == 2:
|
||||
problem_kind, fn = entry
|
||||
min_version = None
|
||||
elif len(entry) == 3:
|
||||
problem_kind, fn, min_version = entry
|
||||
else:
|
||||
raise TypeError(entry)
|
||||
if min_version is not None:
|
||||
min_version_tuple = tuple(
|
||||
[int(x) for x in min_version.split('.')])
|
||||
if buildlog_consultant_version < min_version_tuple:
|
||||
continue
|
||||
self.assertTrue(
|
||||
problem_kind in problem_clses,
|
||||
f"{problem_kind} does not exist in known "
|
||||
"buildlog-consultant problem kinds")
|
|
@ -1,95 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2022 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from unittest import TestCase
|
||||
|
||||
from ognibuild.logs import (
|
||||
copy_output,
|
||||
redirect_output,
|
||||
rotate_logfile,
|
||||
DirectoryLogManager,
|
||||
)
|
||||
|
||||
|
||||
class TestCopyOutput(TestCase):
|
||||
|
||||
def test_no_tee(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = os.path.join(td, 'foo.log')
|
||||
with copy_output(p, tee=False):
|
||||
sys.stdout.write('lala\n')
|
||||
sys.stdout.flush()
|
||||
with open(p, 'r') as f:
|
||||
self.assertEqual('lala\n', f.read())
|
||||
|
||||
def test_tee(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = os.path.join(td, 'foo.log')
|
||||
with copy_output(p, tee=True):
|
||||
sys.stdout.write('lala\n')
|
||||
sys.stdout.flush()
|
||||
with open(p, 'r') as f:
|
||||
self.assertEqual('lala\n', f.read())
|
||||
|
||||
|
||||
class TestRedirectOutput(TestCase):
|
||||
|
||||
def test_simple(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = os.path.join(td, 'foo.log')
|
||||
with open(p, 'w') as f:
|
||||
with redirect_output(f):
|
||||
sys.stdout.write('lala\n')
|
||||
sys.stdout.flush()
|
||||
with open(p, 'r') as f:
|
||||
self.assertEqual('lala\n', f.read())
|
||||
|
||||
|
||||
class TestRotateLogfile(TestCase):
|
||||
|
||||
def test_does_not_exist(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = os.path.join(td, 'foo.log')
|
||||
rotate_logfile(p)
|
||||
self.assertEqual([], os.listdir(td))
|
||||
|
||||
def test_simple(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = os.path.join(td, 'foo.log')
|
||||
with open(p, 'w') as f:
|
||||
f.write('contents\n')
|
||||
rotate_logfile(p)
|
||||
self.assertEqual(['foo.log.1'], os.listdir(td))
|
||||
|
||||
|
||||
class TestLogManager(TestCase):
|
||||
|
||||
def test_simple(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
p = os.path.join(td, 'foo.log')
|
||||
lm = DirectoryLogManager(p, mode='redirect')
|
||||
|
||||
def writesomething():
|
||||
sys.stdout.write('foo\n')
|
||||
sys.stdout.flush()
|
||||
fn = lm.wrap(writesomething)
|
||||
fn()
|
||||
with open(p, 'r') as f:
|
||||
self.assertEqual('foo\n', f.read())
|
|
@ -1,47 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2022 Jelmer Vernooij <jelmer@jelmer.uk>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
from ognibuild.resolver.apt import get_possible_python3_paths_for_python_object
|
||||
|
||||
|
||||
class TestPython3Paths(TestCase):
|
||||
|
||||
def test_paths(self):
|
||||
self.assertEqual([
|
||||
'/usr/lib/python3/dist\\-packages/dulwich/__init__\\.py',
|
||||
'/usr/lib/python3/dist\\-packages/dulwich\\.py',
|
||||
'/usr/lib/python3\\.[0-9]+/'
|
||||
'lib\\-dynload/dulwich.cpython\\-.*\\.so',
|
||||
'/usr/lib/python3\\.[0-9]+/dulwich\\.py',
|
||||
'/usr/lib/python3\\.[0-9]+/dulwich/__init__\\.py'],
|
||||
get_possible_python3_paths_for_python_object('dulwich'))
|
||||
self.assertEqual([
|
||||
'/usr/lib/python3/dist\\-packages/cleo/foo/__init__\\.py',
|
||||
'/usr/lib/python3/dist\\-packages/cleo/foo\\.py',
|
||||
'/usr/lib/python3\\.[0-9]+/'
|
||||
'lib\\-dynload/cleo/foo.cpython\\-.*\\.so',
|
||||
'/usr/lib/python3\\.[0-9]+/cleo/foo\\.py',
|
||||
'/usr/lib/python3\\.[0-9]+/cleo/foo/__init__\\.py',
|
||||
'/usr/lib/python3/dist\\-packages/cleo/__init__\\.py',
|
||||
'/usr/lib/python3/dist\\-packages/cleo\\.py',
|
||||
'/usr/lib/python3\\.[0-9]+/lib\\-dynload/cleo.cpython\\-.*\\.so',
|
||||
'/usr/lib/python3\\.[0-9]+/cleo\\.py',
|
||||
'/usr/lib/python3\\.[0-9]+/cleo/__init__\\.py'],
|
||||
get_possible_python3_paths_for_python_object('cleo.foo'))
|
Loading…
Add table
Add a link
Reference in a new issue