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
|
name: Python package
|
||||||
|
|
||||||
"on":
|
on: [push, pull_request]
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 6 * * *' # Daily 6AM UTC build
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -14,7 +9,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest, macos-latest]
|
||||||
python-version: [3.7, 3.8, 3.9, '3.10']
|
python-version: [3.7, 3.8]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -25,28 +20,28 @@ jobs:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip flake8 cython
|
||||||
python -m pip install -e ".[remote,dep_server,dev]"
|
|
||||||
python setup.py develop
|
python setup.py develop
|
||||||
- name: Install Debian-specific dependencies
|
- name: Install Debian-specific dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt install libapt-pkg-dev
|
||||||
sudo apt install python3-wheel libapt-pkg-dev
|
python -m pip install wheel
|
||||||
python -m pip install \
|
python -m pip install git+https://salsa.debian.org/apt-team/python-apt
|
||||||
python_apt@git+https://salsa.debian.org/apt-team/python-apt.git
|
|
||||||
python -m pip install -e ".[debian]"
|
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'"
|
if: "matrix.python-version != 'pypy3' && matrix.os == 'ubuntu-latest'"
|
||||||
- name: Style checks
|
- name: Style checks
|
||||||
run: |
|
run: |
|
||||||
pip install flake8
|
|
||||||
python -m flake8
|
python -m flake8
|
||||||
- name: Typing checks
|
- name: Typing checks
|
||||||
run: |
|
run: |
|
||||||
pip install -U mypy types-toml
|
pip install -U mypy
|
||||||
python -m mypy ognibuild
|
python -m mypy ognibuild
|
||||||
if: "matrix.python-version != 'pypy3'"
|
if: "matrix.python-version != 'pypy3'"
|
||||||
- name: Test suite run
|
- name: Test suite run
|
||||||
run: |
|
run: |
|
||||||
python -m unittest tests.test_suite
|
python -m unittest ognibuild.tests.test_suite
|
||||||
env:
|
env:
|
||||||
PYTHONHASHSEED: random
|
PYTHONHASHSEED: random
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,3 @@
|
||||||
.coverage
|
|
||||||
build
|
build
|
||||||
*~
|
*~
|
||||||
ognibuild.egg-info
|
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
|
It also includes a subcommand that can fix up the build dependencies
|
||||||
for Debian packages, called deb-fix-build.
|
for Debian packages, called deb-fix-build.
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```
|
|
||||||
ogni -d https://gitlab.gnome.org/GNOME/fractal install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Ognibuild is functional, but sometimes rough around the edges. If you run into
|
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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
|
|
||||||
ckBqZWxtZXIuY28udWs+iQJUBBMBCgA+AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4B
|
|
||||||
AheAFiEE3IN+4Up+NzR+hwYXAIBvK9cppFcFAmDrkoEFCRlgswAACgkQAIBvK9cp
|
|
||||||
pFcshA//dO+lmT0hzvjaFWw5YcQUx9ce3ngLdm2ooMHP498Zaiee0W1aRN4F82G+
|
|
||||||
akgIy0AdNKmGW9/LVImngCtunQAQkv+OoS4fapOW0sgRzOltBNBZckXewOjg7DCG
|
|
||||||
L9/5voUpU8fkk4Fx2H/kGliq6I9VNBIZrrLOkiOLPvsnMw+FD7/XaDVWt+6QuWEK
|
|
||||||
uy6YbxCXn9fsxQQsf9aJ6PB7SKfT1gRbqLzqipfnmQOBQEHUcGC3YugjsWmRm21j
|
|
||||||
r6ez4l3YPTLVoYL9R8A0Pa9UgIbOcIUDVPacj7I/hj5nDah8ACAl+/baMo3HxfOP
|
|
||||||
yTV3tYCzfTl+rQKvwlQ0yiFjPSmC5OtyBhW98h0GYxq62niFM050w3MaTmdfxrou
|
|
||||||
D690eKq8O+naRCH+JTewquYr4dc3N1zGxgagJ57C1sX5ShkP90D3jNFK+U7crJkI
|
|
||||||
FPfg5K4KbYryAWl/JbXyxoiuZMczV59bUlUugBJ+AhSPp0KAPcKI8BYoHb0Na3Db
|
|
||||||
Maj41CyEVYCMUl7XWig+DUNXZ1eeA/9WbEXXAoMFeY05xnCV6WVZudcLa1T4qEWU
|
|
||||||
XSZ2mWdTKXH5POPPV0h3I57oANDCF+E3jq3++RcvSCTTq6SkrAWp+UaL910pySnt
|
|
||||||
GSlkkN95DN6PhdVgnFArpi5csFrjNJ0cBGdJEKBGJ9/oO9C4x++0JUplbG1lciBW
|
|
||||||
ZXJub29paiA8amVsbWVyQG5sLmxpbnV4Lm9yZz6JAjwEMAECACYFAk1vtFIfHSBu
|
|
||||||
bC5saW51eC5vcmcgbm8gbG9uZ2VyIGV4aXN0cwAKCRAAgG8r1ymkVw47D/9VGUUe
|
|
||||||
AZ59IiTGDfzXCjy3fyDs1dsvAa2PG8mOevCfFBJWd6wbGlkeDKj9OrQvL489i0/M
|
|
||||||
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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 os
|
||||||
import stat
|
import stat
|
||||||
from typing import List, Dict, Type
|
|
||||||
|
|
||||||
|
|
||||||
__version__ = (0, 0, 15)
|
__version__ = (0, 0, 7)
|
||||||
version_string = '.'.join(map(str, __version__))
|
|
||||||
|
|
||||||
|
|
||||||
USER_AGENT = f"Ognibuild/{version_string}"
|
USER_AGENT = "Ognibuild"
|
||||||
|
|
||||||
|
|
||||||
class DetailedFailure(Exception):
|
class DetailedFailure(Exception):
|
||||||
|
@ -34,12 +32,6 @@ class DetailedFailure(Exception):
|
||||||
self.argv = argv
|
self.argv = argv
|
||||||
self.error = error
|
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):
|
class UnidentifiedError(Exception):
|
||||||
"""An unidentified error."""
|
"""An unidentified error."""
|
||||||
|
@ -50,13 +42,6 @@ class UnidentifiedError(Exception):
|
||||||
self.lines = lines
|
self.lines = lines
|
||||||
self.secondary = secondary
|
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):
|
def __repr__(self):
|
||||||
return "<%s(%r, %r, ..., secondary=%r)>" % (
|
return "<%s(%r, %r, ..., secondary=%r)>" % (
|
||||||
type(self).__name__,
|
type(self).__name__,
|
||||||
|
@ -79,64 +64,17 @@ def shebang_binary(p):
|
||||||
return os.path.basename(args[0].decode()).strip()
|
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):
|
class Requirement(object):
|
||||||
|
|
||||||
# Name of the family of requirements - e.g. "python-package"
|
# Name of the family of requirements - e.g. "python-package"
|
||||||
family: str
|
family: str
|
||||||
|
|
||||||
_JSON_DESERIALIZERS: Dict[str, Type["Requirement"]] = {}
|
def __init__(self, family):
|
||||||
|
self.family = family
|
||||||
@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 met(self, session):
|
def met(self, session):
|
||||||
raise NotImplementedError(self)
|
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):
|
class UpstreamOutput(object):
|
||||||
def __init__(self, family):
|
def __init__(self, family):
|
||||||
|
|
|
@ -15,13 +15,11 @@
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
from contextlib import ExitStack
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from urllib.parse import urlparse
|
from . import UnidentifiedError, DetailedFailure
|
||||||
from . import UnidentifiedError, DetailedFailure, version_string
|
|
||||||
from .buildlog import (
|
from .buildlog import (
|
||||||
InstallFixer,
|
InstallFixer,
|
||||||
ExplainInstallFixer,
|
ExplainInstallFixer,
|
||||||
|
@ -31,10 +29,9 @@ from .buildlog import (
|
||||||
from .buildsystem import NoBuildToolsFound, detect_buildsystems
|
from .buildsystem import NoBuildToolsFound, detect_buildsystems
|
||||||
from .resolver import (
|
from .resolver import (
|
||||||
auto_resolver,
|
auto_resolver,
|
||||||
select_resolvers,
|
native_resolvers,
|
||||||
UnsatisfiedRequirements,
|
|
||||||
)
|
)
|
||||||
from .session import SessionSetupFailure
|
from .resolver.apt import AptResolver
|
||||||
|
|
||||||
|
|
||||||
def display_explain_commands(commands):
|
def display_explain_commands(commands):
|
||||||
|
@ -42,33 +39,34 @@ def display_explain_commands(commands):
|
||||||
for command, reqs in commands:
|
for command, reqs in commands:
|
||||||
if isinstance(command, list):
|
if isinstance(command, list):
|
||||||
command = shlex.join(command)
|
command = shlex.join(command)
|
||||||
logging.info(
|
logging.info(" %s (to install %s)", command, ", ".join(map(str, reqs)))
|
||||||
" %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(
|
def install_necessary_declared_requirements(
|
||||||
session, resolver, fixers, buildsystems, stages, explain=False
|
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:
|
install_missing_reqs(session, resolver, relevant, explain=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
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Types of dependencies:
|
# Types of dependencies:
|
||||||
|
@ -84,7 +82,6 @@ STAGE_MAP = {
|
||||||
"test": ["test", "build", "core"],
|
"test": ["test", "build", "core"],
|
||||||
"build": ["build", "core"],
|
"build": ["build", "core"],
|
||||||
"clean": [],
|
"clean": [],
|
||||||
"verify": ["build", "core", "test"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,13 +95,9 @@ def determine_fixers(session, resolver, explain=False):
|
||||||
def main(): # noqa: C901
|
def main(): # noqa: C901
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog='ogni')
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--version", action="version", version="%(prog)s " + version_string
|
"--directory", "-d", type=str, help="Directory for project.", default="."
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--directory", "-d", type=str, help="Directory for project.",
|
|
||||||
default="."
|
|
||||||
)
|
)
|
||||||
parser.add_argument("--schroot", type=str, help="schroot to run in.")
|
parser.add_argument("--schroot", type=str, help="schroot to run in.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -130,15 +123,6 @@ def main(): # noqa: C901
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Ignore declared dependencies, follow build errors only",
|
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")
|
parser.add_argument("--verbose", action="store_true", help="Be verbose")
|
||||||
subparsers = parser.add_subparsers(dest="subcommand")
|
subparsers = parser.add_subparsers(dest="subcommand")
|
||||||
subparsers.add_parser("dist")
|
subparsers.add_parser("dist")
|
||||||
|
@ -146,11 +130,12 @@ def main(): # noqa: C901
|
||||||
subparsers.add_parser("clean")
|
subparsers.add_parser("clean")
|
||||||
subparsers.add_parser("test")
|
subparsers.add_parser("test")
|
||||||
subparsers.add_parser("info")
|
subparsers.add_parser("info")
|
||||||
subparsers.add_parser("verify")
|
|
||||||
exec_parser = subparsers.add_parser("exec")
|
exec_parser = subparsers.add_parser("exec")
|
||||||
exec_parser.add_argument(
|
exec_parser.add_argument('subargv', nargs=argparse.REMAINDER, help='Command to run.')
|
||||||
'subargv', nargs=argparse.REMAINDER, help='Command to run.')
|
|
||||||
install_parser = subparsers.add_parser("install")
|
install_parser = subparsers.add_parser("install")
|
||||||
|
install_parser.add_argument(
|
||||||
|
"--user", action="store_true", help="Install in local-user directories."
|
||||||
|
)
|
||||||
install_parser.add_argument(
|
install_parser.add_argument(
|
||||||
"--prefix", type=str, help='Prefix to install in')
|
"--prefix", type=str, help='Prefix to install in')
|
||||||
|
|
||||||
|
@ -170,72 +155,38 @@ def main(): # noqa: C901
|
||||||
from .session.plain import PlainSession
|
from .session.plain import PlainSession
|
||||||
|
|
||||||
session = PlainSession()
|
session = PlainSession()
|
||||||
|
with session:
|
||||||
with ExitStack() as es:
|
logging.info("Preparing directory %s", args.directory)
|
||||||
try:
|
external_dir, internal_dir = session.setup_from_directory(args.directory)
|
||||||
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)
|
|
||||||
session.chdir(internal_dir)
|
session.chdir(internal_dir)
|
||||||
os.chdir(external_dir)
|
os.chdir(external_dir)
|
||||||
|
|
||||||
if not session.is_temporary and args.subcommand == 'info':
|
if not session.is_temporary and args.subcommand == 'info':
|
||||||
args.explain = True
|
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)
|
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)
|
logging.info("Using requirement resolver: %s", resolver)
|
||||||
fixers = determine_fixers(session, resolver, explain=args.explain)
|
fixers = determine_fixers(session, resolver, explain=args.explain)
|
||||||
try:
|
try:
|
||||||
if args.subcommand == "exec":
|
if args.subcommand == "exec":
|
||||||
from .fix_build import run_with_build_fixers
|
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
|
return 0
|
||||||
bss = list(detect_buildsystems(external_dir))
|
bss = list(detect_buildsystems(args.directory))
|
||||||
logging.info("Detected buildsystems: %s", ", ".join(map(str, bss)))
|
logging.info("Detected buildsystems: %s", ", ".join(map(str, bss)))
|
||||||
if not args.ignore_declared_dependencies:
|
if not args.ignore_declared_dependencies:
|
||||||
stages = STAGE_MAP[args.subcommand]
|
stages = STAGE_MAP[args.subcommand]
|
||||||
if stages:
|
if stages:
|
||||||
logging.info(
|
logging.info("Checking that declared requirements are present")
|
||||||
"Checking that declared requirements are present")
|
|
||||||
try:
|
try:
|
||||||
install_necessary_declared_requirements(
|
install_necessary_declared_requirements(
|
||||||
session, resolver, fixers, bss, stages,
|
session, resolver, fixers, bss, stages, explain=args.explain
|
||||||
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:
|
except ExplainInstall as e:
|
||||||
display_explain_commands(e.commands)
|
display_explain_commands(e.commands)
|
||||||
return 1
|
return 1
|
||||||
|
@ -256,15 +207,11 @@ def main(): # noqa: C901
|
||||||
if args.subcommand == "build":
|
if args.subcommand == "build":
|
||||||
from .build import run_build
|
from .build import run_build
|
||||||
|
|
||||||
run_build(
|
run_build(session, buildsystems=bss, resolver=resolver, fixers=fixers)
|
||||||
session, buildsystems=bss, resolver=resolver,
|
|
||||||
fixers=fixers)
|
|
||||||
if args.subcommand == "clean":
|
if args.subcommand == "clean":
|
||||||
from .clean import run_clean
|
from .clean import run_clean
|
||||||
|
|
||||||
run_clean(
|
run_clean(session, buildsystems=bss, resolver=resolver, fixers=fixers)
|
||||||
session, buildsystems=bss, resolver=resolver,
|
|
||||||
fixers=fixers)
|
|
||||||
if args.subcommand == "install":
|
if args.subcommand == "install":
|
||||||
from .install import run_install
|
from .install import run_install
|
||||||
|
|
||||||
|
@ -279,42 +226,14 @@ def main(): # noqa: C901
|
||||||
if args.subcommand == "test":
|
if args.subcommand == "test":
|
||||||
from .test import run_test
|
from .test import run_test
|
||||||
|
|
||||||
run_test(
|
run_test(session, buildsystems=bss, resolver=resolver, fixers=fixers)
|
||||||
session, buildsystems=bss, resolver=resolver,
|
|
||||||
fixers=fixers)
|
|
||||||
if args.subcommand == "info":
|
if args.subcommand == "info":
|
||||||
from .info import run_info
|
from .info import run_info
|
||||||
|
|
||||||
run_info(session, buildsystems=bss, fixers=fixers)
|
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:
|
except ExplainInstall as e:
|
||||||
display_explain_commands(e.commands)
|
display_explain_commands(e.commands)
|
||||||
except UnidentifiedError:
|
except (UnidentifiedError, DetailedFailure):
|
||||||
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')
|
|
||||||
return 1
|
return 1
|
||||||
except NoBuildToolsFound:
|
except NoBuildToolsFound:
|
||||||
logging.info("No build tools found.")
|
logging.info("No build tools found.")
|
||||||
|
|
|
@ -15,28 +15,16 @@
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
from .buildsystem import NoBuildToolsFound
|
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):
|
||||||
|
|
||||||
|
|
||||||
def run_build(session, buildsystems, resolver, fixers, log_manager=None):
|
|
||||||
# Some things want to write to the user's home directory,
|
# Some things want to write to the user's home directory,
|
||||||
# e.g. pip caches in ~/.cache
|
# e.g. pip caches in ~/.cache
|
||||||
session.create_home()
|
session.create_home()
|
||||||
|
|
||||||
if log_manager is None:
|
|
||||||
log_manager = NoLogManager()
|
|
||||||
|
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
iterate_with_build_fixers(
|
buildsystem.build(session, resolver, fixers)
|
||||||
fixers, log_manager.wrap(
|
|
||||||
partial(buildsystem.build, session, resolver)))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NoBuildToolsFound()
|
raise NoBuildToolsFound()
|
||||||
|
|
|
@ -19,32 +19,65 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, List, Callable, Union, Tuple
|
|
||||||
|
|
||||||
from buildlog_consultant.common import (
|
from buildlog_consultant.common import (
|
||||||
Problem,
|
MissingPythonModule,
|
||||||
|
MissingPythonDistribution,
|
||||||
|
MissingCHeader,
|
||||||
|
MissingPkgConfig,
|
||||||
|
MissingCommand,
|
||||||
|
MissingFile,
|
||||||
|
MissingJavaScriptRuntime,
|
||||||
|
MissingSprocketsFile,
|
||||||
|
MissingGoPackage,
|
||||||
MissingPerlFile,
|
MissingPerlFile,
|
||||||
|
MissingPerlModule,
|
||||||
|
MissingXmlEntity,
|
||||||
|
MissingJDKFile,
|
||||||
|
MissingJDK,
|
||||||
|
MissingJRE,
|
||||||
|
MissingNodeModule,
|
||||||
|
MissingNodePackage,
|
||||||
|
MissingPhpClass,
|
||||||
|
MissingRubyGem,
|
||||||
|
MissingLibrary,
|
||||||
MissingSetupPyCommand,
|
MissingSetupPyCommand,
|
||||||
MissingCMakeComponents,
|
MissingJavaClass,
|
||||||
|
MissingCSharpCompiler,
|
||||||
|
MissingRPackage,
|
||||||
|
MissingRubyFile,
|
||||||
|
MissingAutoconfMacro,
|
||||||
|
MissingValaPackage,
|
||||||
|
MissingBoostComponents,
|
||||||
MissingXfceDependency,
|
MissingXfceDependency,
|
||||||
MissingHaskellDependencies,
|
MissingHaskellDependencies,
|
||||||
|
MissingVagueDependency,
|
||||||
|
DhAddonLoadFailure,
|
||||||
MissingMavenArtifacts,
|
MissingMavenArtifacts,
|
||||||
|
MissingIntrospectionTypelib,
|
||||||
|
GnomeCommonMissing,
|
||||||
MissingGnomeCommonDependency,
|
MissingGnomeCommonDependency,
|
||||||
|
UnknownCertificateAuthority,
|
||||||
|
CMakeFilesMissing,
|
||||||
|
MissingLibtool,
|
||||||
|
MissingQt,
|
||||||
|
MissingX11,
|
||||||
MissingPerlPredeclared,
|
MissingPerlPredeclared,
|
||||||
MissingLatexFile,
|
MissingLatexFile,
|
||||||
MissingCargoCrate,
|
MissingCargoCrate,
|
||||||
|
MissingStaticLibrary,
|
||||||
)
|
)
|
||||||
|
from buildlog_consultant.apt import UnsatisfiedAptDependencies
|
||||||
|
|
||||||
from . import OneOfRequirement
|
|
||||||
from .fix_build import BuildFixer
|
from .fix_build import BuildFixer
|
||||||
from .requirements import (
|
from .requirements import (
|
||||||
Requirement,
|
|
||||||
BinaryRequirement,
|
BinaryRequirement,
|
||||||
PathRequirement,
|
PathRequirement,
|
||||||
PkgConfigRequirement,
|
PkgConfigRequirement,
|
||||||
CHeaderRequirement,
|
CHeaderRequirement,
|
||||||
JavaScriptRuntimeRequirement,
|
JavaScriptRuntimeRequirement,
|
||||||
ValaPackageRequirement,
|
ValaPackageRequirement,
|
||||||
|
RubyGemRequirement,
|
||||||
GoPackageRequirement,
|
GoPackageRequirement,
|
||||||
DhAddonRequirement,
|
DhAddonRequirement,
|
||||||
PhpClassRequirement,
|
PhpClassRequirement,
|
||||||
|
@ -59,7 +92,6 @@ from .requirements import (
|
||||||
HaskellPackageRequirement,
|
HaskellPackageRequirement,
|
||||||
MavenArtifactRequirement,
|
MavenArtifactRequirement,
|
||||||
BoostComponentRequirement,
|
BoostComponentRequirement,
|
||||||
KF5ComponentRequirement,
|
|
||||||
GnomeCommonRequirement,
|
GnomeCommonRequirement,
|
||||||
JDKFileRequirement,
|
JDKFileRequirement,
|
||||||
JDKRequirement,
|
JDKRequirement,
|
||||||
|
@ -80,124 +112,86 @@ from .requirements import (
|
||||||
LatexPackageRequirement,
|
LatexPackageRequirement,
|
||||||
CargoCrateRequirement,
|
CargoCrateRequirement,
|
||||||
StaticLibraryRequirement,
|
StaticLibraryRequirement,
|
||||||
GnulibDirectoryRequirement,
|
|
||||||
LuaModuleRequirement,
|
|
||||||
PHPExtensionRequirement,
|
|
||||||
VcsControlDirectoryAccessRequirement,
|
|
||||||
RubyGemRequirement,
|
|
||||||
QtModuleRequirement,
|
|
||||||
)
|
)
|
||||||
from .resolver import UnsatisfiedRequirements
|
from .resolver import UnsatisfiedRequirements
|
||||||
|
|
||||||
|
|
||||||
def map_pytest_arguments_to_plugin(args):
|
def problem_to_upstream_requirement(problem): # noqa: C901
|
||||||
# TODO(jelmer): Map argument to PytestPluginRequirement
|
if isinstance(problem, MissingFile):
|
||||||
return None
|
return PathRequirement(problem.path)
|
||||||
|
elif isinstance(problem, MissingCommand):
|
||||||
|
return BinaryRequirement(problem.command)
|
||||||
ProblemToRequirementConverter = Callable[[Problem], Optional[Requirement]]
|
elif isinstance(problem, MissingPkgConfig):
|
||||||
|
return PkgConfigRequirement(problem.module, problem.minimum_version)
|
||||||
|
elif isinstance(problem, MissingCHeader):
|
||||||
PROBLEM_CONVERTERS: List[Union[
|
return CHeaderRequirement(problem.header)
|
||||||
Tuple[str, ProblemToRequirementConverter],
|
elif isinstance(problem, MissingIntrospectionTypelib):
|
||||||
Tuple[str, ProblemToRequirementConverter, str]]] = [
|
return IntrospectionTypelibRequirement(problem.library)
|
||||||
('missing-file', lambda p: PathRequirement(p.path)),
|
elif isinstance(problem, MissingJavaScriptRuntime):
|
||||||
('command-missing', lambda p: BinaryRequirement(p.command)),
|
return JavaScriptRuntimeRequirement()
|
||||||
('valac-cannot-compile', lambda p: VagueDependencyRequirement('valac'),
|
elif isinstance(problem, MissingRubyGem):
|
||||||
'0.0.27'),
|
return RubyGemRequirement(problem.gem, problem.version)
|
||||||
('missing-cmake-files', lambda p: OneOfRequirement(
|
elif isinstance(problem, MissingValaPackage):
|
||||||
[CMakefileRequirement(filename, p.version)
|
return ValaPackageRequirement(problem.package)
|
||||||
for filename in p.filenames])),
|
elif isinstance(problem, MissingGoPackage):
|
||||||
('missing-command-or-build-file', lambda p: BinaryRequirement(p.command)),
|
return GoPackageRequirement(problem.package)
|
||||||
('missing-pkg-config-package',
|
elif isinstance(problem, MissingBoostComponents):
|
||||||
lambda p: PkgConfigRequirement(p.module, p.minimum_version)),
|
return [BoostComponentRequirement(name) for name in problem.components]
|
||||||
('missing-c-header', lambda p: CHeaderRequirement(p.header)),
|
elif isinstance(problem, DhAddonLoadFailure):
|
||||||
('missing-introspection-typelib',
|
return DhAddonRequirement(problem.path)
|
||||||
lambda p: IntrospectionTypelibRequirement(p.library)),
|
elif isinstance(problem, MissingPhpClass):
|
||||||
('missing-python-module', lambda p: PythonModuleRequirement(
|
return PhpClassRequirement(problem.php_class)
|
||||||
p.module, python_version=p.python_version,
|
elif isinstance(problem, MissingRPackage):
|
||||||
minimum_version=p.minimum_version)),
|
return RPackageRequirement(problem.package, problem.minimum_version)
|
||||||
('missing-python-distribution', lambda p: PythonPackageRequirement(
|
elif isinstance(problem, MissingNodeModule):
|
||||||
p.distribution, python_version=p.python_version,
|
return NodeModuleRequirement(problem.module)
|
||||||
minimum_version=p.minimum_version)),
|
elif isinstance(problem, MissingStaticLibrary):
|
||||||
('javascript-runtime-missing', lambda p: JavaScriptRuntimeRequirement()),
|
return StaticLibraryRequirement(problem.library, problem.filename)
|
||||||
('missing-node-module', lambda p: NodeModuleRequirement(p.module)),
|
elif isinstance(problem, MissingNodePackage):
|
||||||
('missing-node-package', lambda p: NodePackageRequirement(p.package)),
|
return NodePackageRequirement(problem.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
|
|
||||||
elif isinstance(problem, MissingLatexFile):
|
elif isinstance(problem, MissingLatexFile):
|
||||||
if problem.filename.endswith('.sty'):
|
if problem.filename.endswith('.sty'):
|
||||||
return LatexPackageRequirement(problem.filename[:-4])
|
return LatexPackageRequirement(problem.filename[:-4])
|
||||||
return None
|
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):
|
elif isinstance(problem, MissingHaskellDependencies):
|
||||||
return OneOfRequirement(
|
return [HaskellPackageRequirement.from_string(dep) for dep in problem.deps]
|
||||||
[HaskellPackageRequirement.from_string(dep)
|
|
||||||
for dep in problem.deps])
|
|
||||||
elif isinstance(problem, MissingMavenArtifacts):
|
elif isinstance(problem, MissingMavenArtifacts):
|
||||||
return OneOfRequirement([
|
return [
|
||||||
MavenArtifactRequirement.from_str(artifact)
|
MavenArtifactRequirement.from_str(artifact)
|
||||||
for artifact in problem.artifacts
|
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):
|
elif isinstance(problem, MissingPerlPredeclared):
|
||||||
ret = PerlPreDeclaredRequirement(problem.name)
|
ret = PerlPreDeclaredRequirement(problem.name)
|
||||||
try:
|
try:
|
||||||
|
@ -216,20 +210,36 @@ def problem_to_upstream_requirement(
|
||||||
return BinaryRequirement("glib-gettextize")
|
return BinaryRequirement("glib-gettextize")
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"No known command for gnome-common dependency %s",
|
"No known command for gnome-common dependency %s", problem.package
|
||||||
problem.package
|
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
elif isinstance(problem, MissingXfceDependency):
|
elif isinstance(problem, MissingXfceDependency):
|
||||||
if problem.package == "gtk-doc":
|
if problem.package == "gtk-doc":
|
||||||
return BinaryRequirement("gtkdocize")
|
return BinaryRequirement("gtkdocize")
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning("No known command for xfce dependency %s", problem.package)
|
||||||
"No known command for xfce dependency %s", problem.package)
|
|
||||||
return None
|
return None
|
||||||
|
elif isinstance(problem, MissingPerlModule):
|
||||||
|
return PerlModuleRequirement(
|
||||||
|
module=problem.module, filename=problem.filename, inc=problem.inc
|
||||||
|
)
|
||||||
elif isinstance(problem, MissingPerlFile):
|
elif isinstance(problem, MissingPerlFile):
|
||||||
return PerlFileRequirement(filename=problem.filename)
|
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
|
from .resolver.apt import AptRequirement
|
||||||
return AptRequirement(problem.relations)
|
return AptRequirement(problem.relations)
|
||||||
else:
|
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
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# 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 .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,
|
# Some things want to write to the user's home directory,
|
||||||
# e.g. pip caches in ~/.cache
|
# e.g. pip caches in ~/.cache
|
||||||
session.create_home()
|
session.create_home()
|
||||||
|
|
||||||
if log_manager is None:
|
|
||||||
log_manager = NoLogManager()
|
|
||||||
|
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
iterate_with_build_fixers(
|
buildsystem.clean(session, resolver, fixers)
|
||||||
fixers, log_manager.wrap(
|
|
||||||
partial(buildsystem.clean, session, resolver)))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NoBuildToolsFound()
|
raise NoBuildToolsFound()
|
||||||
|
|
|
@ -29,8 +29,7 @@ def satisfy_build_deps(session: Session, tree, debian_path):
|
||||||
deps.append(source[name].strip().strip(","))
|
deps.append(source[name].strip().strip(","))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
for name in ["Build-Conflicts", "Build-Conflicts-Indep",
|
for name in ["Build-Conflicts", "Build-Conflicts-Indep", "Build-Conflicts-Arch"]:
|
||||||
"Build-Conflicts-Arch"]:
|
|
||||||
try:
|
try:
|
||||||
deps.append("Conflicts: " + source[name])
|
deps.append("Conflicts: " + source[name])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
from debian.changelog import Version
|
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Optional, Iterable
|
from typing import List, Optional
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from buildlog_consultant.apt import (
|
from buildlog_consultant.apt import (
|
||||||
|
@ -38,12 +37,7 @@ from .file_search import (
|
||||||
def run_apt(
|
def run_apt(
|
||||||
session: Session, args: List[str], prefix: Optional[List[str]] = None
|
session: Session, args: List[str], prefix: Optional[List[str]] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run apt.
|
"""Run apt."""
|
||||||
|
|
||||||
Raises:
|
|
||||||
DetailedFailure: When a known error occurs
|
|
||||||
UnidentifiedError: If an unknown error occurs
|
|
||||||
"""
|
|
||||||
if prefix is None:
|
if prefix is None:
|
||||||
prefix = []
|
prefix = []
|
||||||
args = prefix = ["apt", "-y"] + args
|
args = prefix = ["apt", "-y"] + args
|
||||||
|
@ -54,7 +48,7 @@ def run_apt(
|
||||||
match, error = find_apt_get_failure(lines)
|
match, error = find_apt_get_failure(lines)
|
||||||
if error is not None:
|
if error is not None:
|
||||||
raise DetailedFailure(retcode, args, error)
|
raise DetailedFailure(retcode, args, error)
|
||||||
while lines and lines[-1].rstrip('\n') == "":
|
while lines and lines[-1] == "":
|
||||||
lines.pop(-1)
|
lines.pop(-1)
|
||||||
raise UnidentifiedError(retcode, args, lines, secondary=match)
|
raise UnidentifiedError(retcode, args, lines, secondary=match)
|
||||||
|
|
||||||
|
@ -99,18 +93,13 @@ class AptManager(object):
|
||||||
def package_exists(self, package):
|
def package_exists(self, package):
|
||||||
return package in self.apt_cache
|
return package in self.apt_cache
|
||||||
|
|
||||||
def package_versions(self, package: str) -> Optional[Iterable[Version]]:
|
def package_versions(self, package):
|
||||||
try:
|
return list(self.apt_cache[package].versions)
|
||||||
return list(self.apt_cache[package].versions)
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def get_packages_for_paths(
|
def get_packages_for_paths(self, paths, regex=False, case_insensitive=False):
|
||||||
self, paths, regex: bool = False, case_insensitive: bool = False):
|
|
||||||
logging.debug("Searching for packages containing %r", paths)
|
logging.debug("Searching for packages containing %r", paths)
|
||||||
return await get_packages_for_paths(
|
return get_packages_for_paths(
|
||||||
paths, self.searchers(), regex=regex,
|
paths, self.searchers(), regex=regex, case_insensitive=case_insensitive
|
||||||
case_insensitive=case_insensitive
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def missing(self, packages):
|
def missing(self, packages):
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"get_build_architecture",
|
"get_build_architecture",
|
||||||
"version_add_suffix",
|
|
||||||
"add_dummy_changelog_entry",
|
"add_dummy_changelog_entry",
|
||||||
"build",
|
"build",
|
||||||
"DetailedDebianBuildFailure",
|
"DetailedDebianBuildFailure",
|
||||||
|
@ -25,22 +24,20 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from debmutate.changelog import ChangelogEditor
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional, List, Tuple
|
|
||||||
|
|
||||||
from debian.changelog import Changelog, Version, ChangeBlock
|
from debian.changelog import Changelog
|
||||||
from debmutate.changelog import get_maintainer, ChangelogEditor
|
from debmutate.changelog import get_maintainer
|
||||||
from debmutate.reformatting import GeneratedFile
|
|
||||||
|
|
||||||
from breezy.mutabletree import MutableTree
|
from breezy.mutabletree import MutableTree
|
||||||
from breezy.plugins.debian.builder import BuildFailedError
|
from breezy.plugins.debian.builder import BuildFailedError
|
||||||
from breezy.tree import Tree
|
from breezy.tree import Tree
|
||||||
from breezy.workingtree import WorkingTree
|
|
||||||
|
|
||||||
from buildlog_consultant.sbuild import (
|
from buildlog_consultant.sbuild import (
|
||||||
worker_failure_from_sbuild_log,
|
worker_failure_from_sbuild_log,
|
||||||
|
@ -48,18 +45,10 @@ from buildlog_consultant.sbuild import (
|
||||||
|
|
||||||
from .. import DetailedFailure as DetailedFailure, UnidentifiedError
|
from .. import DetailedFailure as DetailedFailure, UnidentifiedError
|
||||||
|
|
||||||
BUILD_LOG_FILENAME = 'build.log'
|
|
||||||
|
|
||||||
DEFAULT_BUILDER = "sbuild --no-clean-source"
|
DEFAULT_BUILDER = "sbuild --no-clean-source"
|
||||||
|
|
||||||
|
|
||||||
class ChangelogNotEditable(Exception):
|
|
||||||
"""Changelog can not be edited."""
|
|
||||||
|
|
||||||
def __init__(self, path):
|
|
||||||
self.path = path
|
|
||||||
|
|
||||||
|
|
||||||
class DetailedDebianBuildFailure(DetailedFailure):
|
class DetailedDebianBuildFailure(DetailedFailure):
|
||||||
|
|
||||||
def __init__(self, stage, phase, retcode, argv, error, description):
|
def __init__(self, stage, phase, retcode, argv, error, description):
|
||||||
|
@ -71,8 +60,7 @@ class DetailedDebianBuildFailure(DetailedFailure):
|
||||||
|
|
||||||
class UnidentifiedDebianBuildError(UnidentifiedError):
|
class UnidentifiedDebianBuildError(UnidentifiedError):
|
||||||
|
|
||||||
def __init__(self, stage, phase, retcode, argv, lines, description,
|
def __init__(self, stage, phase, retcode, argv, lines, description, secondary=None):
|
||||||
secondary=None):
|
|
||||||
super(UnidentifiedDebianBuildError, self).__init__(
|
super(UnidentifiedDebianBuildError, self).__init__(
|
||||||
retcode, argv, lines, secondary)
|
retcode, argv, lines, secondary)
|
||||||
self.stage = stage
|
self.stage = stage
|
||||||
|
@ -87,12 +75,11 @@ class MissingChangesFile(Exception):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
|
|
||||||
def find_changes_files(path: str, package: str, version: Version):
|
def find_changes_files(path, package, version):
|
||||||
non_epoch_version = version.upstream_version or ''
|
non_epoch_version = version.upstream_version
|
||||||
if version.debian_version is not None:
|
if version.debian_version is not None:
|
||||||
non_epoch_version += "-%s" % version.debian_version
|
non_epoch_version += "-%s" % version.debian_version
|
||||||
c = re.compile('%s_%s_(.*).changes' % (
|
c = re.compile('%s_%s_(.*).changes' % (re.escape(package), re.escape(non_epoch_version)))
|
||||||
re.escape(package), re.escape(non_epoch_version)))
|
|
||||||
for entry in os.scandir(path):
|
for entry in os.scandir(path):
|
||||||
m = c.match(entry.name)
|
m = c.match(entry.name)
|
||||||
if m:
|
if m:
|
||||||
|
@ -122,32 +109,15 @@ def control_files_in_root(tree: Tree, subpath: str) -> bool:
|
||||||
return False
|
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(
|
def add_dummy_changelog_entry(
|
||||||
tree: MutableTree,
|
tree: MutableTree,
|
||||||
subpath: str,
|
subpath: str,
|
||||||
suffix: str,
|
suffix: str,
|
||||||
suite: str,
|
suite: str,
|
||||||
message: str,
|
message: str,
|
||||||
timestamp: Optional[datetime] = None,
|
timestamp=None,
|
||||||
maintainer: Tuple[Optional[str], Optional[str]] = None,
|
maintainer=None,
|
||||||
allow_reformatting: bool = True,
|
):
|
||||||
) -> Version:
|
|
||||||
"""Add a dummy changelog entry to a package.
|
"""Add a dummy changelog entry to a package.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -155,10 +125,18 @@ def add_dummy_changelog_entry(
|
||||||
suffix: Suffix for the version
|
suffix: Suffix for the version
|
||||||
suite: Debian suite
|
suite: Debian suite
|
||||||
message: Changelog message
|
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):
|
if control_files_in_root(tree, subpath):
|
||||||
path = os.path.join(subpath, "changelog")
|
path = os.path.join(subpath, "changelog")
|
||||||
else:
|
else:
|
||||||
|
@ -167,38 +145,38 @@ def add_dummy_changelog_entry(
|
||||||
maintainer = get_maintainer()
|
maintainer = get_maintainer()
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now()
|
timestamp = datetime.now()
|
||||||
try:
|
with ChangelogEditor(tree.abspath(os.path.join(path))) as editor:
|
||||||
with ChangelogEditor(
|
version = editor[0].version
|
||||||
tree.abspath(path), # type: ignore
|
if version.debian_revision:
|
||||||
allow_reformatting=allow_reformatting) as editor:
|
version.debian_revision = add_suffix(version.debian_revision, suffix)
|
||||||
version = version_add_suffix(editor[0].version, suffix)
|
else:
|
||||||
editor.auto_version(version, timestamp=timestamp)
|
version.upstream_version = add_suffix(version.upstream_version, suffix)
|
||||||
editor.add_entry(
|
editor.auto_version(version, timestamp=timestamp)
|
||||||
summary=[message], maintainer=maintainer, timestamp=timestamp,
|
editor.add_entry(
|
||||||
urgency='low')
|
summary=[message], maintainer=maintainer, timestamp=timestamp, urgency='low')
|
||||||
editor[0].distributions = suite
|
editor[0].distributions = suite
|
||||||
return version
|
|
||||||
except GeneratedFile as e:
|
|
||||||
raise ChangelogNotEditable(path) from e
|
|
||||||
|
|
||||||
|
|
||||||
def get_latest_changelog_entry(
|
def get_latest_changelog_entry(local_tree, subpath=""):
|
||||||
local_tree: WorkingTree, subpath: str = "") -> ChangeBlock:
|
|
||||||
if control_files_in_root(local_tree, subpath):
|
if control_files_in_root(local_tree, subpath):
|
||||||
path = os.path.join(subpath, "changelog")
|
path = os.path.join(subpath, "changelog")
|
||||||
else:
|
else:
|
||||||
path = os.path.join(subpath, "debian", "changelog")
|
path = os.path.join(subpath, "debian", "changelog")
|
||||||
with local_tree.get_file(path) as f:
|
with local_tree.get_file(path) as f:
|
||||||
cl = Changelog(f, max_blocks=1)
|
cl = Changelog(f, max_blocks=1)
|
||||||
return cl[0]
|
return cl.package, cl.version
|
||||||
|
|
||||||
|
|
||||||
def _builddeb_command(
|
def build(
|
||||||
build_command: str = DEFAULT_BUILDER,
|
local_tree,
|
||||||
result_dir: Optional[str] = None,
|
outf,
|
||||||
apt_repository: Optional[str] = None,
|
build_command=DEFAULT_BUILDER,
|
||||||
apt_repository_key: Optional[str] = None,
|
result_dir=None,
|
||||||
extra_repositories: Optional[List[str]] = None):
|
distribution=None,
|
||||||
|
subpath="",
|
||||||
|
source_date_epoch=None,
|
||||||
|
extra_repositories=None,
|
||||||
|
):
|
||||||
for repo in extra_repositories or []:
|
for repo in extra_repositories or []:
|
||||||
build_command += " --extra-repository=" + shlex.quote(repo)
|
build_command += " --extra-repository=" + shlex.quote(repo)
|
||||||
args = [
|
args = [
|
||||||
|
@ -209,34 +187,8 @@ def _builddeb_command(
|
||||||
"--guess-upstream-branch-url",
|
"--guess-upstream-branch-url",
|
||||||
"--builder=%s" % build_command,
|
"--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:
|
if result_dir:
|
||||||
args.append("--result-dir=%s" % 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.write("Running %r\n" % (build_command,))
|
||||||
outf.flush()
|
outf.flush()
|
||||||
env = dict(os.environ.items())
|
env = dict(os.environ.items())
|
||||||
|
@ -247,25 +199,22 @@ def build(
|
||||||
logging.info("Building debian packages, running %r.", build_command)
|
logging.info("Building debian packages, running %r.", build_command)
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(
|
subprocess.check_call(
|
||||||
args, cwd=local_tree.abspath(subpath), stdout=outf, stderr=outf,
|
args, cwd=local_tree.abspath(subpath), stdout=outf, stderr=outf, env=env
|
||||||
env=env
|
|
||||||
)
|
)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
raise BuildFailedError()
|
raise BuildFailedError()
|
||||||
|
|
||||||
|
|
||||||
def build_once(
|
def build_once(
|
||||||
local_tree: WorkingTree,
|
local_tree,
|
||||||
build_suite: str,
|
build_suite,
|
||||||
output_directory: str,
|
output_directory,
|
||||||
build_command: str,
|
build_command,
|
||||||
subpath: str = "",
|
subpath="",
|
||||||
source_date_epoch: Optional[int] = None,
|
source_date_epoch=None,
|
||||||
apt_repository: Optional[str] = None,
|
extra_repositories=None
|
||||||
apt_repository_key: Optional[str] = None,
|
|
||||||
extra_repositories: Optional[List[str]] = 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)
|
logging.debug("Writing build log to %s", build_log_path)
|
||||||
try:
|
try:
|
||||||
with open(build_log_path, "w") as f:
|
with open(build_log_path, "w") as f:
|
||||||
|
@ -277,8 +226,6 @@ def build_once(
|
||||||
distribution=build_suite,
|
distribution=build_suite,
|
||||||
subpath=subpath,
|
subpath=subpath,
|
||||||
source_date_epoch=source_date_epoch,
|
source_date_epoch=source_date_epoch,
|
||||||
apt_repository=apt_repository,
|
|
||||||
apt_repository_key=apt_repository_key,
|
|
||||||
extra_repositories=extra_repositories,
|
extra_repositories=extra_repositories,
|
||||||
)
|
)
|
||||||
except BuildFailedError as e:
|
except BuildFailedError as e:
|
||||||
|
@ -300,39 +247,27 @@ def build_once(
|
||||||
[], sbuild_failure.description)
|
[], sbuild_failure.description)
|
||||||
|
|
||||||
cl_entry = get_latest_changelog_entry(local_tree, subpath)
|
cl_entry = get_latest_changelog_entry(local_tree, subpath)
|
||||||
if cl_entry.package is None:
|
|
||||||
raise Exception('missing package in changelog entry')
|
|
||||||
changes_names = []
|
changes_names = []
|
||||||
for kind, entry in find_changes_files(
|
for kind, entry in find_changes_files(output_directory, cl_entry.package, cl_entry.version):
|
||||||
output_directory, cl_entry.package, cl_entry.version):
|
|
||||||
changes_names.append((entry.name))
|
changes_names.append((entry.name))
|
||||||
return (changes_names, cl_entry)
|
return (changes_names, cl_entry)
|
||||||
|
|
||||||
|
|
||||||
class GitBuildpackageMissing(Exception):
|
|
||||||
"""git-buildpackage is not installed"""
|
|
||||||
|
|
||||||
|
|
||||||
def gbp_dch(path):
|
def gbp_dch(path):
|
||||||
try:
|
subprocess.check_call(["gbp", "dch", "--ignore-branch"], cwd=path)
|
||||||
subprocess.check_call(["gbp", "dch", "--ignore-branch"], cwd=path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise GitBuildpackageMissing()
|
|
||||||
|
|
||||||
|
|
||||||
def attempt_build(
|
def attempt_build(
|
||||||
local_tree: WorkingTree,
|
local_tree,
|
||||||
suffix: str,
|
suffix,
|
||||||
build_suite: str,
|
build_suite,
|
||||||
output_directory: str,
|
output_directory,
|
||||||
build_command: str,
|
build_command,
|
||||||
build_changelog_entry: Optional[str] = None,
|
build_changelog_entry=None,
|
||||||
subpath: str = "",
|
subpath="",
|
||||||
source_date_epoch: Optional[int] = None,
|
source_date_epoch=None,
|
||||||
run_gbp_dch: bool = False,
|
run_gbp_dch=False,
|
||||||
apt_repository: Optional[str] = None,
|
extra_repositories=None
|
||||||
apt_repository_key: Optional[str] = None,
|
|
||||||
extra_repositories: Optional[List[str]] = None
|
|
||||||
):
|
):
|
||||||
"""Attempt a build, with a custom distribution set.
|
"""Attempt a build, with a custom distribution set.
|
||||||
|
|
||||||
|
@ -347,7 +282,7 @@ def attempt_build(
|
||||||
source_date_epoch: Source date epoch to set
|
source_date_epoch: Source date epoch to set
|
||||||
Returns: Tuple with (changes_name, cl_version)
|
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))
|
gbp_dch(local_tree.abspath(subpath))
|
||||||
if build_changelog_entry is not None:
|
if build_changelog_entry is not None:
|
||||||
add_dummy_changelog_entry(
|
add_dummy_changelog_entry(
|
||||||
|
@ -360,7 +295,5 @@ def attempt_build(
|
||||||
build_command,
|
build_command,
|
||||||
subpath,
|
subpath,
|
||||||
source_date_epoch=source_date_epoch,
|
source_date_epoch=source_date_epoch,
|
||||||
apt_repository=apt_repository,
|
|
||||||
apt_repository_key=apt_repository_key,
|
|
||||||
extra_repositories=extra_repositories,
|
extra_repositories=extra_repositories,
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,46 +18,43 @@
|
||||||
"""Tie breaking by build deps."""
|
"""Tie breaking by build deps."""
|
||||||
|
|
||||||
|
|
||||||
from debian.deb822 import PkgRelation
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from breezy.plugins.debian.apt_repo import LocalApt, NoAptSources
|
|
||||||
|
|
||||||
|
|
||||||
class BuildDependencyTieBreaker(object):
|
class BuildDependencyTieBreaker(object):
|
||||||
def __init__(self, apt):
|
def __init__(self, rootdir):
|
||||||
self.apt = apt
|
self.rootdir = rootdir
|
||||||
self._counts = None
|
self._counts = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.apt)
|
return "%s(%r)" % (type(self).__name__, self.rootdir)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_session(cls, session):
|
def from_session(cls, session):
|
||||||
return cls(LocalApt(session.location))
|
return cls(session.location)
|
||||||
|
|
||||||
def _count(self):
|
def _count(self):
|
||||||
counts = {}
|
counts = {}
|
||||||
with self.apt:
|
import apt_pkg
|
||||||
for source in self.apt.iter_sources():
|
|
||||||
for field in ['Build-Depends', 'Build-Depends-Indep',
|
apt_pkg.init()
|
||||||
'Build-Depends-Arch']:
|
apt_pkg.config.set("Dir", self.rootdir)
|
||||||
for r in PkgRelation.parse_relations(
|
apt_cache = apt_pkg.SourceRecords()
|
||||||
source.get(field, '')):
|
apt_cache.restart()
|
||||||
for p in r:
|
while apt_cache.step():
|
||||||
counts.setdefault(p['name'], 0)
|
try:
|
||||||
counts[p['name']] += 1
|
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
|
return counts
|
||||||
|
|
||||||
def __call__(self, reqs):
|
def __call__(self, reqs):
|
||||||
if self._counts is None:
|
if self._counts is None:
|
||||||
try:
|
self._counts = self._count()
|
||||||
self._counts = self._count()
|
|
||||||
except NoAptSources:
|
|
||||||
logging.warning(
|
|
||||||
"No 'deb-src' in sources.list, "
|
|
||||||
"unable to break build-depends")
|
|
||||||
return None
|
|
||||||
by_count = {}
|
by_count = {}
|
||||||
for req in reqs:
|
for req in reqs:
|
||||||
try:
|
try:
|
||||||
|
@ -83,5 +80,5 @@ if __name__ == "__main__":
|
||||||
parser.add_argument("req", nargs="+")
|
parser.add_argument("req", nargs="+")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
reqs = [AptRequirement.from_str(req) for req in args.req]
|
reqs = [AptRequirement.from_str(req) for req in args.req]
|
||||||
tie_breaker = BuildDependencyTieBreaker(LocalApt())
|
tie_breaker = BuildDependencyTieBreaker("/")
|
||||||
print(tie_breaker(reqs))
|
print(tie_breaker(reqs))
|
||||||
|
|
|
@ -17,13 +17,12 @@
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
import apt_pkg
|
import apt_pkg
|
||||||
import asyncio
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from debian.deb822 import Release
|
from debian.deb822 import Release
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import List, AsyncIterator
|
from typing import Iterator, List
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,15 +32,11 @@ from ..session import Session
|
||||||
|
|
||||||
class FileSearcher(object):
|
class FileSearcher(object):
|
||||||
def search_files(
|
def search_files(
|
||||||
self, path: str, regex: bool = False,
|
self, path: str, regex: bool = False, case_insensitive: bool = False
|
||||||
case_insensitive: bool = False) -> AsyncIterator[str]:
|
) -> Iterator[str]:
|
||||||
raise NotImplementedError(self.search_files)
|
raise NotImplementedError(self.search_files)
|
||||||
|
|
||||||
|
|
||||||
class AptFileAccessError(Exception):
|
|
||||||
"""Apt file access error."""
|
|
||||||
|
|
||||||
|
|
||||||
class ContentsFileNotFound(Exception):
|
class ContentsFileNotFound(Exception):
|
||||||
"""The contents file was not found."""
|
"""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)
|
response = load_url(release_url)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Unable to download %s or %s: %s", inrelease_url,
|
"Unable to download %s or %s: %s", inrelease_url, release_url, e
|
||||||
release_url, e
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -124,7 +118,7 @@ def _unwrap(f, ext):
|
||||||
|
|
||||||
|
|
||||||
def load_direct_url(url):
|
def load_direct_url(url):
|
||||||
from urllib.error import HTTPError, URLError
|
from urllib.error import HTTPError
|
||||||
from urllib.request import urlopen, Request
|
from urllib.request import urlopen, Request
|
||||||
|
|
||||||
for ext in [".xz", ".gz", ""]:
|
for ext in [".xz", ".gz", ""]:
|
||||||
|
@ -134,11 +128,7 @@ def load_direct_url(url):
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
if e.status == 404:
|
if e.status == 404:
|
||||||
continue
|
continue
|
||||||
raise AptFileAccessError(
|
raise
|
||||||
'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))
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise FileNotFoundError(url)
|
raise FileNotFoundError(url)
|
||||||
|
@ -197,7 +187,7 @@ class AptFileFileSearcher(FileSearcher):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_session(cls, session):
|
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)):
|
if not os.path.exists(session.external_path(cls.CACHE_IS_EMPTY_PATH)):
|
||||||
from .apt import AptManager
|
from .apt import AptManager
|
||||||
AptManager.from_session(session).install(['apt-file'])
|
AptManager.from_session(session).install(['apt-file'])
|
||||||
|
@ -205,7 +195,7 @@ class AptFileFileSearcher(FileSearcher):
|
||||||
session.check_call(['apt-file', 'update'], user='root')
|
session.check_call(['apt-file', 'update'], user='root')
|
||||||
return cls(session)
|
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 = []
|
args = []
|
||||||
if regex:
|
if regex:
|
||||||
args.append('-x')
|
args.append('-x')
|
||||||
|
@ -214,17 +204,15 @@ class AptFileFileSearcher(FileSearcher):
|
||||||
if case_insensitive:
|
if case_insensitive:
|
||||||
args.append('-i')
|
args.append('-i')
|
||||||
args.append(path)
|
args.append(path)
|
||||||
process = await asyncio.create_subprocess_exec(
|
try:
|
||||||
'/usr/bin/apt-file', 'search', *args,
|
output = self.session.check_output(['/usr/bin/apt-file', 'search'] + args)
|
||||||
stdout=asyncio.subprocess.PIPE)
|
except subprocess.CalledProcessError as e:
|
||||||
(output, error) = await process.communicate(input=None)
|
if e.returncode == 1:
|
||||||
if process.returncode == 1:
|
# No results
|
||||||
# No results
|
return
|
||||||
return
|
if e.returncode == 3:
|
||||||
elif process.returncode == 3:
|
raise Exception('apt-file cache is empty')
|
||||||
raise Exception('apt-file cache is empty')
|
raise
|
||||||
elif process.returncode != 0:
|
|
||||||
raise Exception("unexpected return code %d" % process.returncode)
|
|
||||||
|
|
||||||
for line in output.splitlines(False):
|
for line in output.splitlines(False):
|
||||||
pkg, path = line.split(b': ')
|
pkg, path = line.split(b': ')
|
||||||
|
@ -265,8 +253,7 @@ class RemoteContentsFileSearcher(FileSearcher):
|
||||||
return load_url_with_cache(url, cache_dirs)
|
return load_url_with_cache(url, cache_dirs)
|
||||||
|
|
||||||
urls = list(
|
urls = list(
|
||||||
contents_urls_from_sourceslist(
|
contents_urls_from_sourceslist(sl, get_build_architecture(), load_url)
|
||||||
sl, get_build_architecture(), load_url)
|
|
||||||
)
|
)
|
||||||
self._load_urls(urls, cache_dirs, 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)
|
return load_url_with_cache(url, cache_dirs)
|
||||||
|
|
||||||
urls = list(
|
urls = list(
|
||||||
contents_urls_from_sourceslist(
|
contents_urls_from_sourceslist(sl, get_build_architecture(), load_url)
|
||||||
sl, get_build_architecture(), load_url))
|
)
|
||||||
self._load_urls(urls, cache_dirs, load_url)
|
self._load_urls(urls, cache_dirs, load_url)
|
||||||
|
|
||||||
def _load_urls(self, urls, cache_dirs, load_url):
|
def _load_urls(self, urls, cache_dirs, load_url):
|
||||||
|
@ -299,16 +286,13 @@ class RemoteContentsFileSearcher(FileSearcher):
|
||||||
try:
|
try:
|
||||||
f = load_url(url)
|
f = load_url(url)
|
||||||
self.load_file(f, url)
|
self.load_file(f, url)
|
||||||
except ConnectionResetError:
|
|
||||||
logging.warning("Connection reset error retrieving %s", url)
|
|
||||||
# TODO(jelmer): Retry?
|
|
||||||
except ContentsFileNotFound:
|
except ContentsFileNotFound:
|
||||||
logging.warning("Unable to fetch contents file %s", url)
|
logging.warning("Unable to fetch contents file %s", url)
|
||||||
|
|
||||||
def __setitem__(self, path, package):
|
def __setitem__(self, path, package):
|
||||||
self._db[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")
|
path = path.lstrip("/").encode("utf-8", "surrogateescape")
|
||||||
if case_insensitive and not regex:
|
if case_insensitive and not regex:
|
||||||
regex = True
|
regex = True
|
||||||
|
@ -354,9 +338,9 @@ class GeneratedFileSearcher(FileSearcher):
|
||||||
(path, pkg) = line.strip().split(None, 1)
|
(path, pkg) = line.strip().split(None, 1)
|
||||||
self._db.append(path, pkg)
|
self._db.append(path, pkg)
|
||||||
|
|
||||||
async def search_files(
|
def search_files(
|
||||||
self, path: str, regex: bool = False,
|
self, path: str, regex: bool = False, case_insensitive: bool = False
|
||||||
case_insensitive: bool = False):
|
) -> Iterator[str]:
|
||||||
for p, pkg in self._db:
|
for p, pkg in self._db:
|
||||||
if regex:
|
if regex:
|
||||||
flags = 0
|
flags = 0
|
||||||
|
@ -387,17 +371,16 @@ GENERATED_FILE_SEARCHER = GeneratedFileSearcher(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def get_packages_for_paths(
|
def get_packages_for_paths(
|
||||||
paths: List[str],
|
paths: List[str],
|
||||||
searchers: List[FileSearcher],
|
searchers: List[FileSearcher],
|
||||||
regex: bool = False,
|
regex: bool = False,
|
||||||
case_insensitive: bool = False,
|
case_insensitive: bool = False,
|
||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
candidates: List[str] = list()
|
candidates: List[str] = list()
|
||||||
# TODO(jelmer): Combine these, perhaps by creating one gigantic regex?
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
for searcher in searchers:
|
for searcher in searchers:
|
||||||
async for pkg in searcher.search_files(
|
for pkg in searcher.search_files(
|
||||||
path, regex=regex, case_insensitive=case_insensitive
|
path, regex=regex, case_insensitive=case_insensitive
|
||||||
):
|
):
|
||||||
if pkg not in candidates:
|
if pkg not in candidates:
|
||||||
|
@ -410,10 +393,8 @@ def main(argv):
|
||||||
from ..session.plain import PlainSession
|
from ..session.plain import PlainSession
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument("path", help="Path to search for.", type=str, nargs="*")
|
||||||
"path", help="Path to search for.", type=str, nargs="*")
|
parser.add_argument("--regex", "-x", help="Search for regex.", action="store_true")
|
||||||
parser.add_argument(
|
|
||||||
"--regex", "-x", help="Search for regex.", action="store_true")
|
|
||||||
parser.add_argument("--debug", action="store_true")
|
parser.add_argument("--debug", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -422,14 +403,13 @@ def main(argv):
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
with PlainSession() as session:
|
main_searcher = get_apt_contents_file_searcher(PlainSession())
|
||||||
main_searcher = get_apt_contents_file_searcher(session)
|
main_searcher.load_local()
|
||||||
searchers = [main_searcher, GENERATED_FILE_SEARCHER]
|
searchers = [main_searcher, GENERATED_FILE_SEARCHER]
|
||||||
|
|
||||||
packages = asyncio.run(get_packages_for_paths(
|
packages = get_packages_for_paths(args.path, searchers=searchers, regex=args.regex)
|
||||||
args.path, searchers=searchers, regex=args.regex))
|
for package in packages:
|
||||||
for package in packages:
|
print(package)
|
||||||
print(package)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -22,10 +22,10 @@ __all__ = [
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import time
|
from typing import List, Set, Optional, Type
|
||||||
from typing import List, Set, Optional, Type, Tuple
|
|
||||||
|
|
||||||
from debian.deb822 import (
|
from debian.deb822 import (
|
||||||
Deb822,
|
Deb822,
|
||||||
|
@ -34,8 +34,6 @@ from debian.deb822 import (
|
||||||
|
|
||||||
from breezy.commit import PointlessCommit, NullCommitReporter
|
from breezy.commit import PointlessCommit, NullCommitReporter
|
||||||
from breezy.tree import Tree
|
from breezy.tree import Tree
|
||||||
from breezy.workingtree import WorkingTree
|
|
||||||
|
|
||||||
from debmutate.changelog import ChangelogEditor
|
from debmutate.changelog import ChangelogEditor
|
||||||
from debmutate.control import (
|
from debmutate.control import (
|
||||||
ensure_relation,
|
ensure_relation,
|
||||||
|
@ -52,7 +50,49 @@ from debmutate.reformatting import (
|
||||||
GeneratedFile,
|
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 (
|
from debmutate._rules import (
|
||||||
dh_invoke_add_with,
|
dh_invoke_add_with,
|
||||||
|
@ -73,21 +113,18 @@ from buildlog_consultant.common import (
|
||||||
)
|
)
|
||||||
from buildlog_consultant.sbuild import (
|
from buildlog_consultant.sbuild import (
|
||||||
DebcargoUnacceptablePredicate,
|
DebcargoUnacceptablePredicate,
|
||||||
DebcargoUnacceptableComparator,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from .build import (
|
from .build import (
|
||||||
DetailedDebianBuildFailure,
|
DetailedDebianBuildFailure,
|
||||||
UnidentifiedDebianBuildError,
|
UnidentifiedDebianBuildError,
|
||||||
)
|
)
|
||||||
from ..logs import rotate_logfile
|
|
||||||
from ..buildlog import problem_to_upstream_requirement
|
from ..buildlog import problem_to_upstream_requirement
|
||||||
from ..fix_build import BuildFixer, resolve_error
|
from ..fix_build import BuildFixer, resolve_error
|
||||||
from ..resolver.apt import (
|
from ..resolver.apt import (
|
||||||
AptRequirement,
|
AptRequirement,
|
||||||
)
|
)
|
||||||
from .apt import AptManager
|
from .build import attempt_build, DEFAULT_BUILDER
|
||||||
from .build import attempt_build, DEFAULT_BUILDER, BUILD_LOG_FILENAME
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_MAX_ITERATIONS = 10
|
DEFAULT_MAX_ITERATIONS = 10
|
||||||
|
@ -113,9 +150,7 @@ class DebianPackagingContext(object):
|
||||||
def abspath(self, *parts):
|
def abspath(self, *parts):
|
||||||
return self.tree.abspath(os.path.join(self.subpath, *parts))
|
return self.tree.abspath(os.path.join(self.subpath, *parts))
|
||||||
|
|
||||||
def commit(
|
def commit(self, summary: str, update_changelog: Optional[bool] = None) -> bool:
|
||||||
self, summary: str,
|
|
||||||
update_changelog: Optional[bool] = None) -> bool:
|
|
||||||
if update_changelog is None:
|
if update_changelog is None:
|
||||||
update_changelog = self.update_changelog
|
update_changelog = self.update_changelog
|
||||||
with self.tree.lock_write():
|
with self.tree.lock_write():
|
||||||
|
@ -179,11 +214,6 @@ def add_dependency(context, phase, requirement: AptRequirement):
|
||||||
return add_test_dependency(context, phase[1], requirement)
|
return add_test_dependency(context, phase[1], requirement)
|
||||||
elif phase[0] == "build":
|
elif phase[0] == "build":
|
||||||
return add_build_dependency(context, requirement)
|
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:
|
else:
|
||||||
logging.warning("Unknown phase %r", phase)
|
logging.warning("Unknown phase %r", phase)
|
||||||
return False
|
return False
|
||||||
|
@ -201,19 +231,16 @@ def add_build_dependency(context, requirement: AptRequirement):
|
||||||
raise CircularDependency(binary["Package"])
|
raise CircularDependency(binary["Package"])
|
||||||
for rel in requirement.relations:
|
for rel in requirement.relations:
|
||||||
updater.source["Build-Depends"] = ensure_relation(
|
updater.source["Build-Depends"] = ensure_relation(
|
||||||
updater.source.get("Build-Depends", ""),
|
updater.source.get("Build-Depends", ""), PkgRelation.str([rel])
|
||||||
PkgRelation.str([rel])
|
|
||||||
)
|
)
|
||||||
except FormattingUnpreservable as e:
|
except FormattingUnpreservable as e:
|
||||||
logging.info(
|
logging.info("Unable to edit %s in a way that preserves formatting.", e.path)
|
||||||
"Unable to edit %s in a way that preserves formatting.", e.path)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
desc = requirement.pkg_relation_str()
|
desc = requirement.pkg_relation_str()
|
||||||
|
|
||||||
if not updater.changed:
|
if not updater.changed:
|
||||||
logging.info(
|
logging.info("Giving up; dependency %s was already present.", desc)
|
||||||
"Giving up; build dependency %s was already present.", desc)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logging.info("Adding build dependency: %s", desc)
|
logging.info("Adding build dependency: %s", desc)
|
||||||
|
@ -245,18 +272,13 @@ def add_test_dependency(context, testname, requirement):
|
||||||
control.get("Depends", ""), PkgRelation.str([rel])
|
control.get("Depends", ""), PkgRelation.str([rel])
|
||||||
)
|
)
|
||||||
except FormattingUnpreservable as e:
|
except FormattingUnpreservable as e:
|
||||||
logging.info(
|
logging.info("Unable to edit %s in a way that preserves formatting.", e.path)
|
||||||
"Unable to edit %s in a way that preserves formatting.", e.path)
|
return False
|
||||||
|
if not updater.changed:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
desc = requirement.pkg_relation_str()
|
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)
|
logging.info("Adding dependency to test %s: %s", testname, desc)
|
||||||
return context.commit(
|
return context.commit(
|
||||||
"Add missing dependency for test %s on %s." % (testname, desc),
|
"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]:
|
def targeted_python_versions(tree: Tree, subpath: str) -> List[str]:
|
||||||
with tree.get_file(os.path.join(subpath, "debian/control")) as f:
|
with tree.get_file(os.path.join(subpath, "debian/control")) as f:
|
||||||
control = Deb822(f)
|
control = Deb822(f)
|
||||||
build_depends = PkgRelation.parse_relations(
|
build_depends = PkgRelation.parse_relations(control.get("Build-Depends", ""))
|
||||||
control.get("Build-Depends", ""))
|
|
||||||
all_build_deps: Set[str] = set()
|
all_build_deps: Set[str] = set()
|
||||||
for or_deps in build_depends:
|
for or_deps in build_depends:
|
||||||
all_build_deps.update(or_dep["name"] for or_dep in or_deps)
|
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
|
return True
|
||||||
if pkg.startswith("lib%s-" % python_version):
|
if pkg.startswith("lib%s-" % python_version):
|
||||||
return True
|
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 True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -316,8 +337,7 @@ def retry_apt_failure(error, phase, apt, context):
|
||||||
def enable_dh_autoreconf(context, phase):
|
def enable_dh_autoreconf(context, phase):
|
||||||
# Debhelper >= 10 depends on dh-autoreconf and enables autoreconf by
|
# Debhelper >= 10 depends on dh-autoreconf and enables autoreconf by
|
||||||
# default.
|
# default.
|
||||||
debhelper_compat_version = get_debhelper_compat_level(
|
debhelper_compat_version = get_debhelper_compat_level(context.tree.abspath("."))
|
||||||
context.tree.abspath("."))
|
|
||||||
if debhelper_compat_version is not None and debhelper_compat_version < 10:
|
if debhelper_compat_version is not None and debhelper_compat_version < 10:
|
||||||
|
|
||||||
def add_with_autoreconf(line, target):
|
def add_with_autoreconf(line, target):
|
||||||
|
@ -336,8 +356,9 @@ def enable_dh_autoreconf(context, phase):
|
||||||
|
|
||||||
|
|
||||||
def fix_missing_configure(error, phase, context):
|
def fix_missing_configure(error, phase, context):
|
||||||
if (not context.tree.has_filename("configure.ac")
|
if not context.tree.has_filename("configure.ac") and not context.tree.has_filename(
|
||||||
and not context.tree.has_filename("configure.in")):
|
"configure.in"
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return enable_dh_autoreconf(context, phase)
|
return enable_dh_autoreconf(context, phase)
|
||||||
|
@ -412,7 +433,7 @@ def fix_missing_makefile_pl(error, phase, context):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def debcargo_coerce_unacceptable_prerelease(error, phase, context):
|
def coerce_unacceptable_predicate(error, phase, context):
|
||||||
from debmutate.debcargo import DebcargoEditor
|
from debmutate.debcargo import DebcargoEditor
|
||||||
with DebcargoEditor(context.abspath('debian/debcargo.toml')) as editor:
|
with DebcargoEditor(context.abspath('debian/debcargo.toml')) as editor:
|
||||||
editor['allow_prerelease_deps'] = True
|
editor['allow_prerelease_deps'] = True
|
||||||
|
@ -440,8 +461,7 @@ class SimpleBuildFixer(BuildFixer):
|
||||||
|
|
||||||
|
|
||||||
class DependencyBuildFixer(BuildFixer):
|
class DependencyBuildFixer(BuildFixer):
|
||||||
def __init__(self, packaging_context, apt_resolver,
|
def __init__(self, packaging_context, apt_resolver, problem_cls: Type[Problem], fn):
|
||||||
problem_cls: Type[Problem], fn):
|
|
||||||
self.context = packaging_context
|
self.context = packaging_context
|
||||||
self.apt_resolver = apt_resolver
|
self.apt_resolver = apt_resolver
|
||||||
self._problem_cls = problem_cls
|
self._problem_cls = problem_cls
|
||||||
|
@ -461,47 +481,32 @@ class DependencyBuildFixer(BuildFixer):
|
||||||
return self._fn(problem, phase, self.apt_resolver, self.context)
|
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 [
|
return [
|
||||||
PgBuildExtOutOfDateControlFixer(packaging_context, session, apt),
|
PgBuildExtOutOfDateControlFixer(packaging_context, session, apt),
|
||||||
SimpleBuildFixer(
|
SimpleBuildFixer(packaging_context, MissingConfigure, fix_missing_configure),
|
||||||
packaging_context, MissingConfigure, fix_missing_configure),
|
|
||||||
SimpleBuildFixer(
|
SimpleBuildFixer(
|
||||||
packaging_context, MissingAutomakeInput, fix_missing_automake_input
|
packaging_context, MissingAutomakeInput, fix_missing_automake_input
|
||||||
),
|
),
|
||||||
SimpleBuildFixer(
|
SimpleBuildFixer(
|
||||||
packaging_context, MissingConfigStatusInput,
|
packaging_context, MissingConfigStatusInput, fix_missing_config_status_input
|
||||||
fix_missing_config_status_input
|
|
||||||
),
|
),
|
||||||
SimpleBuildFixer(
|
SimpleBuildFixer(packaging_context, MissingPerlFile, fix_missing_makefile_pl),
|
||||||
packaging_context, MissingPerlFile, fix_missing_makefile_pl),
|
SimpleBuildFixer(packaging_context, DebcargoUnacceptablePredicate, coerce_unacceptable_predicate),
|
||||||
SimpleBuildFixer(
|
|
||||||
packaging_context, DebcargoUnacceptablePredicate,
|
|
||||||
debcargo_coerce_unacceptable_prerelease),
|
|
||||||
SimpleBuildFixer(
|
|
||||||
packaging_context, DebcargoUnacceptableComparator,
|
|
||||||
debcargo_coerce_unacceptable_prerelease),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def apt_fixers(apt: AptManager, packaging_context,
|
def apt_fixers(apt, packaging_context) -> List[BuildFixer]:
|
||||||
dep_server_url: Optional[str] = None) -> List[BuildFixer]:
|
|
||||||
from ..resolver.apt import AptResolver
|
from ..resolver.apt import AptResolver
|
||||||
from .udd import popcon_tie_breaker
|
from .udd import popcon_tie_breaker
|
||||||
from .build_deps import BuildDependencyTieBreaker
|
from .build_deps import BuildDependencyTieBreaker
|
||||||
|
|
||||||
apt_tie_breakers = [
|
apt_tie_breakers = [
|
||||||
partial(python_tie_breaker, packaging_context.tree,
|
partial(python_tie_breaker, packaging_context.tree, packaging_context.subpath),
|
||||||
packaging_context.subpath),
|
|
||||||
BuildDependencyTieBreaker.from_session(apt.session),
|
BuildDependencyTieBreaker.from_session(apt.session),
|
||||||
popcon_tie_breaker,
|
popcon_tie_breaker,
|
||||||
]
|
]
|
||||||
resolver: AptResolver
|
resolver = AptResolver(apt, apt_tie_breakers)
|
||||||
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)
|
|
||||||
return [
|
return [
|
||||||
DependencyBuildFixer(
|
DependencyBuildFixer(
|
||||||
packaging_context, apt, AptFetchFailure, retry_apt_failure
|
packaging_context, apt, AptFetchFailure, retry_apt_failure
|
||||||
|
@ -510,49 +515,38 @@ def apt_fixers(apt: AptManager, packaging_context,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def default_fixers(
|
def default_fixers(local_tree, subpath, apt, committer=None, update_changelog=None):
|
||||||
local_tree: WorkingTree,
|
|
||||||
subpath: str, apt: AptManager,
|
|
||||||
committer: Optional[str] = None,
|
|
||||||
update_changelog: Optional[bool] = None,
|
|
||||||
dep_server_url: Optional[str] = None):
|
|
||||||
packaging_context = DebianPackagingContext(
|
packaging_context = DebianPackagingContext(
|
||||||
local_tree, subpath, committer, update_changelog,
|
local_tree, subpath, committer, update_changelog,
|
||||||
commit_reporter=NullCommitReporter()
|
commit_reporter=NullCommitReporter()
|
||||||
)
|
)
|
||||||
return (versioned_package_fixers(apt.session, packaging_context, apt)
|
return versioned_package_fixers(apt.session, packaging_context, apt) + apt_fixers(
|
||||||
+ apt_fixers(apt, packaging_context, dep_server_url))
|
apt, packaging_context
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_incrementally(
|
def build_incrementally(
|
||||||
local_tree: WorkingTree,
|
local_tree,
|
||||||
apt: AptManager,
|
apt,
|
||||||
suffix: str,
|
suffix,
|
||||||
build_suite: str,
|
build_suite,
|
||||||
output_directory: str,
|
output_directory,
|
||||||
build_command: str,
|
build_command,
|
||||||
build_changelog_entry,
|
build_changelog_entry,
|
||||||
committer: Optional[str] = None,
|
committer=None,
|
||||||
max_iterations: int = DEFAULT_MAX_ITERATIONS,
|
max_iterations=DEFAULT_MAX_ITERATIONS,
|
||||||
subpath: str = "",
|
subpath="",
|
||||||
source_date_epoch=None,
|
source_date_epoch=None,
|
||||||
update_changelog: bool = True,
|
update_changelog=True,
|
||||||
apt_repository: Optional[str] = None,
|
extra_repositories=None,
|
||||||
apt_repository_key: Optional[str] = None,
|
fixers=None
|
||||||
extra_repositories: Optional[List[str]] = None,
|
|
||||||
fixers: Optional[List[BuildFixer]] = None,
|
|
||||||
run_gbp_dch: Optional[bool] = None,
|
|
||||||
dep_server_url: Optional[str] = None,
|
|
||||||
):
|
):
|
||||||
fixed_errors: List[Tuple[Problem, str]] = []
|
fixed_errors = []
|
||||||
if fixers is None:
|
if fixers is None:
|
||||||
fixers = default_fixers(
|
fixers = default_fixers(
|
||||||
local_tree, subpath, apt, committer=committer,
|
local_tree, subpath, apt, committer=committer,
|
||||||
update_changelog=update_changelog,
|
update_changelog=update_changelog)
|
||||||
dep_server_url=dep_server_url)
|
|
||||||
logging.info("Using fixers: %r", fixers)
|
logging.info("Using fixers: %r", fixers)
|
||||||
if run_gbp_dch is None:
|
|
||||||
run_gbp_dch = (update_changelog is False)
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
return attempt_build(
|
return attempt_build(
|
||||||
|
@ -564,9 +558,7 @@ def build_incrementally(
|
||||||
build_changelog_entry,
|
build_changelog_entry,
|
||||||
subpath=subpath,
|
subpath=subpath,
|
||||||
source_date_epoch=source_date_epoch,
|
source_date_epoch=source_date_epoch,
|
||||||
run_gbp_dch=run_gbp_dch,
|
run_gbp_dch=(update_changelog is False),
|
||||||
apt_repository=apt_repository,
|
|
||||||
apt_repository_key=apt_repository_key,
|
|
||||||
extra_repositories=extra_repositories,
|
extra_repositories=extra_repositories,
|
||||||
)
|
)
|
||||||
except UnidentifiedDebianBuildError:
|
except UnidentifiedDebianBuildError:
|
||||||
|
@ -577,19 +569,15 @@ def build_incrementally(
|
||||||
logging.info("No relevant context, not making any changes.")
|
logging.info("No relevant context, not making any changes.")
|
||||||
raise
|
raise
|
||||||
if (e.error, e.phase) in fixed_errors:
|
if (e.error, e.phase) in fixed_errors:
|
||||||
logging.warning(
|
logging.warning("Error was still not fixed on second try. Giving up.")
|
||||||
"Error was still not fixed on second try. Giving up.")
|
|
||||||
raise
|
raise
|
||||||
if (max_iterations is not None
|
if max_iterations is not None and len(fixed_errors) > max_iterations:
|
||||||
and len(fixed_errors) > max_iterations):
|
logging.warning("Last fix did not address the issue. Giving up.")
|
||||||
logging.warning(
|
|
||||||
"Last fix did not address the issue. Giving up.")
|
|
||||||
raise
|
raise
|
||||||
reset_tree(local_tree, subpath=subpath)
|
reset_tree(local_tree, subpath=subpath)
|
||||||
try:
|
try:
|
||||||
if not resolve_error(e.error, e.phase, fixers):
|
if not resolve_error(e.error, e.phase, fixers):
|
||||||
logging.warning(
|
logging.warning("Failed to resolve error %r. Giving up.", e.error)
|
||||||
"Failed to resolve error %r. Giving up.", e.error)
|
|
||||||
raise
|
raise
|
||||||
except GeneratedFile:
|
except GeneratedFile:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
|
@ -600,71 +588,71 @@ def build_incrementally(
|
||||||
raise e
|
raise e
|
||||||
except CircularDependency:
|
except CircularDependency:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Unable to fix %r; it would introduce a circular "
|
"Unable to fix %r; it would introduce a circular " "dependency.",
|
||||||
"dependency.",
|
|
||||||
e.error,
|
e.error,
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
fixed_errors.append((e.error, e.phase))
|
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):
|
def main(argv=None):
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser("ognibuild.debian.fix_build")
|
parser = argparse.ArgumentParser("ognibuild.debian.fix_build")
|
||||||
modifications = parser.add_argument_group('Modifications')
|
parser.add_argument(
|
||||||
modifications.add_argument(
|
"--suffix", type=str, help="Suffix to use for test builds.", default="fixbuild1"
|
||||||
"--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"
|
"--suite", type=str, help="Suite to target.", default="unstable"
|
||||||
)
|
)
|
||||||
modifications.add_argument(
|
parser.add_argument(
|
||||||
"--committer", type=str, help="Committer string (name and email)",
|
"--output-directory", type=str, help="Output directory.", default=None
|
||||||
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",
|
"--no-update-changelog",
|
||||||
action="store_false",
|
action="store_false",
|
||||||
default=None,
|
default=None,
|
||||||
dest="update_changelog",
|
dest="update_changelog",
|
||||||
help="do not update the 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",
|
"--update-changelog",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
dest="update_changelog",
|
dest="update_changelog",
|
||||||
help="force updating of the changelog",
|
help="force updating of the changelog",
|
||||||
default=None,
|
default=None,
|
||||||
)
|
)
|
||||||
build_behaviour = parser.add_argument_group('Build Behaviour')
|
parser.add_argument("--schroot", type=str, help="chroot to use.")
|
||||||
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("--verbose", action="store_true", help="Be verbose")
|
parser.add_argument("--verbose", action="store_true", help="Be verbose")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
from breezy.workingtree import WorkingTree
|
||||||
import breezy.git # noqa: F401
|
import breezy.git # noqa: F401
|
||||||
import breezy.bzr # noqa: F401
|
import breezy.bzr # noqa: F401
|
||||||
|
from .apt import AptManager
|
||||||
from ..session.plain import PlainSession
|
from ..session.plain import PlainSession
|
||||||
from ..session.schroot import SchrootSession
|
from ..session.schroot import SchrootSession
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -681,10 +669,6 @@ def main(argv=None):
|
||||||
logging.info("Using output directory %s", output_directory)
|
logging.info("Using output directory %s", output_directory)
|
||||||
else:
|
else:
|
||||||
output_directory = args.output_directory
|
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(".")
|
tree = WorkingTree.open(".")
|
||||||
if args.schroot:
|
if args.schroot:
|
||||||
|
@ -708,7 +692,6 @@ def main(argv=None):
|
||||||
committer=args.committer,
|
committer=args.committer,
|
||||||
update_changelog=args.update_changelog,
|
update_changelog=args.update_changelog,
|
||||||
max_iterations=args.max_iterations,
|
max_iterations=args.max_iterations,
|
||||||
dep_server_url=args.dep_server_url,
|
|
||||||
)
|
)
|
||||||
except DetailedDebianBuildFailure as e:
|
except DetailedDebianBuildFailure as e:
|
||||||
if e.phase is None:
|
if e.phase is None:
|
||||||
|
@ -718,21 +701,6 @@ def main(argv=None):
|
||||||
else:
|
else:
|
||||||
phase = "%s (%s)" % (e.phase[0], e.phase[1])
|
phase = "%s (%s)" % (e.phase[0], e.phase[1])
|
||||||
logging.fatal("Error during %s: %s", phase, e.error)
|
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
|
return 1
|
||||||
except UnidentifiedDebianBuildError as e:
|
except UnidentifiedDebianBuildError as e:
|
||||||
if e.phase is None:
|
if e.phase is None:
|
||||||
|
|
|
@ -35,8 +35,7 @@ class UDD(object):
|
||||||
def get_most_popular(self, packages):
|
def get_most_popular(self, packages):
|
||||||
cursor = self._conn.cursor()
|
cursor = self._conn.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"SELECT package FROM popcon "
|
"SELECT package FROM popcon WHERE package IN %s ORDER BY insts DESC LIMIT 1",
|
||||||
"WHERE package IN %s ORDER BY insts DESC LIMIT 1",
|
|
||||||
(tuple(packages),),
|
(tuple(packages),),
|
||||||
)
|
)
|
||||||
return cursor.fetchone()[0]
|
return cursor.fetchone()[0]
|
||||||
|
@ -55,8 +54,7 @@ def popcon_tie_breaker(candidates):
|
||||||
names = {list(c.package_names())[0]: c for c in candidates}
|
names = {list(c.package_names())[0]: c for c in candidates}
|
||||||
winner = udd.get_most_popular(list(names.keys()))
|
winner = udd.get_most_popular(list(names.keys()))
|
||||||
if winner is None:
|
if winner is None:
|
||||||
logging.warning(
|
logging.warning("No relevant popcon information found, not ranking by popcon")
|
||||||
"No relevant popcon information found, not ranking by popcon")
|
|
||||||
return None
|
return None
|
||||||
logging.info("Picked winner using popcon")
|
logging.info("Picked winner using popcon")
|
||||||
return names[winner]
|
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__ = [
|
__all__ = [
|
||||||
"UnidentifiedError",
|
"UnidentifiedError",
|
||||||
"DetailedFailure",
|
"DetailedFailure",
|
||||||
"run_dist",
|
|
||||||
"create_dist_schroot",
|
|
||||||
"create_dist",
|
"create_dist",
|
||||||
"dist",
|
"create_dist_schroot",
|
||||||
]
|
]
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
from functools import partial
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from debian.deb822 import Deb822
|
||||||
|
|
||||||
from breezy.tree import Tree
|
from breezy.tree import Tree
|
||||||
from breezy.workingtree import WorkingTree
|
from breezy.workingtree import WorkingTree
|
||||||
|
|
||||||
|
@ -38,78 +37,16 @@ from buildlog_consultant.common import (
|
||||||
NoSpaceOnDevice,
|
NoSpaceOnDevice,
|
||||||
)
|
)
|
||||||
|
|
||||||
from debian.deb822 import Deb822
|
|
||||||
|
|
||||||
|
|
||||||
from . import DetailedFailure, UnidentifiedError
|
from . import DetailedFailure, UnidentifiedError
|
||||||
from .dist_catcher import DistNoTarball
|
from .dist_catcher import DistNoTarball
|
||||||
from .fix_build import iterate_with_build_fixers
|
|
||||||
from .logs import LogManager, NoLogManager
|
|
||||||
from .buildsystem import NoBuildToolsFound
|
from .buildsystem import NoBuildToolsFound
|
||||||
from .resolver import auto_resolver
|
from .resolver import auto_resolver
|
||||||
from .session import Session
|
from .session import Session
|
||||||
from .session.schroot import SchrootSession
|
from .session.schroot import SchrootSession
|
||||||
|
|
||||||
|
|
||||||
DIST_LOG_FILENAME = 'dist.log'
|
def run_dist(session, buildsystems, resolver, fixers, target_directory, quiet=False):
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
# Some things want to write to the user's home directory,
|
# Some things want to write to the user's home directory,
|
||||||
# e.g. pip caches in ~/.cache
|
# e.g. pip caches in ~/.cache
|
||||||
session.create_home()
|
session.create_home()
|
||||||
|
@ -117,34 +54,31 @@ def dist(session, export_directory, reldir, target_dir, log_manager, *,
|
||||||
logging.info('Using dependency resolver: %s', resolver)
|
logging.info('Using dependency resolver: %s', resolver)
|
||||||
|
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
filename = iterate_with_build_fixers(fixers, log_manager.wrap(
|
filename = buildsystem.dist(
|
||||||
partial(
|
session, resolver, fixers, target_directory, quiet=quiet
|
||||||
buildsystem.dist, session, resolver, target_dir,
|
)
|
||||||
quiet=quiet)))
|
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
raise NoBuildToolsFound()
|
raise NoBuildToolsFound()
|
||||||
|
|
||||||
|
|
||||||
# This is the function used by debianize()
|
|
||||||
def create_dist(
|
def create_dist(
|
||||||
session: Session,
|
session: Session,
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
target_dir: str,
|
target_dir: str,
|
||||||
include_controldir: bool = True,
|
include_controldir: bool = True,
|
||||||
subdir: Optional[str] = None,
|
subdir: Optional[str] = None,
|
||||||
log_manager: Optional[LogManager] = None,
|
cleanup: bool = False,
|
||||||
version: Optional[str] = None,
|
|
||||||
) -> Optional[str]:
|
) -> 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:
|
if subdir is None:
|
||||||
subdir = "package"
|
subdir = "package"
|
||||||
try:
|
try:
|
||||||
|
@ -156,11 +90,19 @@ def create_dist(
|
||||||
raise DetailedFailure(1, ["mkdtemp"], NoSpaceOnDevice())
|
raise DetailedFailure(1, ["mkdtemp"], NoSpaceOnDevice())
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if log_manager is None:
|
# TODO(jelmer): use scan_buildsystems to also look in subdirectories
|
||||||
log_manager = NoLogManager()
|
buildsystems = list(detect_buildsystems(export_directory))
|
||||||
|
resolver = auto_resolver(session)
|
||||||
|
fixers: List[BuildFixer] = [UnexpandedAutoconfMacroFixer(session, resolver)]
|
||||||
|
|
||||||
return dist(session, export_directory, reldir, target_dir,
|
fixers.append(InstallFixer(resolver))
|
||||||
log_manager=log_manager, version=version)
|
|
||||||
|
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(
|
def create_dist_schroot(
|
||||||
|
@ -171,35 +113,30 @@ def create_dist_schroot(
|
||||||
packaging_subpath: Optional[str] = None,
|
packaging_subpath: Optional[str] = None,
|
||||||
include_controldir: bool = True,
|
include_controldir: bool = True,
|
||||||
subdir: Optional[str] = None,
|
subdir: Optional[str] = None,
|
||||||
log_manager: Optional[LogManager] = None,
|
cleanup: bool = False,
|
||||||
) -> Optional[str]:
|
) -> 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:
|
with SchrootSession(chroot) as session:
|
||||||
if packaging_tree is not None:
|
if packaging_tree is not None:
|
||||||
from .debian import satisfy_build_deps
|
from .debian import satisfy_build_deps
|
||||||
|
|
||||||
satisfy_build_deps(session, packaging_tree, packaging_subpath)
|
satisfy_build_deps(session, packaging_tree, packaging_subpath)
|
||||||
return create_dist(
|
return create_dist(
|
||||||
session, tree, target_dir,
|
session,
|
||||||
include_controldir=include_controldir, subdir=subdir,
|
tree,
|
||||||
log_manager=log_manager)
|
target_dir,
|
||||||
|
include_controldir=include_controldir,
|
||||||
|
subdir=subdir,
|
||||||
|
cleanup=cleanup,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
import breezy.bzr # noqa: F401
|
import breezy.bzr # noqa: F401
|
||||||
import breezy.git # noqa: F401
|
import breezy.git # noqa: F401
|
||||||
from breezy.export import export
|
from breezy.export import export
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(argv)
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--chroot",
|
"--chroot",
|
||||||
default="unstable-amd64-sbuild",
|
default="unstable-amd64-sbuild",
|
||||||
|
@ -220,12 +157,8 @@ def main(argv=None):
|
||||||
"--target-directory", type=str, default="..", help="Target directory"
|
"--target-directory", type=str, default="..", help="Target directory"
|
||||||
)
|
)
|
||||||
parser.add_argument("--verbose", action="store_true", help="Be verbose")
|
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(
|
parser.add_argument(
|
||||||
"--include-controldir", action="store_true",
|
"--include-controldir", action="store_true", help="Clone rather than export."
|
||||||
help="Clone rather than export."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
@ -236,10 +169,6 @@ def main(argv=None):
|
||||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||||
|
|
||||||
tree = WorkingTree.open(args.directory)
|
tree = WorkingTree.open(args.directory)
|
||||||
|
|
||||||
packaging_tree: Optional[WorkingTree]
|
|
||||||
subdir: Optional[str]
|
|
||||||
|
|
||||||
if args.packaging_directory:
|
if args.packaging_directory:
|
||||||
packaging_tree = WorkingTree.open(args.packaging_directory)
|
packaging_tree = WorkingTree.open(args.packaging_directory)
|
||||||
with packaging_tree.lock_read():
|
with packaging_tree.lock_read():
|
||||||
|
@ -250,47 +179,30 @@ def main(argv=None):
|
||||||
packaging_tree = None
|
packaging_tree = None
|
||||||
subdir = 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)
|
export(tree, "dist.tar.gz", "tgz", None)
|
||||||
elif args.mode in ('auto', 'buildsystem'):
|
except NotImplementedError:
|
||||||
try:
|
logging.info(
|
||||||
ret = create_dist_schroot(
|
"Build system does not support dist tarball creation, "
|
||||||
tree,
|
"falling back to simple export."
|
||||||
subdir=subdir,
|
)
|
||||||
target_dir=os.path.abspath(args.target_directory),
|
export(tree, "dist.tar.gz", "tgz", None)
|
||||||
packaging_tree=packaging_tree,
|
except UnidentifiedError as e:
|
||||||
chroot=args.chroot,
|
logging.fatal("Unidentified error: %r", e.lines)
|
||||||
include_controldir=args.include_controldir,
|
except DetailedFailure as e:
|
||||||
)
|
logging.fatal("Identified error during dist creation: %s", e.error)
|
||||||
except NoBuildToolsFound:
|
except DistNoTarball:
|
||||||
if args.mode == 'buildsystem':
|
logging.fatal("dist operation did not create a tarball")
|
||||||
logging.fatal('No build tools found, unable to create tarball')
|
else:
|
||||||
return 1
|
logging.info("Created %s", ret)
|
||||||
logging.info(
|
sys.exit(0)
|
||||||
"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:]))
|
|
||||||
|
|
|
@ -54,8 +54,7 @@ class DistCatcher(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls, directory):
|
def default(cls, directory):
|
||||||
return cls(
|
return cls(
|
||||||
[os.path.join(directory, "dist"), directory,
|
[os.path.join(directory, "dist"), directory, os.path.join(directory, "..")]
|
||||||
os.path.join(directory, "..")]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
|
@ -88,23 +87,19 @@ class DistCatcher(object):
|
||||||
continue
|
continue
|
||||||
if len(possible_new) == 1:
|
if len(possible_new) == 1:
|
||||||
entry = possible_new[0]
|
entry = possible_new[0]
|
||||||
logging.info(
|
logging.info("Found new tarball %s in %s.", entry.name, directory)
|
||||||
"Found new tarball %s in %s.", entry.name, directory)
|
|
||||||
self.files.append(entry.path)
|
self.files.append(entry.path)
|
||||||
return entry.name
|
return entry.name
|
||||||
elif len(possible_new) > 1:
|
elif len(possible_new) > 1:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Found multiple tarballs %r in %s.", possible_new,
|
"Found multiple tarballs %r in %s.", possible_new, directory
|
||||||
directory
|
|
||||||
)
|
)
|
||||||
self.files.extend([entry.path for entry in possible_new])
|
self.files.extend([entry.path for entry in possible_new])
|
||||||
return possible_new[0].name
|
return possible_new[0].name
|
||||||
|
|
||||||
if len(possible_updated) == 1:
|
if len(possible_updated) == 1:
|
||||||
entry = possible_updated[0]
|
entry = possible_updated[0]
|
||||||
logging.info(
|
logging.info("Found updated tarball %s in %s.", entry.name, directory)
|
||||||
"Found updated tarball %s in %s.", entry.name,
|
|
||||||
directory)
|
|
||||||
self.files.append(entry.path)
|
self.files.append(entry.path)
|
||||||
return entry.name
|
return entry.name
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
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 import Problem
|
||||||
from buildlog_consultant.common import (
|
from buildlog_consultant.common import (
|
||||||
|
@ -29,14 +29,6 @@ from . import DetailedFailure, UnidentifiedError
|
||||||
from .session import Session, run_with_tee
|
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):
|
class BuildFixer(object):
|
||||||
"""Build fixer."""
|
"""Build fixer."""
|
||||||
|
|
||||||
|
@ -52,11 +44,7 @@ class BuildFixer(object):
|
||||||
return self._fix(problem, phase)
|
return self._fix(problem, phase)
|
||||||
|
|
||||||
|
|
||||||
def run_detecting_problems(
|
def run_detecting_problems(session: Session, args: List[str], check_success=None, **kwargs):
|
||||||
session: Session, args: List[str], check_success=None,
|
|
||||||
quiet=False, **kwargs) -> List[str]:
|
|
||||||
if not quiet:
|
|
||||||
logging.info('Running %r', args)
|
|
||||||
if check_success is None:
|
if check_success is None:
|
||||||
def check_success(retcode, contents):
|
def check_success(retcode, contents):
|
||||||
return (retcode == 0)
|
return (retcode == 0)
|
||||||
|
@ -75,26 +63,17 @@ def run_detecting_problems(
|
||||||
logging.warning("Build failed with unidentified error:")
|
logging.warning("Build failed with unidentified error:")
|
||||||
logging.warning("%s", match.line.rstrip("\n"))
|
logging.warning("%s", match.line.rstrip("\n"))
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning("Build failed and unable to find cause. Giving up.")
|
||||||
"Build failed and unable to find cause. Giving up.")
|
|
||||||
raise UnidentifiedError(retcode, args, lines, secondary=match)
|
raise UnidentifiedError(retcode, args, lines, secondary=match)
|
||||||
raise DetailedFailure(retcode, args, error)
|
raise DetailedFailure(retcode, args, error)
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar('T')
|
def iterate_with_build_fixers(fixers: List[BuildFixer], cb: Callable[[], Any]):
|
||||||
|
|
||||||
|
|
||||||
def iterate_with_build_fixers(
|
|
||||||
fixers: List[BuildFixer],
|
|
||||||
cb: Callable[[], T], limit=DEFAULT_LIMIT) -> T:
|
|
||||||
"""Call cb() until there are no more DetailedFailures we can fix.
|
"""Call cb() until there are no more DetailedFailures we can fix.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
fixers: List of fixers to use to resolve issues
|
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 = []
|
fixed_errors = []
|
||||||
while True:
|
while True:
|
||||||
to_resolve = []
|
to_resolve = []
|
||||||
|
@ -107,13 +86,9 @@ def iterate_with_build_fixers(
|
||||||
logging.info("Identified error: %r", f.error)
|
logging.info("Identified error: %r", f.error)
|
||||||
if f.error in fixed_errors:
|
if f.error in fixed_errors:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Failed to resolve error %r, it persisted. Giving up.",
|
"Failed to resolve error %r, it persisted. Giving up.", f.error
|
||||||
f.error
|
|
||||||
)
|
)
|
||||||
raise f
|
raise f
|
||||||
attempts += 1
|
|
||||||
if limit is not None and limit <= attempts:
|
|
||||||
raise FixerLimitReached(limit)
|
|
||||||
try:
|
try:
|
||||||
resolved = resolve_error(f.error, None, fixers=fixers)
|
resolved = resolve_error(f.error, None, fixers=fixers)
|
||||||
except DetailedFailure as n:
|
except DetailedFailure as n:
|
||||||
|
@ -125,25 +100,23 @@ def iterate_with_build_fixers(
|
||||||
else:
|
else:
|
||||||
if not resolved:
|
if not resolved:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Failed to find resolution for error %r. Giving up.",
|
"Failed to find resolution for error %r. Giving up.", f.error
|
||||||
f.error
|
|
||||||
)
|
)
|
||||||
raise f
|
raise f
|
||||||
fixed_errors.append(f.error)
|
fixed_errors.append(f.error)
|
||||||
|
|
||||||
|
|
||||||
def run_with_build_fixers(
|
def run_with_build_fixers(
|
||||||
fixers: Optional[List[BuildFixer]], session: Session, args: List[str],
|
session: Session, args: List[str], fixers: Optional[List[BuildFixer]], **kwargs
|
||||||
quiet: bool = False, **kwargs
|
):
|
||||||
) -> List[str]:
|
|
||||||
if fixers is None:
|
if fixers is None:
|
||||||
fixers = []
|
fixers = []
|
||||||
return iterate_with_build_fixers(
|
return iterate_with_build_fixers(
|
||||||
fixers,
|
fixers, partial(run_detecting_problems, session, args, **kwargs)
|
||||||
partial(run_detecting_problems, session, args, quiet=quiet, **kwargs))
|
)
|
||||||
|
|
||||||
|
|
||||||
def resolve_error(error, phase, fixers) -> bool:
|
def resolve_error(error, phase, fixers):
|
||||||
relevant_fixers = []
|
relevant_fixers = []
|
||||||
for fixer in fixers:
|
for fixer in fixers:
|
||||||
if fixer.can_fix(error):
|
if fixer.can_fix(error):
|
||||||
|
|
|
@ -21,10 +21,8 @@ from typing import Tuple
|
||||||
from buildlog_consultant import Problem
|
from buildlog_consultant import Problem
|
||||||
from buildlog_consultant.common import (
|
from buildlog_consultant.common import (
|
||||||
MissingGitIdentity,
|
MissingGitIdentity,
|
||||||
MissingGoSumEntry,
|
|
||||||
MissingSecretGpgKey,
|
MissingSecretGpgKey,
|
||||||
MissingAutoconfMacro,
|
MissingAutoconfMacro,
|
||||||
MissingGnulibDirectory,
|
|
||||||
)
|
)
|
||||||
from ognibuild.requirements import AutoconfMacroRequirement
|
from ognibuild.requirements import AutoconfMacroRequirement
|
||||||
from ognibuild.resolver import UnsatisfiedRequirements
|
from ognibuild.resolver import UnsatisfiedRequirements
|
||||||
|
@ -32,18 +30,6 @@ from ognibuild.resolver import UnsatisfiedRequirements
|
||||||
from .fix_build import BuildFixer
|
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):
|
class GitIdentityFixer(BuildFixer):
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
@ -91,26 +77,6 @@ Passphrase: ""
|
||||||
return False
|
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):
|
class UnexpandedAutoconfMacroFixer(BuildFixer):
|
||||||
def __init__(self, session, resolver):
|
def __init__(self, session, resolver):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
|
@ -21,13 +21,11 @@ def run_info(session, buildsystems, fixers=None):
|
||||||
print("%r:" % buildsystem)
|
print("%r:" % buildsystem)
|
||||||
deps = {}
|
deps = {}
|
||||||
try:
|
try:
|
||||||
for kind, dep in buildsystem.get_declared_dependencies(
|
for kind, dep in buildsystem.get_declared_dependencies(session, fixers=fixers):
|
||||||
session, fixers=fixers):
|
|
||||||
deps.setdefault(kind, []).append(dep)
|
deps.setdefault(kind, []).append(dep)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
print(
|
print(
|
||||||
"\tUnable to detect declared dependencies for this type of "
|
"\tUnable to detect declared dependencies for this type of build system"
|
||||||
"build system"
|
|
||||||
)
|
)
|
||||||
if deps:
|
if deps:
|
||||||
print("\tDeclared dependencies:")
|
print("\tDeclared dependencies:")
|
||||||
|
@ -37,11 +35,9 @@ def run_info(session, buildsystems, fixers=None):
|
||||||
print("\t\t\t%s" % dep)
|
print("\t\t\t%s" % dep)
|
||||||
print("")
|
print("")
|
||||||
try:
|
try:
|
||||||
outputs = list(buildsystem.get_declared_outputs(
|
outputs = list(buildsystem.get_declared_outputs(session, fixers=fixers))
|
||||||
session, fixers=fixers))
|
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
print("\tUnable to detect declared outputs for this type of "
|
print("\tUnable to detect declared outputs for this type of build system")
|
||||||
"build system")
|
|
||||||
outputs = []
|
outputs = []
|
||||||
if outputs:
|
if outputs:
|
||||||
print("\tDeclared outputs:")
|
print("\tDeclared outputs:")
|
||||||
|
|
|
@ -15,34 +15,21 @@
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# 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 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):
|
||||||
def run_install(
|
|
||||||
session, buildsystems, resolver, fixers, *, user: bool = False,
|
|
||||||
prefix: Optional[str] = None, log_manager=None):
|
|
||||||
# Some things want to write to the user's home directory,
|
# Some things want to write to the user's home directory,
|
||||||
# e.g. pip caches in ~/.cache
|
# e.g. pip caches in ~/.cache
|
||||||
session.create_home()
|
session.create_home()
|
||||||
|
|
||||||
if log_manager is None:
|
|
||||||
log_manager = NoLogManager()
|
|
||||||
|
|
||||||
install_target = InstallTarget()
|
install_target = InstallTarget()
|
||||||
install_target.user = user
|
install_target.user = user
|
||||||
install_target.prefix = prefix
|
install_target.prefix = prefix
|
||||||
|
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
iterate_with_build_fixers(
|
buildsystem.install(session, resolver, fixers, install_target)
|
||||||
fixers,
|
|
||||||
log_manager.wrap(
|
|
||||||
partial(buildsystem.install, session, resolver,
|
|
||||||
install_target)))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NoBuildToolsFound()
|
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):
|
class PythonPackageRequirement(Requirement):
|
||||||
|
|
||||||
family = "python-package"
|
|
||||||
|
|
||||||
package: str
|
package: str
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, package, python_version=None, specs=None, minimum_version=None):
|
||||||
self, package, python_version=None, specs=None,
|
super(PythonPackageRequirement, self).__init__("python-package")
|
||||||
minimum_version=None):
|
|
||||||
self.package = package
|
self.package = package
|
||||||
self.python_version = python_version
|
self.python_version = python_version
|
||||||
|
if minimum_version is not None:
|
||||||
|
specs = [(">=", minimum_version)]
|
||||||
if specs is None:
|
if specs is None:
|
||||||
specs = []
|
specs = []
|
||||||
if minimum_version is not None:
|
|
||||||
specs.append((">=", minimum_version))
|
|
||||||
self.specs = specs
|
self.specs = specs
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -56,29 +53,11 @@ class PythonPackageRequirement(Requirement):
|
||||||
return "python package: %s" % (self.package,)
|
return "python package: %s" % (self.package,)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_requirement_str(cls, text, python_version=None):
|
def from_requirement_str(cls, text):
|
||||||
from requirements.requirement import Requirement
|
from requirements.requirement import Requirement
|
||||||
|
|
||||||
req = Requirement.parse(text)
|
req = Requirement.parse(text)
|
||||||
return cls(
|
return cls(package=req.name, specs=req.specs)
|
||||||
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()
|
|
||||||
|
|
||||||
def met(self, session):
|
def met(self, session):
|
||||||
if self.python_version == "cpython3":
|
if self.python_version == "cpython3":
|
||||||
|
@ -95,8 +74,7 @@ class PythonPackageRequirement(Requirement):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
text = self.package + ",".join(["".join(spec) for spec in self.specs])
|
text = self.package + ",".join(["".join(spec) for spec in self.specs])
|
||||||
p = session.Popen(
|
p = session.Popen(
|
||||||
[cmd, "-c",
|
[cmd, "-c", "import pkg_resources; pkg_resources.require(%r)" % text],
|
||||||
"import pkg_resources; pkg_resources.require(%r)" % text],
|
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
)
|
)
|
||||||
|
@ -104,33 +82,16 @@ class PythonPackageRequirement(Requirement):
|
||||||
return p.returncode == 0
|
return p.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
Requirement.register_json(PythonPackageRequirement)
|
|
||||||
|
|
||||||
|
|
||||||
class LatexPackageRequirement(Requirement):
|
class LatexPackageRequirement(Requirement):
|
||||||
|
|
||||||
family = "latex-package"
|
|
||||||
|
|
||||||
def __init__(self, package: str):
|
def __init__(self, package: str):
|
||||||
self.package = package
|
self.package = package
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.package)
|
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):
|
class PhpPackageRequirement(Requirement):
|
||||||
|
|
||||||
family = "php-package"
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
package: str,
|
package: str,
|
||||||
|
@ -143,13 +104,6 @@ class PhpPackageRequirement(Requirement):
|
||||||
self.min_version = min_version
|
self.min_version = min_version
|
||||||
self.max_version = max_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):
|
def __repr__(self):
|
||||||
return "%s(%r, %r, %r, %r)" % (
|
return "%s(%r, %r, %r, %r)" % (
|
||||||
type(self).__name__,
|
type(self).__name__,
|
||||||
|
@ -160,24 +114,14 @@ class PhpPackageRequirement(Requirement):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
Requirement.register_json(PhpPackageRequirement)
|
|
||||||
|
|
||||||
|
|
||||||
class BinaryRequirement(Requirement):
|
class BinaryRequirement(Requirement):
|
||||||
|
|
||||||
family = "binary"
|
|
||||||
binary_name: str
|
binary_name: str
|
||||||
|
|
||||||
def __init__(self, binary_name):
|
def __init__(self, binary_name):
|
||||||
|
super(BinaryRequirement, self).__init__("binary")
|
||||||
self.binary_name = binary_name
|
self.binary_name = binary_name
|
||||||
|
|
||||||
def _json(self):
|
|
||||||
return self.binary_name
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _from_json(cls, js):
|
|
||||||
return cls(js)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.binary_name)
|
return "%s(%r)" % (type(self).__name__, self.binary_name)
|
||||||
|
|
||||||
|
@ -191,54 +135,14 @@ class BinaryRequirement(Requirement):
|
||||||
return p.returncode == 0
|
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):
|
class PerlModuleRequirement(Requirement):
|
||||||
|
|
||||||
module: str
|
module: str
|
||||||
filename: Optional[str]
|
filename: Optional[str]
|
||||||
inc: Optional[List[str]]
|
inc: Optional[List[str]]
|
||||||
family = "perl-module"
|
|
||||||
|
|
||||||
def __init__(self, module, filename=None, inc=None):
|
def __init__(self, module, filename=None, inc=None):
|
||||||
|
super(PerlModuleRequirement, self).__init__("perl-module")
|
||||||
self.module = module
|
self.module = module
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.inc = inc
|
self.inc = inc
|
||||||
|
@ -254,10 +158,10 @@ class PerlModuleRequirement(Requirement):
|
||||||
class VagueDependencyRequirement(Requirement):
|
class VagueDependencyRequirement(Requirement):
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
family = "vague"
|
|
||||||
minimum_version: Optional[str] = None
|
minimum_version: Optional[str] = None
|
||||||
|
|
||||||
def __init__(self, name, minimum_version=None):
|
def __init__(self, name, minimum_version=None):
|
||||||
|
super(VagueDependencyRequirement, self).__init__("vague")
|
||||||
self.name = name
|
self.name = name
|
||||||
self.minimum_version = minimum_version
|
self.minimum_version = minimum_version
|
||||||
|
|
||||||
|
@ -265,26 +169,19 @@ class VagueDependencyRequirement(Requirement):
|
||||||
if " " not in self.name:
|
if " " not in self.name:
|
||||||
yield BinaryRequirement(self.name)
|
yield BinaryRequirement(self.name)
|
||||||
yield LibraryRequirement(self.name)
|
yield LibraryRequirement(self.name)
|
||||||
yield PkgConfigRequirement(
|
yield PkgConfigRequirement(self.name, minimum_version=self.minimum_version)
|
||||||
self.name, minimum_version=self.minimum_version)
|
|
||||||
if self.name.lower() != self.name:
|
if self.name.lower() != self.name:
|
||||||
yield BinaryRequirement(self.name.lower())
|
yield BinaryRequirement(self.name.lower())
|
||||||
yield LibraryRequirement(self.name.lower())
|
yield LibraryRequirement(self.name.lower())
|
||||||
yield PkgConfigRequirement(
|
yield PkgConfigRequirement(self.name.lower(), minimum_version=self.minimum_version)
|
||||||
self.name.lower(), minimum_version=self.minimum_version)
|
from .resolver.apt import AptRequirement
|
||||||
try:
|
|
||||||
from .resolver.apt import AptRequirement
|
yield AptRequirement.simple(self.name.lower(), minimum_version=self.minimum_version)
|
||||||
except ModuleNotFoundError:
|
if self.name.lower().startswith('lib'):
|
||||||
pass
|
devname = '%s-dev' % self.name.lower()
|
||||||
else:
|
else:
|
||||||
yield AptRequirement.simple(
|
devname = 'lib%s-dev' % self.name.lower()
|
||||||
self.name.lower(), minimum_version=self.minimum_version)
|
yield AptRequirement.simple(devname, 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)
|
|
||||||
|
|
||||||
def met(self, session):
|
def met(self, session):
|
||||||
for x in self.expand():
|
for x in self.expand():
|
||||||
|
@ -295,36 +192,19 @@ class VagueDependencyRequirement(Requirement):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.name)
|
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):
|
class NodePackageRequirement(Requirement):
|
||||||
|
|
||||||
package: str
|
package: str
|
||||||
family = "npm-package"
|
|
||||||
|
|
||||||
def __init__(self, package):
|
def __init__(self, package):
|
||||||
|
super(NodePackageRequirement, self).__init__("npm-package")
|
||||||
self.package = package
|
self.package = package
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.package)
|
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):
|
class PerlPreDeclaredRequirement(Requirement):
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
|
@ -347,9 +227,8 @@ class PerlPreDeclaredRequirement(Requirement):
|
||||||
'auto_set_bugtracker': 'Module::Install::Bugtracker',
|
'auto_set_bugtracker': 'Module::Install::Bugtracker',
|
||||||
}
|
}
|
||||||
|
|
||||||
family = "perl-predeclared"
|
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
|
super(PerlPreDeclaredRequirement, self).__init__("perl-predeclared")
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def lookup_module(self):
|
def lookup_module(self):
|
||||||
|
@ -363,9 +242,9 @@ class PerlPreDeclaredRequirement(Requirement):
|
||||||
class NodeModuleRequirement(Requirement):
|
class NodeModuleRequirement(Requirement):
|
||||||
|
|
||||||
module: str
|
module: str
|
||||||
family = "npm-module"
|
|
||||||
|
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
|
super(NodeModuleRequirement, self).__init__("npm-module")
|
||||||
self.module = module
|
self.module = module
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -376,45 +255,41 @@ class CargoCrateRequirement(Requirement):
|
||||||
|
|
||||||
crate: str
|
crate: str
|
||||||
features: Set[str]
|
features: Set[str]
|
||||||
api_version: Optional[str]
|
version: Optional[str]
|
||||||
minimum_version: Optional[str]
|
|
||||||
family = "cargo-crate"
|
|
||||||
|
|
||||||
def __init__(self, crate, features=None, api_version=None,
|
def __init__(self, crate, features=None, version=None):
|
||||||
minimum_version=None):
|
super(CargoCrateRequirement, self).__init__("cargo-crate")
|
||||||
self.crate = crate
|
self.crate = crate
|
||||||
if features is None:
|
if features is None:
|
||||||
features = set()
|
features = set()
|
||||||
self.features = features
|
self.features = features
|
||||||
self.api_version = api_version
|
self.version = version
|
||||||
self.minimum_version = minimum_version
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r, features=%r, api_version=%r, minimum_version=%r)" % (
|
return "%s(%r, features=%r, version=%r)" % (
|
||||||
type(self).__name__,
|
type(self).__name__,
|
||||||
self.crate,
|
self.crate,
|
||||||
self.features,
|
self.features,
|
||||||
self.api_version,
|
self.version,
|
||||||
self.minimum_version,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
ret = "cargo crate: %s %s" % (
|
|
||||||
self.crate,
|
|
||||||
self.api_version or "")
|
|
||||||
if self.features:
|
if self.features:
|
||||||
ret += " (%s)" % (", ".join(sorted(self.features)))
|
return "cargo crate: %s %s (%s)" % (
|
||||||
if self.minimum_version:
|
self.crate,
|
||||||
ret += " (>= %s)" % self.minimum_version
|
self.version or "",
|
||||||
return ret
|
", ".join(sorted(self.features)),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return "cargo crate: %s %s" % (self.crate, self.version or "")
|
||||||
|
|
||||||
|
|
||||||
class PkgConfigRequirement(Requirement):
|
class PkgConfigRequirement(Requirement):
|
||||||
|
|
||||||
module: str
|
module: str
|
||||||
family = "pkg-config"
|
|
||||||
|
|
||||||
def __init__(self, module, minimum_version=None):
|
def __init__(self, module, minimum_version=None):
|
||||||
|
super(PkgConfigRequirement, self).__init__("pkg-config")
|
||||||
self.module = module
|
self.module = module
|
||||||
self.minimum_version = minimum_version
|
self.minimum_version = minimum_version
|
||||||
|
|
||||||
|
@ -426,9 +301,9 @@ class PkgConfigRequirement(Requirement):
|
||||||
class PathRequirement(Requirement):
|
class PathRequirement(Requirement):
|
||||||
|
|
||||||
path: str
|
path: str
|
||||||
family = "path"
|
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
|
super(PathRequirement, self).__init__("path")
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -438,9 +313,9 @@ class PathRequirement(Requirement):
|
||||||
class CHeaderRequirement(Requirement):
|
class CHeaderRequirement(Requirement):
|
||||||
|
|
||||||
header: str
|
header: str
|
||||||
family = "c-header"
|
|
||||||
|
|
||||||
def __init__(self, header):
|
def __init__(self, header):
|
||||||
|
super(CHeaderRequirement, self).__init__("c-header")
|
||||||
self.header = header
|
self.header = header
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -448,15 +323,16 @@ class CHeaderRequirement(Requirement):
|
||||||
|
|
||||||
|
|
||||||
class JavaScriptRuntimeRequirement(Requirement):
|
class JavaScriptRuntimeRequirement(Requirement):
|
||||||
family = "javascript-runtime"
|
def __init__(self):
|
||||||
|
super(JavaScriptRuntimeRequirement, self).__init__("javascript-runtime")
|
||||||
|
|
||||||
|
|
||||||
class ValaPackageRequirement(Requirement):
|
class ValaPackageRequirement(Requirement):
|
||||||
|
|
||||||
package: str
|
package: str
|
||||||
family = "vala"
|
|
||||||
|
|
||||||
def __init__(self, package: str):
|
def __init__(self, package: str):
|
||||||
|
super(ValaPackageRequirement, self).__init__("vala")
|
||||||
self.package = package
|
self.package = package
|
||||||
|
|
||||||
|
|
||||||
|
@ -464,9 +340,9 @@ class RubyGemRequirement(Requirement):
|
||||||
|
|
||||||
gem: str
|
gem: str
|
||||||
minimum_version: Optional[str]
|
minimum_version: Optional[str]
|
||||||
family = "gem"
|
|
||||||
|
|
||||||
def __init__(self, gem: str, minimum_version: Optional[str]):
|
def __init__(self, gem: str, minimum_version: Optional[str]):
|
||||||
|
super(RubyGemRequirement, self).__init__("gem")
|
||||||
self.gem = gem
|
self.gem = gem
|
||||||
self.minimum_version = minimum_version
|
self.minimum_version = minimum_version
|
||||||
|
|
||||||
|
@ -475,16 +351,12 @@ class GoPackageRequirement(Requirement):
|
||||||
|
|
||||||
package: str
|
package: str
|
||||||
version: Optional[str]
|
version: Optional[str]
|
||||||
family = "go-package"
|
|
||||||
|
|
||||||
def __init__(self, package: str, version: Optional[str] = None):
|
def __init__(self, package: str, version: Optional[str] = None):
|
||||||
|
super(GoPackageRequirement, self).__init__("go-package")
|
||||||
self.package = package
|
self.package = package
|
||||||
self.version = version
|
self.version = version
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "%s(%r, version=%r)" % (
|
|
||||||
type(self).__name__, self.package, self.version)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.version:
|
if self.version:
|
||||||
return "go package: %s (= %s)" % (self.package, self.version)
|
return "go package: %s (= %s)" % (self.package, self.version)
|
||||||
|
@ -494,9 +366,9 @@ class GoPackageRequirement(Requirement):
|
||||||
class GoRequirement(Requirement):
|
class GoRequirement(Requirement):
|
||||||
|
|
||||||
version: Optional[str]
|
version: Optional[str]
|
||||||
family = "go"
|
|
||||||
|
|
||||||
def __init__(self, version: Optional[str] = None):
|
def __init__(self, version: Optional[str] = None):
|
||||||
|
super(GoRequirement, self).__init__("go")
|
||||||
self.version = version
|
self.version = version
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -506,18 +378,18 @@ class GoRequirement(Requirement):
|
||||||
class DhAddonRequirement(Requirement):
|
class DhAddonRequirement(Requirement):
|
||||||
|
|
||||||
path: str
|
path: str
|
||||||
family = "dh-addon"
|
|
||||||
|
|
||||||
def __init__(self, path: str):
|
def __init__(self, path: str):
|
||||||
|
super(DhAddonRequirement, self).__init__("dh-addon")
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
|
||||||
class PhpClassRequirement(Requirement):
|
class PhpClassRequirement(Requirement):
|
||||||
|
|
||||||
php_class: str
|
php_class: str
|
||||||
family = "php-class"
|
|
||||||
|
|
||||||
def __init__(self, php_class: str):
|
def __init__(self, php_class: str):
|
||||||
|
super(PhpClassRequirement, self).__init__("php-class")
|
||||||
self.php_class = php_class
|
self.php_class = php_class
|
||||||
|
|
||||||
|
|
||||||
|
@ -525,9 +397,9 @@ class RPackageRequirement(Requirement):
|
||||||
|
|
||||||
package: str
|
package: str
|
||||||
minimum_version: Optional[str]
|
minimum_version: Optional[str]
|
||||||
family = "r-package"
|
|
||||||
|
|
||||||
def __init__(self, package: str, minimum_version: Optional[str] = None):
|
def __init__(self, package: str, minimum_version: Optional[str] = None):
|
||||||
|
super(RPackageRequirement, self).__init__("r-package")
|
||||||
self.package = package
|
self.package = package
|
||||||
self.minimum_version = minimum_version
|
self.minimum_version = minimum_version
|
||||||
|
|
||||||
|
@ -540,8 +412,7 @@ class RPackageRequirement(Requirement):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.minimum_version:
|
if self.minimum_version:
|
||||||
return "R package: %s (>= %s)" % (
|
return "R package: %s (>= %s)" % (self.package, self.minimum_version)
|
||||||
self.package, self.minimum_version)
|
|
||||||
else:
|
else:
|
||||||
return "R package: %s" % (self.package,)
|
return "R package: %s" % (self.package,)
|
||||||
|
|
||||||
|
@ -561,9 +432,9 @@ class OctavePackageRequirement(Requirement):
|
||||||
|
|
||||||
package: str
|
package: str
|
||||||
minimum_version: Optional[str]
|
minimum_version: Optional[str]
|
||||||
family = "octave-package"
|
|
||||||
|
|
||||||
def __init__(self, package: str, minimum_version: Optional[str] = None):
|
def __init__(self, package: str, minimum_version: Optional[str] = None):
|
||||||
|
super(OctavePackageRequirement, self).__init__("octave-package")
|
||||||
self.package = package
|
self.package = package
|
||||||
self.minimum_version = minimum_version
|
self.minimum_version = minimum_version
|
||||||
|
|
||||||
|
@ -576,8 +447,7 @@ class OctavePackageRequirement(Requirement):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.minimum_version:
|
if self.minimum_version:
|
||||||
return "Octave package: %s (>= %s)" % (
|
return "Octave package: %s (>= %s)" % (self.package, self.minimum_version)
|
||||||
self.package, self.minimum_version)
|
|
||||||
else:
|
else:
|
||||||
return "Octave package: %s" % (self.package,)
|
return "Octave package: %s" % (self.package,)
|
||||||
|
|
||||||
|
@ -596,9 +466,9 @@ class OctavePackageRequirement(Requirement):
|
||||||
class LibraryRequirement(Requirement):
|
class LibraryRequirement(Requirement):
|
||||||
|
|
||||||
library: str
|
library: str
|
||||||
family = "lib"
|
|
||||||
|
|
||||||
def __init__(self, library: str):
|
def __init__(self, library: str):
|
||||||
|
super(LibraryRequirement, self).__init__("lib")
|
||||||
self.library = library
|
self.library = library
|
||||||
|
|
||||||
|
|
||||||
|
@ -606,9 +476,9 @@ class StaticLibraryRequirement(Requirement):
|
||||||
|
|
||||||
library: str
|
library: str
|
||||||
filename: str
|
filename: str
|
||||||
family = "static-lib"
|
|
||||||
|
|
||||||
def __init__(self, library: str, filename: str):
|
def __init__(self, library: str, filename: str):
|
||||||
|
super(StaticLibraryRequirement, self).__init__("static-lib")
|
||||||
self.library = library
|
self.library = library
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
|
@ -616,18 +486,18 @@ class StaticLibraryRequirement(Requirement):
|
||||||
class RubyFileRequirement(Requirement):
|
class RubyFileRequirement(Requirement):
|
||||||
|
|
||||||
filename: str
|
filename: str
|
||||||
family = "ruby-file"
|
|
||||||
|
|
||||||
def __init__(self, filename: str):
|
def __init__(self, filename: str):
|
||||||
|
super(RubyFileRequirement, self).__init__("ruby-file")
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
|
|
||||||
class XmlEntityRequirement(Requirement):
|
class XmlEntityRequirement(Requirement):
|
||||||
|
|
||||||
url: str
|
url: str
|
||||||
family = "xml-entity"
|
|
||||||
|
|
||||||
def __init__(self, url: str):
|
def __init__(self, url: str):
|
||||||
|
super(XmlEntityRequirement, self).__init__("xml-entity")
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
|
|
||||||
|
@ -635,9 +505,9 @@ class SprocketsFileRequirement(Requirement):
|
||||||
|
|
||||||
content_type: str
|
content_type: str
|
||||||
name: str
|
name: str
|
||||||
family = "sprockets-file"
|
|
||||||
|
|
||||||
def __init__(self, content_type: str, name: str):
|
def __init__(self, content_type: str, name: str):
|
||||||
|
super(SprocketsFileRequirement, self).__init__("sprockets-file")
|
||||||
self.content_type = content_type
|
self.content_type = content_type
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
@ -645,29 +515,27 @@ class SprocketsFileRequirement(Requirement):
|
||||||
class JavaClassRequirement(Requirement):
|
class JavaClassRequirement(Requirement):
|
||||||
|
|
||||||
classname: str
|
classname: str
|
||||||
family = "java-class"
|
|
||||||
|
|
||||||
def __init__(self, classname: str):
|
def __init__(self, classname: str):
|
||||||
|
super(JavaClassRequirement, self).__init__("java-class")
|
||||||
self.classname = classname
|
self.classname = classname
|
||||||
|
|
||||||
|
|
||||||
class CMakefileRequirement(Requirement):
|
class CMakefileRequirement(Requirement):
|
||||||
|
|
||||||
filename: str
|
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.filename = filename
|
||||||
self.version = version
|
|
||||||
|
|
||||||
|
|
||||||
class HaskellPackageRequirement(Requirement):
|
class HaskellPackageRequirement(Requirement):
|
||||||
|
|
||||||
package: str
|
package: str
|
||||||
family = "haskell-package"
|
|
||||||
|
|
||||||
def __init__(self, package: str, specs=None):
|
def __init__(self, package: str, specs=None):
|
||||||
|
super(HaskellPackageRequirement, self).__init__("haskell-package")
|
||||||
self.package = package
|
self.package = package
|
||||||
self.specs = specs
|
self.specs = specs
|
||||||
|
|
||||||
|
@ -683,9 +551,9 @@ class MavenArtifactRequirement(Requirement):
|
||||||
artifact_id: str
|
artifact_id: str
|
||||||
version: Optional[str]
|
version: Optional[str]
|
||||||
kind: Optional[str]
|
kind: Optional[str]
|
||||||
family = "maven-artifact"
|
|
||||||
|
|
||||||
def __init__(self, group_id, artifact_id, version=None, kind=None):
|
def __init__(self, group_id, artifact_id, version=None, kind=None):
|
||||||
|
super(MavenArtifactRequirement, self).__init__("maven-artifact")
|
||||||
self.group_id = group_id
|
self.group_id = group_id
|
||||||
self.artifact_id = artifact_id
|
self.artifact_id = artifact_id
|
||||||
self.version = version
|
self.version = version
|
||||||
|
@ -698,11 +566,6 @@ class MavenArtifactRequirement(Requirement):
|
||||||
self.version,
|
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
|
@classmethod
|
||||||
def from_str(cls, text):
|
def from_str(cls, text):
|
||||||
return cls.from_tuple(text.split(":"))
|
return cls.from_tuple(text.split(":"))
|
||||||
|
@ -724,16 +587,17 @@ class MavenArtifactRequirement(Requirement):
|
||||||
|
|
||||||
|
|
||||||
class GnomeCommonRequirement(Requirement):
|
class GnomeCommonRequirement(Requirement):
|
||||||
family = "gnome-common"
|
def __init__(self):
|
||||||
|
super(GnomeCommonRequirement, self).__init__("gnome-common")
|
||||||
|
|
||||||
|
|
||||||
class JDKFileRequirement(Requirement):
|
class JDKFileRequirement(Requirement):
|
||||||
|
|
||||||
jdk_path: str
|
jdk_path: str
|
||||||
filename: str
|
filename: str
|
||||||
family = "jdk-file"
|
|
||||||
|
|
||||||
def __init__(self, jdk_path: str, filename: str):
|
def __init__(self, jdk_path: str, filename: str):
|
||||||
|
super(JDKFileRequirement, self).__init__("jdk-file")
|
||||||
self.jdk_path = jdk_path
|
self.jdk_path = jdk_path
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
|
@ -743,70 +607,55 @@ class JDKFileRequirement(Requirement):
|
||||||
|
|
||||||
|
|
||||||
class JDKRequirement(Requirement):
|
class JDKRequirement(Requirement):
|
||||||
family = "jdk"
|
def __init__(self):
|
||||||
|
super(JDKRequirement, self).__init__("jdk")
|
||||||
|
|
||||||
|
|
||||||
class JRERequirement(Requirement):
|
class JRERequirement(Requirement):
|
||||||
family = "jre"
|
def __init__(self):
|
||||||
|
super(JRERequirement, self).__init__("jre")
|
||||||
|
|
||||||
class QtModuleRequirement(Requirement):
|
|
||||||
family = "qt-module"
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
self.module = module
|
|
||||||
|
|
||||||
|
|
||||||
class QTRequirement(Requirement):
|
class QTRequirement(Requirement):
|
||||||
family = "qt"
|
def __init__(self):
|
||||||
|
super(QTRequirement, self).__init__("qt")
|
||||||
|
|
||||||
|
|
||||||
class X11Requirement(Requirement):
|
class X11Requirement(Requirement):
|
||||||
family = "x11"
|
def __init__(self):
|
||||||
|
super(X11Requirement, self).__init__("x11")
|
||||||
|
|
||||||
|
|
||||||
class CertificateAuthorityRequirement(Requirement):
|
class CertificateAuthorityRequirement(Requirement):
|
||||||
family = "ca-cert"
|
|
||||||
|
|
||||||
def __init__(self, url):
|
def __init__(self, url):
|
||||||
|
super(CertificateAuthorityRequirement, self).__init__("ca-cert")
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
|
|
||||||
class PerlFileRequirement(Requirement):
|
class PerlFileRequirement(Requirement):
|
||||||
|
|
||||||
filename: str
|
filename: str
|
||||||
family = "perl-file"
|
|
||||||
|
|
||||||
def __init__(self, filename: str):
|
def __init__(self, filename: str):
|
||||||
|
super(PerlFileRequirement, self).__init__("perl-file")
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
|
||||||
|
|
||||||
class AutoconfMacroRequirement(Requirement):
|
class AutoconfMacroRequirement(Requirement):
|
||||||
|
|
||||||
family = "autoconf-macro"
|
|
||||||
macro: str
|
macro: str
|
||||||
|
|
||||||
def __init__(self, macro: str):
|
def __init__(self, macro: str):
|
||||||
|
super(AutoconfMacroRequirement, self).__init__("autoconf-macro")
|
||||||
self.macro = 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):
|
class LibtoolRequirement(Requirement):
|
||||||
family = "libtool"
|
def __init__(self):
|
||||||
|
super(LibtoolRequirement, self).__init__("libtool")
|
||||||
|
|
||||||
|
|
||||||
class IntrospectionTypelibRequirement(Requirement):
|
class IntrospectionTypelibRequirement(Requirement):
|
||||||
family = "introspection-type-lib"
|
|
||||||
|
|
||||||
def __init__(self, library):
|
def __init__(self, library):
|
||||||
self.library = library
|
self.library = library
|
||||||
|
|
||||||
|
@ -816,9 +665,9 @@ class PythonModuleRequirement(Requirement):
|
||||||
module: str
|
module: str
|
||||||
python_version: Optional[str]
|
python_version: Optional[str]
|
||||||
minimum_version: Optional[str]
|
minimum_version: Optional[str]
|
||||||
family = "python-module"
|
|
||||||
|
|
||||||
def __init__(self, module, python_version=None, minimum_version=None):
|
def __init__(self, module, python_version=None, minimum_version=None):
|
||||||
|
super(PythonModuleRequirement, self).__init__("python-module")
|
||||||
self.module = module
|
self.module = module
|
||||||
self.python_version = python_version
|
self.python_version = python_version
|
||||||
self.minimum_version = minimum_version
|
self.minimum_version = minimum_version
|
||||||
|
@ -853,25 +702,7 @@ class PythonModuleRequirement(Requirement):
|
||||||
class BoostComponentRequirement(Requirement):
|
class BoostComponentRequirement(Requirement):
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
family = "boost-component"
|
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
|
super(BoostComponentRequirement, self).__init__("boost-component")
|
||||||
self.name = name
|
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 logging
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Optional, List, Type
|
from .. import UnidentifiedError
|
||||||
|
|
||||||
from .. import UnidentifiedError, Requirement
|
|
||||||
from ..fix_build import run_detecting_problems
|
from ..fix_build import run_detecting_problems
|
||||||
from ..session import Session
|
|
||||||
|
|
||||||
|
|
||||||
class UnsatisfiedRequirements(Exception):
|
class UnsatisfiedRequirements(Exception):
|
||||||
|
@ -31,22 +28,13 @@ class UnsatisfiedRequirements(Exception):
|
||||||
|
|
||||||
|
|
||||||
class Resolver(object):
|
class Resolver(object):
|
||||||
|
def install(self, requirements):
|
||||||
name: str
|
|
||||||
|
|
||||||
def __init__(self, session, user_local):
|
|
||||||
raise NotImplementedError(self.__init__)
|
|
||||||
|
|
||||||
def install(self, requirements: List[Requirement]):
|
|
||||||
raise NotImplementedError(self.install)
|
raise NotImplementedError(self.install)
|
||||||
|
|
||||||
def resolve(self, requirement: Requirement) -> Optional[Requirement]:
|
def resolve(self, requirement):
|
||||||
raise NotImplementedError(self.resolve)
|
raise NotImplementedError(self.resolve)
|
||||||
|
|
||||||
def resolve_all(self, requirement: Requirement) -> List[Requirement]:
|
def explain(self, requirements):
|
||||||
raise NotImplementedError(self.resolve_all)
|
|
||||||
|
|
||||||
def explain(self, requirements: List[Requirement]):
|
|
||||||
raise NotImplementedError(self.explain)
|
raise NotImplementedError(self.explain)
|
||||||
|
|
||||||
def env(self):
|
def env(self):
|
||||||
|
@ -54,15 +42,13 @@ class Resolver(object):
|
||||||
|
|
||||||
|
|
||||||
class CPANResolver(Resolver):
|
class CPANResolver(Resolver):
|
||||||
name = "cpan"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False, skip_tests=True):
|
def __init__(self, session, user_local=False, skip_tests=True):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.user_local = user_local
|
self.user_local = user_local
|
||||||
self.skip_tests = skip_tests
|
self.skip_tests = skip_tests
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return "cpan"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.session)
|
return "%s(%r)" % (type(self).__name__, self.session)
|
||||||
|
@ -123,8 +109,7 @@ class TlmgrResolver(Resolver):
|
||||||
self.repository = repository
|
self.repository = repository
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if (self.repository.startswith('http://')
|
if self.repository.startswith('http://') or self.repository.startswith('https://'):
|
||||||
or self.repository.startswith('https://')):
|
|
||||||
return 'tlmgr(%r)' % self.repository
|
return 'tlmgr(%r)' % self.repository
|
||||||
else:
|
else:
|
||||||
return self.repository
|
return self.repository
|
||||||
|
@ -169,8 +154,7 @@ class TlmgrResolver(Resolver):
|
||||||
try:
|
try:
|
||||||
run_detecting_problems(self.session, cmd, user=user)
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
except UnidentifiedError as e:
|
except UnidentifiedError as e:
|
||||||
if ("tlmgr: user mode not initialized, "
|
if "tlmgr: user mode not initialized, please read the documentation!" in e.lines:
|
||||||
"please read the documentation!") in e.lines:
|
|
||||||
self.session.check_call(['tlmgr', 'init-usertree'])
|
self.session.check_call(['tlmgr', 'init-usertree'])
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
@ -179,7 +163,6 @@ class TlmgrResolver(Resolver):
|
||||||
|
|
||||||
|
|
||||||
class CTANResolver(TlmgrResolver):
|
class CTANResolver(TlmgrResolver):
|
||||||
name = "ctan"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False):
|
def __init__(self, session, user_local=False):
|
||||||
super(CTANResolver, self).__init__(
|
super(CTANResolver, self).__init__(
|
||||||
|
@ -187,16 +170,13 @@ class CTANResolver(TlmgrResolver):
|
||||||
|
|
||||||
|
|
||||||
class RResolver(Resolver):
|
class RResolver(Resolver):
|
||||||
|
|
||||||
name: str
|
|
||||||
|
|
||||||
def __init__(self, session, repos, user_local=False):
|
def __init__(self, session, repos, user_local=False):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.repos = repos
|
self.repos = repos
|
||||||
self.user_local = user_local
|
self.user_local = user_local
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return "cran"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r, %r)" % (type(self).__name__, self.session, self.repos)
|
return "%s(%r, %r)" % (type(self).__name__, self.session, self.repos)
|
||||||
|
@ -241,14 +221,12 @@ class RResolver(Resolver):
|
||||||
|
|
||||||
|
|
||||||
class OctaveForgeResolver(Resolver):
|
class OctaveForgeResolver(Resolver):
|
||||||
name = "octave-forge"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False):
|
def __init__(self, session, user_local=False):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.user_local = user_local
|
self.user_local = user_local
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return "octave-forge"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.session)
|
return "%s(%r)" % (type(self).__name__, self.session)
|
||||||
|
@ -289,8 +267,6 @@ class OctaveForgeResolver(Resolver):
|
||||||
|
|
||||||
|
|
||||||
class CRANResolver(RResolver):
|
class CRANResolver(RResolver):
|
||||||
name = "cran"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False):
|
def __init__(self, session, user_local=False):
|
||||||
super(CRANResolver, self).__init__(
|
super(CRANResolver, self).__init__(
|
||||||
session, "http://cran.r-project.org", user_local=user_local
|
session, "http://cran.r-project.org", user_local=user_local
|
||||||
|
@ -298,25 +274,19 @@ class CRANResolver(RResolver):
|
||||||
|
|
||||||
|
|
||||||
class BioconductorResolver(RResolver):
|
class BioconductorResolver(RResolver):
|
||||||
name = "bioconductor"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False):
|
def __init__(self, session, user_local=False):
|
||||||
super(BioconductorResolver, self).__init__(
|
super(BioconductorResolver, self).__init__(
|
||||||
session, "https://hedgehog.fhcrc.org/bioconductor",
|
session, "https://hedgehog.fhcrc.org/bioconductor", user_local=user_local
|
||||||
user_local=user_local
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class HackageResolver(Resolver):
|
class HackageResolver(Resolver):
|
||||||
|
|
||||||
name = "hackage"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False):
|
def __init__(self, session, user_local=False):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.user_local = user_local
|
self.user_local = user_local
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return "hackage"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.session)
|
return "%s(%r)" % (type(self).__name__, self.session)
|
||||||
|
@ -325,8 +295,7 @@ class HackageResolver(Resolver):
|
||||||
extra_args = []
|
extra_args = []
|
||||||
if self.user_local:
|
if self.user_local:
|
||||||
extra_args.append("--user")
|
extra_args.append("--user")
|
||||||
return (["cabal", "install"] + extra_args
|
return ["cabal", "install"] + extra_args + [req.package for req in reqs]
|
||||||
+ [req.package for req in reqs])
|
|
||||||
|
|
||||||
def install(self, requirements):
|
def install(self, requirements):
|
||||||
from ..requirements import HaskellPackageRequirement
|
from ..requirements import HaskellPackageRequirement
|
||||||
|
@ -360,15 +329,12 @@ class HackageResolver(Resolver):
|
||||||
|
|
||||||
|
|
||||||
class PypiResolver(Resolver):
|
class PypiResolver(Resolver):
|
||||||
|
|
||||||
name = "pypi"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False):
|
def __init__(self, session, user_local=False):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.user_local = user_local
|
self.user_local = user_local
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return "pypi"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.session)
|
return "%s(%r)" % (type(self).__name__, self.session)
|
||||||
|
@ -414,15 +380,12 @@ class PypiResolver(Resolver):
|
||||||
|
|
||||||
|
|
||||||
class GoResolver(Resolver):
|
class GoResolver(Resolver):
|
||||||
|
|
||||||
name = "go"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local):
|
def __init__(self, session, user_local):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.user_local = user_local
|
self.user_local = user_local
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return "go"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.session)
|
return "%s(%r)" % (type(self).__name__, self.session)
|
||||||
|
@ -463,26 +426,17 @@ NPM_COMMAND_PACKAGES = {
|
||||||
"del-cli": "del-cli",
|
"del-cli": "del-cli",
|
||||||
"husky": "husky",
|
"husky": "husky",
|
||||||
"cross-env": "cross-env",
|
"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):
|
class NpmResolver(Resolver):
|
||||||
name = "npm"
|
|
||||||
|
|
||||||
def __init__(self, session, user_local=False):
|
def __init__(self, session, user_local=False):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.user_local = user_local
|
self.user_local = user_local
|
||||||
# TODO(jelmer): Handle user_local
|
# TODO(jelmer): Handle user_local
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return "npm"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%r)" % (type(self).__name__, self.session)
|
return "%s(%r)" % (type(self).__name__, self.session)
|
||||||
|
@ -518,10 +472,7 @@ class NpmResolver(Resolver):
|
||||||
if not isinstance(requirement, NodePackageRequirement):
|
if not isinstance(requirement, NodePackageRequirement):
|
||||||
missing.append(requirement)
|
missing.append(requirement)
|
||||||
continue
|
continue
|
||||||
cmd = ["npm", "install"]
|
cmd = ["npm", "-g", "install", requirement.package]
|
||||||
if not self.user_local:
|
|
||||||
cmd.append('-g')
|
|
||||||
cmd.append(requirement.package)
|
|
||||||
logging.info("npm: running %r", cmd)
|
logging.info("npm: running %r", cmd)
|
||||||
run_detecting_problems(self.session, cmd, user=user)
|
run_detecting_problems(self.session, cmd, user=user)
|
||||||
if missing:
|
if missing:
|
||||||
|
@ -578,7 +529,7 @@ class StackedResolver(Resolver):
|
||||||
raise UnsatisfiedRequirements(requirements)
|
raise UnsatisfiedRequirements(requirements)
|
||||||
|
|
||||||
|
|
||||||
NATIVE_RESOLVER_CLS: List[Type[Resolver]] = [
|
NATIVE_RESOLVER_CLS = [
|
||||||
CPANResolver,
|
CPANResolver,
|
||||||
CTANResolver,
|
CTANResolver,
|
||||||
PypiResolver,
|
PypiResolver,
|
||||||
|
@ -592,70 +543,24 @@ NATIVE_RESOLVER_CLS: List[Type[Resolver]] = [
|
||||||
|
|
||||||
|
|
||||||
def native_resolvers(session, user_local):
|
def native_resolvers(session, user_local):
|
||||||
return StackedResolver(
|
return StackedResolver([kls(session, user_local) for kls in NATIVE_RESOLVER_CLS])
|
||||||
[kls(session, user_local) for kls in NATIVE_RESOLVER_CLS])
|
|
||||||
|
|
||||||
|
|
||||||
def select_resolvers(session, user_local, resolvers,
|
def auto_resolver(session, explain=False):
|
||||||
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):
|
|
||||||
# if session is SchrootSession or if we're root, use apt
|
# if session is SchrootSession or if we're root, use apt
|
||||||
|
from .apt import AptResolver
|
||||||
from ..session.schroot import SchrootSession
|
from ..session.schroot import SchrootSession
|
||||||
from ..session import get_user
|
from ..session import get_user
|
||||||
|
|
||||||
user = get_user(session)
|
user = get_user(session)
|
||||||
resolvers = []
|
resolvers = []
|
||||||
if system_wide is None:
|
# TODO(jelmer): Check VIRTUAL_ENV, and prioritize PypiResolver if
|
||||||
# TODO(jelmer): Check VIRTUAL_ENV, and prioritize PypiResolver if
|
# present?
|
||||||
# present?
|
if isinstance(session, SchrootSession) or user == "root" or explain:
|
||||||
if isinstance(session, SchrootSession) or user == "root" or explain:
|
user_local = False
|
||||||
system_wide = True
|
else:
|
||||||
else:
|
user_local = True
|
||||||
system_wide = False
|
if not user_local:
|
||||||
if system_wide:
|
resolvers.append(AptResolver.from_session(session))
|
||||||
try:
|
resolvers.extend([kls(session, user_local) for kls in NATIVE_RESOLVER_CLS])
|
||||||
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])
|
|
||||||
return StackedResolver(resolvers)
|
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)
|
raise NotImplementedError(self.check_output)
|
||||||
|
|
||||||
def Popen(
|
def Popen(
|
||||||
self, argv, cwd: Optional[str] = None, user: Optional[str] = None,
|
self, argv, cwd: Optional[str] = None, user: Optional[str] = None, **kwargs
|
||||||
**kwargs
|
|
||||||
):
|
):
|
||||||
raise NotImplementedError(self.Popen)
|
raise NotImplementedError(self.Popen)
|
||||||
|
|
||||||
def call(
|
def call(
|
||||||
self, argv: List[str], cwd: Optional[str] = None,
|
self, argv: List[str], cwd: Optional[str] = None, user: Optional[str] = None
|
||||||
user: Optional[str] = None
|
|
||||||
):
|
):
|
||||||
raise NotImplementedError(self.call)
|
raise NotImplementedError(self.call)
|
||||||
|
|
||||||
|
@ -102,26 +100,17 @@ class Session(object):
|
||||||
def external_path(self, path: str) -> str:
|
def external_path(self, path: str) -> str:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def rmtree(self, path: str) -> str:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
is_temporary: bool
|
is_temporary: bool
|
||||||
|
|
||||||
|
|
||||||
class SessionSetupFailure(Exception):
|
class SessionSetupFailure(Exception):
|
||||||
"""Session failed to be set up."""
|
"""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):
|
||||||
def run_with_tee(session: Session,
|
|
||||||
args: List[str], **kwargs) -> Tuple[int, List[str]]:
|
|
||||||
if "stdin" not in kwargs:
|
if "stdin" not in kwargs:
|
||||||
kwargs["stdin"] = subprocess.DEVNULL
|
kwargs["stdin"] = subprocess.DEVNULL
|
||||||
p = session.Popen(
|
p = session.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
||||||
args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
|
||||||
contents = []
|
contents = []
|
||||||
while p.poll() is None:
|
while p.poll() is None:
|
||||||
line = p.stdout.readline()
|
line = p.stdout.readline()
|
||||||
|
@ -132,8 +121,7 @@ def run_with_tee(session: Session,
|
||||||
|
|
||||||
|
|
||||||
def get_user(session):
|
def get_user(session):
|
||||||
return session.check_output(
|
return session.check_output(["echo", "$USER"], cwd="/").decode().strip()
|
||||||
["sh", "-c", "echo $USER"], cwd="/").decode().strip()
|
|
||||||
|
|
||||||
|
|
||||||
def which(session, name):
|
def which(session, name):
|
||||||
|
|
|
@ -20,7 +20,6 @@ from . import Session, NoSessionOpen, SessionAlreadyOpen
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Optional, Dict, List
|
from typing import Optional, Dict, List
|
||||||
|
@ -73,8 +72,7 @@ class PlainSession(Session):
|
||||||
close_fds: bool = True,
|
close_fds: bool = True,
|
||||||
):
|
):
|
||||||
argv = self._prepend_user(user, argv)
|
argv = self._prepend_user(user, argv)
|
||||||
return subprocess.check_call(
|
return subprocess.check_call(argv, cwd=cwd, env=env, close_fds=close_fds)
|
||||||
argv, cwd=cwd, env=env, close_fds=close_fds)
|
|
||||||
|
|
||||||
def check_output(
|
def check_output(
|
||||||
self,
|
self,
|
||||||
|
@ -86,19 +84,13 @@ class PlainSession(Session):
|
||||||
argv = self._prepend_user(user, argv)
|
argv = self._prepend_user(user, argv)
|
||||||
return subprocess.check_output(argv, cwd=cwd, env=env)
|
return subprocess.check_output(argv, cwd=cwd, env=env)
|
||||||
|
|
||||||
def Popen(
|
def Popen(self, args, stdout=None, stderr=None, stdin=None, user=None, cwd=None, env=None):
|
||||||
self, args, stdout=None, stderr=None, stdin=None, user=None,
|
|
||||||
cwd=None, env=None):
|
|
||||||
args = self._prepend_user(user, args)
|
args = self._prepend_user(user, args)
|
||||||
return subprocess.Popen(
|
return subprocess.Popen(args, stdout=stdout, stderr=stderr, stdin=stdin, cwd=cwd, env=env)
|
||||||
args, stdout=stdout, stderr=stderr, stdin=stdin, cwd=cwd, env=env)
|
|
||||||
|
|
||||||
def exists(self, path):
|
def exists(self, path):
|
||||||
return os.path.exists(path)
|
return os.path.exists(path)
|
||||||
|
|
||||||
def rmtree(self, path):
|
|
||||||
return shutil.rmtree(path)
|
|
||||||
|
|
||||||
def scandir(self, path):
|
def scandir(self, path):
|
||||||
return os.scandir(path)
|
return os.scandir(path)
|
||||||
|
|
||||||
|
|
|
@ -66,38 +66,25 @@ class SchrootSession(Session):
|
||||||
if line.startswith(b"E: "):
|
if line.startswith(b"E: "):
|
||||||
logging.error("%s", line[3:].decode(errors="replace"))
|
logging.error("%s", line[3:].decode(errors="replace"))
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Failed to close schroot session %s, leaving stray.",
|
"Failed to close schroot session %s, leaving stray.", self.session_id
|
||||||
self.session_id
|
|
||||||
)
|
)
|
||||||
self.session_id = None
|
self.session_id = None
|
||||||
return False
|
return False
|
||||||
self.session_id = None
|
self.session_id = None
|
||||||
self._location = None
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __enter__(self) -> "Session":
|
def __enter__(self) -> "Session":
|
||||||
if self.session_id is not None:
|
if self.session_id is not None:
|
||||||
raise SessionAlreadyOpen(self)
|
raise SessionAlreadyOpen(self)
|
||||||
stderr = tempfile.TemporaryFile()
|
|
||||||
try:
|
try:
|
||||||
self.session_id = (
|
self.session_id = (
|
||||||
subprocess.check_output(
|
subprocess.check_output(["schroot", "-c", self.chroot, "-b"])
|
||||||
["schroot", "-c", self.chroot, "-b"], stderr=stderr)
|
|
||||||
.strip()
|
.strip()
|
||||||
.decode()
|
.decode()
|
||||||
)
|
)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
stderr.seek(0)
|
# TODO(jelmer): Capture stderr and forward in SessionSetupFailure
|
||||||
errlines = stderr.readlines()
|
raise SessionSetupFailure()
|
||||||
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)
|
|
||||||
logging.info(
|
logging.info(
|
||||||
"Opened schroot session %s (from %s)", self.session_id, self.chroot
|
"Opened schroot session %s (from %s)", self.session_id, self.chroot
|
||||||
)
|
)
|
||||||
|
@ -169,28 +156,24 @@ class SchrootSession(Session):
|
||||||
env: Optional[Dict[str, str]] = None,
|
env: Optional[Dict[str, str]] = None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
try:
|
try:
|
||||||
return subprocess.check_output(
|
return subprocess.check_output(self._run_argv(argv, cwd, user, env=env))
|
||||||
self._run_argv(argv, cwd, user, env=env))
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
raise subprocess.CalledProcessError(e.returncode, argv)
|
raise subprocess.CalledProcessError(e.returncode, argv)
|
||||||
|
|
||||||
def Popen(
|
def Popen(
|
||||||
self, argv, cwd: Optional[str] = None, user: Optional[str] = None,
|
self, argv, cwd: Optional[str] = None, user: Optional[str] = None, **kwargs
|
||||||
**kwargs
|
|
||||||
):
|
):
|
||||||
return subprocess.Popen(self._run_argv(argv, cwd, user), **kwargs)
|
return subprocess.Popen(self._run_argv(argv, cwd, user), **kwargs)
|
||||||
|
|
||||||
def call(
|
def call(
|
||||||
self, argv: List[str], cwd: Optional[str] = None,
|
self, argv: List[str], cwd: Optional[str] = None, user: Optional[str] = None
|
||||||
user: Optional[str] = None
|
|
||||||
):
|
):
|
||||||
return subprocess.call(self._run_argv(argv, cwd, user))
|
return subprocess.call(self._run_argv(argv, cwd, user))
|
||||||
|
|
||||||
def create_home(self) -> None:
|
def create_home(self) -> None:
|
||||||
"""Create the user's home directory."""
|
"""Create the user's home directory."""
|
||||||
home = (
|
home = (
|
||||||
self.check_output(
|
self.check_output(["sh", "-c", "echo $HOME"], cwd="/").decode().rstrip("\n")
|
||||||
["sh", "-c", "echo $HOME"], cwd="/").decode().rstrip("\n")
|
|
||||||
)
|
)
|
||||||
user = (
|
user = (
|
||||||
self.check_output(["sh", "-c", "echo $LOGNAME"], cwd="/")
|
self.check_output(["sh", "-c", "echo $LOGNAME"], cwd="/")
|
||||||
|
@ -206,8 +189,7 @@ class SchrootSession(Session):
|
||||||
return os.path.join(self.location, path.lstrip("/"))
|
return os.path.join(self.location, path.lstrip("/"))
|
||||||
if self._cwd is None:
|
if self._cwd is None:
|
||||||
raise ValueError("no cwd set")
|
raise ValueError("no cwd set")
|
||||||
return os.path.join(
|
return os.path.join(self.location, os.path.join(self._cwd, path).lstrip("/"))
|
||||||
self.location, os.path.join(self._cwd, path).lstrip("/"))
|
|
||||||
|
|
||||||
def exists(self, path: str) -> bool:
|
def exists(self, path: str) -> bool:
|
||||||
fullpath = self.external_path(path)
|
fullpath = self.external_path(path)
|
||||||
|
@ -221,17 +203,13 @@ class SchrootSession(Session):
|
||||||
fullpath = self.external_path(path)
|
fullpath = self.external_path(path)
|
||||||
return os.mkdir(fullpath)
|
return os.mkdir(fullpath)
|
||||||
|
|
||||||
def rmtree(self, path: str):
|
|
||||||
import shutil
|
|
||||||
fullpath = self.external_path(path)
|
|
||||||
return shutil.rmtree(fullpath)
|
|
||||||
|
|
||||||
def setup_from_vcs(
|
def setup_from_vcs(
|
||||||
self, tree, include_controldir: Optional[bool] = None, subdir="package"
|
self, tree, include_controldir: Optional[bool] = None, subdir="package"
|
||||||
):
|
):
|
||||||
from ..vcs import dupe_vcs_tree, export_vcs_tree
|
from ..vcs import dupe_vcs_tree, export_vcs_tree
|
||||||
|
|
||||||
build_dir = os.path.join(self.location, "build")
|
build_dir = os.path.join(self.location, "build")
|
||||||
|
|
||||||
directory = tempfile.mkdtemp(dir=build_dir)
|
directory = tempfile.mkdtemp(dir=build_dir)
|
||||||
reldir = "/" + os.path.relpath(directory, self.location)
|
reldir = "/" + os.path.relpath(directory, self.location)
|
||||||
|
|
||||||
|
@ -250,7 +228,7 @@ class SchrootSession(Session):
|
||||||
directory = tempfile.mkdtemp(dir=build_dir)
|
directory = tempfile.mkdtemp(dir=build_dir)
|
||||||
reldir = "/" + os.path.relpath(directory, self.location)
|
reldir = "/" + os.path.relpath(directory, self.location)
|
||||||
export_directory = os.path.join(directory, subdir)
|
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)
|
return export_directory, os.path.join(reldir, subdir)
|
||||||
|
|
||||||
is_temporary = True
|
is_temporary = True
|
||||||
|
|
|
@ -15,25 +15,16 @@
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
from .buildsystem import NoBuildToolsFound
|
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,
|
# Some things want to write to the user's home directory,
|
||||||
# e.g. pip caches in ~/.cache
|
# e.g. pip caches in ~/.cache
|
||||||
session.create_home()
|
session.create_home()
|
||||||
|
|
||||||
if log_manager is None:
|
|
||||||
log_manager = NoLogManager()
|
|
||||||
|
|
||||||
for buildsystem in buildsystems:
|
for buildsystem in buildsystems:
|
||||||
iterate_with_build_fixers(
|
buildsystem.test(session, resolver, fixers)
|
||||||
fixers, log_manager.wrap(
|
|
||||||
partial(buildsystem.test, session, resolver)))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NoBuildToolsFound()
|
raise NoBuildToolsFound()
|
||||||
|
|
|
@ -23,13 +23,10 @@ import unittest
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
names = [
|
names = [
|
||||||
'buildlog',
|
"debian_build",
|
||||||
'logs',
|
|
||||||
]
|
]
|
||||||
if os.path.exists("/usr/bin/dpkg-architecture"):
|
if os.path.exists("/usr/bin/dpkg-architecture"):
|
||||||
names.append("debian_build")
|
|
||||||
names.append("debian_fix_build")
|
names.append("debian_fix_build")
|
||||||
names.append("resolver_apt")
|
module_names = ["ognibuild.tests.test_" + name for name in names]
|
||||||
module_names = ["tests.test_" + name for name in names]
|
|
||||||
loader = unittest.TestLoader()
|
loader = unittest.TestLoader()
|
||||||
return loader.loadTestsFromNames(module_names)
|
return loader.loadTestsFromNames(module_names)
|
|
@ -17,17 +17,8 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
from debian.changelog import Version
|
from ..debian.build import add_dummy_changelog_entry, get_build_architecture
|
||||||
|
|
||||||
from ognibuild.debian.build import (
|
|
||||||
add_dummy_changelog_entry,
|
|
||||||
get_build_architecture,
|
|
||||||
version_add_suffix,
|
|
||||||
_builddeb_command,
|
|
||||||
DEFAULT_BUILDER,
|
|
||||||
)
|
|
||||||
|
|
||||||
from breezy.tests import TestCaseWithTransport, TestCase
|
from breezy.tests import TestCaseWithTransport, TestCase
|
||||||
|
|
||||||
|
@ -159,43 +150,3 @@ class BuildArchitectureTests(TestCase):
|
||||||
|
|
||||||
def test_is_str(self):
|
def test_is_str(self):
|
||||||
self.assertIsInstance(get_build_architecture(), str)
|
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,
|
MissingRubyGem,
|
||||||
MissingValaPackage,
|
MissingValaPackage,
|
||||||
)
|
)
|
||||||
from ognibuild.debian.apt import AptManager, FileSearcher
|
from ..debian.apt import AptManager, FileSearcher
|
||||||
from ognibuild.debian.fix_build import (
|
from ..debian.fix_build import (
|
||||||
resolve_error,
|
resolve_error,
|
||||||
versioned_package_fixers,
|
versioned_package_fixers,
|
||||||
apt_fixers,
|
apt_fixers,
|
||||||
DebianPackagingContext,
|
DebianPackagingContext,
|
||||||
add_build_dependency,
|
|
||||||
)
|
)
|
||||||
from ognibuild.resolver.apt import AptRequirement
|
|
||||||
from breezy.commit import NullCommitReporter
|
from breezy.commit import NullCommitReporter
|
||||||
from breezy.tests import TestCaseWithTransport
|
from breezy.tests import TestCaseWithTransport
|
||||||
|
|
||||||
|
@ -46,7 +44,7 @@ class DummyAptSearcher(FileSearcher):
|
||||||
def __init__(self, files):
|
def __init__(self, files):
|
||||||
self._apt_files = 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()):
|
for p, pkg in sorted(self._apt_files.items()):
|
||||||
if case_insensitive:
|
if case_insensitive:
|
||||||
flags = re.I
|
flags = re.I
|
||||||
|
@ -99,7 +97,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
||||||
self._apt_files = {}
|
self._apt_files = {}
|
||||||
|
|
||||||
def resolve(self, error, context=("build",)):
|
def resolve(self, error, context=("build",)):
|
||||||
from ognibuild.session.plain import PlainSession
|
from ..session.plain import PlainSession
|
||||||
|
|
||||||
session = PlainSession()
|
session = PlainSession()
|
||||||
apt = AptManager(session)
|
apt = AptManager(session)
|
||||||
|
@ -111,8 +109,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
||||||
update_changelog=True,
|
update_changelog=True,
|
||||||
commit_reporter=NullCommitReporter(),
|
commit_reporter=NullCommitReporter(),
|
||||||
)
|
)
|
||||||
fixers = versioned_package_fixers(
|
fixers = versioned_package_fixers(session, context, apt) + apt_fixers(apt, context)
|
||||||
session, context, apt) + apt_fixers(apt, context)
|
|
||||||
return resolve_error(error, ("build",), fixers)
|
return resolve_error(error, ("build",), fixers)
|
||||||
|
|
||||||
def get_build_deps(self):
|
def get_build_deps(self):
|
||||||
|
@ -121,8 +118,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
def test_missing_command_unknown(self):
|
def test_missing_command_unknown(self):
|
||||||
self._apt_files = {}
|
self._apt_files = {}
|
||||||
self.assertFalse(self.resolve(
|
self.assertFalse(self.resolve(MissingCommand("acommandthatdoesnotexist")))
|
||||||
MissingCommand("acommandthatdoesnotexist")))
|
|
||||||
|
|
||||||
def test_missing_command_brz(self):
|
def test_missing_command_brz(self):
|
||||||
self._apt_files = {
|
self._apt_files = {
|
||||||
|
@ -134,8 +130,7 @@ blah (0.1) UNRELEASED; urgency=medium
|
||||||
self.overrideEnv("DEBFULLNAME", "Jelmer Vernooij")
|
self.overrideEnv("DEBFULLNAME", "Jelmer Vernooij")
|
||||||
self.assertTrue(self.resolve(MissingCommand("brz")))
|
self.assertTrue(self.resolve(MissingCommand("brz")))
|
||||||
self.assertEqual("libc6, brz", self.get_build_deps())
|
self.assertEqual("libc6, brz", self.get_build_deps())
|
||||||
rev = self.tree.branch.repository.get_revision(
|
rev = self.tree.branch.repository.get_revision(self.tree.branch.last_revision())
|
||||||
self.tree.branch.last_revision())
|
|
||||||
self.assertEqual("Add missing build dependency on brz.\n", rev.message)
|
self.assertEqual("Add missing build dependency on brz.\n", rev.message)
|
||||||
self.assertFalse(self.resolve(MissingCommand("brz")))
|
self.assertFalse(self.resolve(MissingCommand("brz")))
|
||||||
self.assertEqual("libc6, brz", self.get_build_deps())
|
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):
|
def test_missing_ruby_file_from_gem(self):
|
||||||
self._apt_files = {
|
self._apt_files = {
|
||||||
"/usr/share/rubygems-integration/all/gems/activesupport-"
|
"/usr/share/rubygems-integration/all/gems/activesupport-"
|
||||||
"5.2.3/lib/active_support/core_ext/string/strip.rb":
|
"5.2.3/lib/active_support/core_ext/string/strip.rb": "ruby-activesupport"
|
||||||
"ruby-activesupport"
|
|
||||||
}
|
}
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
self.resolve(MissingRubyFile(
|
self.resolve(MissingRubyFile("active_support/core_ext/string/strip"))
|
||||||
"active_support/core_ext/string/strip"))
|
|
||||||
)
|
)
|
||||||
self.assertEqual("libc6, ruby-activesupport", self.get_build_deps())
|
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())
|
self.assertEqual("libc6, ruby-bio (>= 2.0.3)", self.get_build_deps())
|
||||||
|
|
||||||
def test_missing_perl_module(self):
|
def test_missing_perl_module(self):
|
||||||
self._apt_files = {
|
self._apt_files = {"/usr/share/perl5/App/cpanminus/fatscript.pm": "cpanminus"}
|
||||||
"/usr/share/perl5/App/cpanminus/fatscript.pm": "cpanminus"}
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
self.resolve(
|
self.resolve(
|
||||||
MissingPerlModule(
|
MissingPerlModule(
|
||||||
|
@ -208,34 +200,28 @@ blah (0.1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
def test_missing_pkg_config(self):
|
def test_missing_pkg_config(self):
|
||||||
self._apt_files = {
|
self._apt_files = {
|
||||||
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc":
|
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc": "libxcb-xfixes0-dev"
|
||||||
"libxcb-xfixes0-dev"
|
|
||||||
}
|
}
|
||||||
self.assertTrue(self.resolve(MissingPkgConfig("xcb-xfixes")))
|
self.assertTrue(self.resolve(MissingPkgConfig("xcb-xfixes")))
|
||||||
self.assertEqual("libc6, libxcb-xfixes0-dev", self.get_build_deps())
|
self.assertEqual("libc6, libxcb-xfixes0-dev", self.get_build_deps())
|
||||||
|
|
||||||
def test_missing_pkg_config_versioned(self):
|
def test_missing_pkg_config_versioned(self):
|
||||||
self._apt_files = {
|
self._apt_files = {
|
||||||
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc":
|
"/usr/lib/x86_64-linux-gnu/pkgconfig/xcb-xfixes.pc": "libxcb-xfixes0-dev"
|
||||||
"libxcb-xfixes0-dev"
|
|
||||||
}
|
}
|
||||||
self.assertTrue(self.resolve(MissingPkgConfig("xcb-xfixes", "1.0")))
|
self.assertTrue(self.resolve(MissingPkgConfig("xcb-xfixes", "1.0")))
|
||||||
self.assertEqual(
|
self.assertEqual("libc6, libxcb-xfixes0-dev (>= 1.0)", self.get_build_deps())
|
||||||
"libc6, libxcb-xfixes0-dev (>= 1.0)", self.get_build_deps())
|
|
||||||
|
|
||||||
def test_missing_python_module(self):
|
def test_missing_python_module(self):
|
||||||
self._apt_files = {
|
self._apt_files = {"/usr/lib/python3/dist-packages/m2r.py": "python3-m2r"}
|
||||||
"/usr/lib/python3/dist-packages/m2r.py": "python3-m2r"}
|
|
||||||
self.assertTrue(self.resolve(MissingPythonModule("m2r")))
|
self.assertTrue(self.resolve(MissingPythonModule("m2r")))
|
||||||
self.assertEqual("libc6, python3-m2r", self.get_build_deps())
|
self.assertEqual("libc6, python3-m2r", self.get_build_deps())
|
||||||
|
|
||||||
def test_missing_go_package(self):
|
def test_missing_go_package(self):
|
||||||
self._apt_files = {
|
self._apt_files = {
|
||||||
"/usr/share/gocode/src/github.com/chzyer/readline/utils_test.go":
|
"/usr/share/gocode/src/github.com/chzyer/readline/utils_test.go": "golang-github-chzyer-readline-dev",
|
||||||
"golang-github-chzyer-readline-dev",
|
|
||||||
}
|
}
|
||||||
self.assertTrue(self.resolve(
|
self.assertTrue(self.resolve(MissingGoPackage("github.com/chzyer/readline")))
|
||||||
MissingGoPackage("github.com/chzyer/readline")))
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"libc6, golang-github-chzyer-readline-dev", self.get_build_deps()
|
"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.assertTrue(self.resolve(MissingValaPackage("posix")))
|
||||||
self.assertEqual("libc6, valac-0.48-vapi", self.get_build_deps())
|
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()
|
tree = tree.basis_tree()
|
||||||
try:
|
try:
|
||||||
result = tree._repository.controldir.sprout(
|
result = tree._repository.controldir.sprout(
|
||||||
directory, create_tree_if_local=True,
|
directory, create_tree_if_local=True, revision_id=tree.get_revision_id()
|
||||||
revision_id=tree.get_revision_id()
|
|
||||||
)
|
)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == errno.ENOSPC:
|
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]
|
[flake8]
|
||||||
banned-modules = silver-platter = Should not use silver-platter
|
banned-modules = silver-platter = Should not use silver-platter
|
||||||
exclude = build,.eggs/
|
|
||||||
|
|
||||||
[mypy]
|
[mypy]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
[bdist_wheel]
|
[bdist_wheel]
|
||||||
universal = 1
|
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
|
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