aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Le Cuirot <chewi@gentoo.org>2023-07-15 12:20:27 +0100
committerSam James <sam@gentoo.org>2023-08-02 07:31:19 +0100
commitad3994394af0bc975ec7c28bd60de496b580c25e (patch)
tree6d7d1321e8c1a9ef585764a67cc0694762a9b217
parentLinting fixes (diff)
downloadportage-ad399439.tar.gz
portage-ad399439.tar.bz2
portage-ad399439.zip
Migrate from setuptools to Meson and meson-python
This makes Portage PEP 517 compliant. When building via meson-python, the man pages and logrotate config are no longer included as there seems little point. Bug: https://bugs.gentoo.org/910035 Signed-off-by: James Le Cuirot <chewi@gentoo.org> Signed-off-by: Sam James <sam@gentoo.org>
-rw-r--r--.builds/ci.yml22
-rw-r--r--.builds/lint.yml10
-rw-r--r--.github/workflows/ci.yml21
-rw-r--r--.github/workflows/lint.yml11
-rw-r--r--DEVELOPING13
-rw-r--r--MANIFEST.in30
-rw-r--r--NEWS2
-rw-r--r--bin/meson.build73
-rw-r--r--cnf/meson.build144
-rw-r--r--doc/api/meson.build43
-rw-r--r--doc/fragment/meson.build5
-rw-r--r--doc/fragment/version.in1
-rw-r--r--doc/meson.build57
-rw-r--r--doc/portage.docbook2
-rw-r--r--lib/_emerge/AbstractEbuildProcess.py7
-rw-r--r--lib/_emerge/create_depgraph_params.py10
-rw-r--r--lib/_emerge/meson.build101
-rw-r--r--lib/_emerge/resolver/meson.build14
-rw-r--r--lib/meson.build2
-rw-r--r--lib/portage/__init__.py11
-rw-r--r--lib/portage/_compat_upgrade/meson.build10
-rw-r--r--lib/portage/_emirrordist/meson.build15
-rw-r--r--lib/portage/_sets/meson.build15
-rw-r--r--lib/portage/binrepo/meson.build8
-rw-r--r--lib/portage/cache/index/meson.build9
-rw-r--r--lib/portage/cache/meson.build20
-rw-r--r--lib/portage/const.py61
-rw-r--r--lib/portage/dbapi/meson.build22
-rw-r--r--lib/portage/dep/meson.build12
-rw-r--r--lib/portage/dep/soname/meson.build10
-rw-r--r--lib/portage/elog/meson.build16
-rw-r--r--lib/portage/emaint/meson.build11
-rw-r--r--lib/portage/emaint/modules/binhost/meson.build8
-rw-r--r--lib/portage/emaint/modules/config/meson.build8
-rw-r--r--lib/portage/emaint/modules/logs/meson.build8
-rw-r--r--lib/portage/emaint/modules/merges/meson.build8
-rw-r--r--lib/portage/emaint/modules/meson.build16
-rw-r--r--lib/portage/emaint/modules/move/meson.build8
-rw-r--r--lib/portage/emaint/modules/resume/meson.build8
-rw-r--r--lib/portage/emaint/modules/sync/meson.build8
-rw-r--r--lib/portage/emaint/modules/world/meson.build8
-rw-r--r--lib/portage/env/meson.build10
-rw-r--r--lib/portage/installation.py21
-rw-r--r--lib/portage/meson.build74
-rw-r--r--lib/portage/package/ebuild/_config/meson.build17
-rw-r--r--lib/portage/package/ebuild/_ipc/meson.build10
-rw-r--r--lib/portage/package/ebuild/_parallel_manifest/meson.build10
-rw-r--r--lib/portage/package/ebuild/meson.build23
-rw-r--r--lib/portage/package/meson.build9
-rw-r--r--lib/portage/proxy/meson.build9
-rw-r--r--lib/portage/repository/meson.build10
-rw-r--r--lib/portage/repository/storage/meson.build11
-rw-r--r--lib/portage/sync/meson.build14
-rw-r--r--lib/portage/sync/modules/cvs/meson.build8
-rw-r--r--lib/portage/sync/modules/git/meson.build8
-rw-r--r--lib/portage/sync/modules/mercurial/meson.build8
-rw-r--r--lib/portage/sync/modules/meson.build14
-rw-r--r--lib/portage/sync/modules/rsync/meson.build8
-rw-r--r--lib/portage/sync/modules/svn/meson.build8
-rw-r--r--lib/portage/sync/modules/webrsync/meson.build8
-rw-r--r--lib/portage/tests/bin/meson.build14
-rw-r--r--lib/portage/tests/conftest.py7
-rw-r--r--lib/portage/tests/dbapi/meson.build12
-rw-r--r--lib/portage/tests/dep/meson.build28
-rw-r--r--lib/portage/tests/ebuild/meson.build17
-rw-r--r--lib/portage/tests/emerge/meson.build14
-rw-r--r--lib/portage/tests/env/config/meson.build12
-rw-r--r--lib/portage/tests/env/meson.build10
-rw-r--r--lib/portage/tests/glsa/meson.build9
-rw-r--r--lib/portage/tests/gpkg/meson.build15
-rw-r--r--lib/portage/tests/lafilefixer/meson.build9
-rw-r--r--lib/portage/tests/lazyimport/meson.build10
-rw-r--r--lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py1
-rw-r--r--lib/portage/tests/lint/meson.build12
-rw-r--r--lib/portage/tests/locks/meson.build10
-rw-r--r--lib/portage/tests/meson.build32
-rw-r--r--lib/portage/tests/news/meson.build9
-rw-r--r--lib/portage/tests/process/meson.build16
-rw-r--r--lib/portage/tests/resolver/binpkg_multi_instance/meson.build10
-rw-r--r--lib/portage/tests/resolver/meson.build96
-rw-r--r--lib/portage/tests/resolver/soname/meson.build19
-rw-r--r--lib/portage/tests/runTests.py7
-rw-r--r--lib/portage/tests/sets/base/meson.build10
-rw-r--r--lib/portage/tests/sets/files/meson.build10
-rw-r--r--lib/portage/tests/sets/meson.build12
-rw-r--r--lib/portage/tests/sets/shell/meson.build9
-rw-r--r--lib/portage/tests/sync/meson.build9
-rw-r--r--lib/portage/tests/unicode/meson.build9
-rw-r--r--lib/portage/tests/update/meson.build11
-rw-r--r--lib/portage/tests/util/dyn_libs/meson.build9
-rw-r--r--lib/portage/tests/util/eventloop/meson.build9
-rw-r--r--lib/portage/tests/util/file_copy/meson.build9
-rw-r--r--lib/portage/tests/util/futures/asyncio/meson.build15
-rw-r--r--lib/portage/tests/util/futures/meson.build15
-rw-r--r--lib/portage/tests/util/meson.build31
-rw-r--r--lib/portage/tests/versions/meson.build10
-rw-r--r--lib/portage/tests/xpak/meson.build9
-rw-r--r--lib/portage/util/_async/meson.build20
-rw-r--r--lib/portage/util/_dyn_libs/meson.build14
-rw-r--r--lib/portage/util/_eventloop/meson.build9
-rw-r--r--lib/portage/util/elf/meson.build9
-rw-r--r--lib/portage/util/endian/meson.build8
-rw-r--r--lib/portage/util/file_copy/meson.build7
-rw-r--r--lib/portage/util/futures/_asyncio/meson.build8
-rw-r--r--lib/portage/util/futures/executor/meson.build8
-rw-r--r--lib/portage/util/futures/meson.build17
-rw-r--r--lib/portage/util/iterators/meson.build8
-rw-r--r--lib/portage/util/meson.build49
-rw-r--r--lib/portage/xml/meson.build8
-rw-r--r--man/color.map.52
-rw-r--r--man/dispatch-conf.12
-rw-r--r--man/ebuild.12
-rw-r--r--man/ebuild.52
-rw-r--r--man/egencache.12
-rw-r--r--man/emaint.12
-rw-r--r--man/emerge.12
-rw-r--r--man/emirrordist.12
-rw-r--r--man/env-update.12
-rw-r--r--man/etc-update.12
-rw-r--r--man/fixpackages.12
-rw-r--r--man/glsa-check.12
-rw-r--r--man/make.conf.52
-rw-r--r--man/meson.build31
-rw-r--r--man/portage.52
-rw-r--r--man/quickpkg.12
-rw-r--r--man/ru/color.map.52
-rw-r--r--man/ru/dispatch-conf.12
-rw-r--r--man/ru/ebuild.12
-rw-r--r--man/ru/env-update.12
-rw-r--r--man/ru/etc-update.12
-rw-r--r--man/ru/fixpackages.12
-rw-r--r--man/ru/meson.build19
-rw-r--r--meson.build123
-rw-r--r--meson_options.txt59
-rw-r--r--pyproject.toml37
-rwxr-xr-xsetup.py925
-rw-r--r--src/meson.build50
-rw-r--r--tox.ini2
138 files changed, 2087 insertions, 1060 deletions
diff --git a/.builds/ci.yml b/.builds/ci.yml
index 797aea074..2c1659c84 100644
--- a/.builds/ci.yml
+++ b/.builds/ci.yml
@@ -9,8 +9,6 @@ repositories:
# pypy: https://ppa.launchpadcontent.net/pypy/ppa/ubuntu jammy main "251104D968854915"
environment:
PYTHON_VERSIONS:
- - '3.7'
- - '3.8'
- '3.9'
- '3.10'
- '3.11'
@@ -21,26 +19,20 @@ tasks:
portage/.builds/setup-python.sh "${PYTHON_VERSIONS[@]}"
- setup-tests: |
+ sudo apt-get install -y --no-install-recommends meson
for py in "${PYTHON_VERSIONS[@]}"; do
- source ".venv-$py/bin/activate"
- pip install pytest
- deactivate
+ # setuptools needed for 3.12+ because of https://github.com/mesonbuild/meson/issues/7702.
+ ".venv-$py/bin/pip" install pytest setuptools
done
- test-install: |
for py in "${PYTHON_VERSIONS[@]}"; do
- source ".venv-$py/bin/activate"
- pushd portage
- time ./setup.py clean install
- popd
- deactivate
+ echo -e "[binaries]\npython = '${PWD}/.venv-${py}/bin/python'" > /tmp/native.ini
+ meson setup --native-file /tmp/native.ini "/tmp/build-$py" portage
+ meson install -C "/tmp/build-$py"
done
- test-portage: |
for py in "${PYTHON_VERSIONS[@]}"; do
- source ".venv-$py/bin/activate"
- pushd portage
- ./setup.py clean test
- popd
- deactivate
+ meson test -C "/tmp/build-$py" --verbose
done
diff --git a/.builds/lint.yml b/.builds/lint.yml
index 2301c87be..847552aac 100644
--- a/.builds/lint.yml
+++ b/.builds/lint.yml
@@ -7,8 +7,6 @@ repositories:
deadsnakes: https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy main "BA6932366A755776"
environment:
PYTHON_VERSIONS:
- - '3.7'
- - '3.8'
- '3.9'
- '3.10'
- '3.11'
@@ -17,15 +15,11 @@ tasks:
portage/.builds/setup-python.sh "${PYTHON_VERSIONS[@]}"
- setup-black: |
- source .venv/bin/activate
- pip install black
- deactivate
+ .venv/bin/pip install black
- setup-pylint: |
for py in "${PYTHON_VERSIONS[@]}"; do
- source ".venv-$py/bin/activate"
- pip install pylint pytest
- deactivate
+ ".venv-$py/bin/pip" install pylint pytest
done
- black: |
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6bfe4847a..519bf92c4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,20 +24,21 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- - name: Install python dependencies
+ - name: Install dependencies
run: |
set -xe
sudo apt-get update
- sudo apt-get install -y --no-install-recommends libxslt-dev libxml2-dev libxml2-utils zstd
+ sudo apt-get install -y --no-install-recommends libxslt-dev libxml2-dev libxml2-utils meson zstd
python -VV
python -m site
python -m pip install --upgrade pip
- python -m pip install tox tox-gh-actions setuptools
- - name: Test ./setup.py install --root=/tmp/install-root
+ # setuptools needed for 3.12+ because of https://github.com/mesonbuild/meson/issues/7702.
+ python -m pip install pytest setuptools
+ - name: Test meson install --destdir /tmp/install-root
run: |
- printf "[build_ext]\nportage_ext_modules=true" >> setup.cfg
- ./setup.py install --root=/tmp/install-root
- - name: Run tox test env for ${{ matrix.python-version }}
- run: tox -vv
- env:
- TARGET: test
+ echo -e "[binaries]\npython = '$(command -v python)'" > /tmp/native.ini
+ meson setup --native-file /tmp/native.ini /tmp/build .
+ meson install -C /tmp/build --destdir /tmp/install-root
+ - name: Run tests for ${{ matrix.python-version }}
+ run: |
+ meson test -C /tmp/build --verbose
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 469cffe37..f2af40957 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -25,7 +25,7 @@ jobs:
- '3.9'
- '3.10'
- '3.11'
- # Complains about importing distutils.dir_utils
+ # pylint currently broken under 3.12
# - '3.12-dev'
fail-fast: false
steps:
@@ -40,8 +40,7 @@ jobs:
python -VV
python -m site
python -m pip install --upgrade pip
- python -m pip install tox tox-gh-actions
- - name: Run tox pylint env for ${{ matrix.python-version }}
- run: tox -vv
- env:
- TARGET: pylint
+ python -m pip install pylint pytest
+ - name: Run pylint for ${{ matrix.python-version }}
+ run: |
+ ./run-pylint
diff --git a/DEVELOPING b/DEVELOPING
index 0d414c4da..a49afff83 100644
--- a/DEVELOPING
+++ b/DEVELOPING
@@ -213,14 +213,17 @@ Releases
1. Repository mangling:
- Update NEWS (for both new version & release date)
- - Update setup.py
+ - Update meson.build
and commit.
2. Create a git tag for this release:
git tag -a -s portage-3.0.30
-3. Create the tarball and run the tests: ./runtests
- (see PYTHON_SUPPORTED_VERSIONS in runtests).
+3. Create the tarball and run the tests:
+ - meson setup -Dmodules-only=true build
+ - meson test -C build --verbose
+ Use meson setup's --native-file to override the Python version. See
+ PYTHON_SUPPORTED_VERSIONS in runtests.
4. Version bump the ebuild locally (don't push) and verify it can re-install itself:
emerge --oneshot sys-apps/portage
@@ -234,8 +237,8 @@ and commit.
6. Create the release for pypi and upload it there:
- python -m venv .venv
- . .venv/bin/activate
- - pip install wheel twine
- - python setup.py bdist_wheel sdist
+ - pip install build twine
+ - python -m build
- twine upload dist/<filenames>
7. Bugzilla wrangling:
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 31ca9c166..000000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,30 +0,0 @@
-include setup.py
-
-# docs
-include DEVELOPING
-include LICENSE
-include TEST-NOTES
-
-# docbook sources
-include doc/custom.xsl
-recursive-include doc *.docbook
-
-# sphinx sources
-include doc/api/index.rst
-include doc/api/conf.py
-include doc/api/Makefile
-
-# extra conf files used in ebuild
-include cnf/make.conf.example.*
-
-# extra files for tests
-include .portage_not_installed
-
-# extra scripts
-include misc/*
-
-# extensions
-include src/*
-
-# GPG test keys
-recursive-include lib/portage/tests/.gnupg *
diff --git a/NEWS b/NEWS
index 82e4c8780..1f34bdd90 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ portage-3.0.50 (UNRELEASED)
Breaking changes:
* The minimum supported Python version is now >= Python 3.9.
+* Portage now installed with Meson and Python sdist + wheel now prepared with
+ meson-python for PEP 517 compliance.
Bug fixes:
* install-qa-check.d/05prefix: Fix prefixifying shebang for >= EAPI 7 ebuilds
diff --git a/bin/meson.build b/bin/meson.build
new file mode 100644
index 000000000..b076eaa39
--- /dev/null
+++ b/bin/meson.build
@@ -0,0 +1,73 @@
+py_bins = [
+ 'ebuild',
+ 'egencache',
+ 'emerge',
+ 'emirrordist',
+ 'glsa-check',
+ 'gpkg-sign',
+ 'portageq',
+ 'quickpkg'
+]
+
+other_bins = [
+ 'emerge-webrsync'
+]
+
+py_sbins = [
+ 'archive-conf',
+ 'dispatch-conf',
+ 'emaint',
+ 'env-update',
+ 'fixpackages',
+ 'regenworld'
+]
+
+etc_update = 'etc-update'
+
+if eprefix != ''
+ etc_update = configure_file(
+ input : etc_update,
+ output : 'etc-update',
+ command : hprefixify,
+ capture : true
+ )
+endif
+
+# It might seem sensible to use py.install_sources() to install the Python
+# scripts, but it's really just a wrapper around install_data that forces the
+# install_dir. Meson 1.2.0 and later also optimize to bytecode, but Gentoo does
+# this in the ebuild.
+
+install_data(
+ py_bins,
+ install_dir : system_wide ? get_option('bindir') : portage_bindir,
+ install_mode : 'rwxr-xr-x',
+)
+
+install_data(
+ other_bins,
+ install_dir : get_option('bindir'),
+ install_mode : 'rwxr-xr-x',
+)
+
+install_data(
+ py_sbins,
+ install_dir : system_wide ? get_option('sbindir') : portage_bindir,
+ install_mode : 'rwxr-xr-x',
+)
+
+install_data(
+ [etc_update],
+ install_dir : system_wide ? get_option('sbindir') : get_option('bindir'),
+ install_mode : 'rwxr-xr-x',
+)
+
+# Note that exclude_files is currently broken in meson-python, but it should
+# work soon. Having duplicates here isn't that bad in the meantime.
+# https://github.com/mesonbuild/meson-python/issues/317
+
+install_subdir(
+ '.',
+ exclude_files : py_bins + other_bins + py_sbins + ['etc-update', 'meson.build'],
+ install_dir : portage_bindir
+)
diff --git a/cnf/meson.build b/cnf/meson.build
new file mode 100644
index 000000000..00af62085
--- /dev/null
+++ b/cnf/meson.build
@@ -0,0 +1,144 @@
+install_data(
+ [
+ 'etc-update.conf',
+ 'dispatch-conf.conf'
+ ],
+ install_dir : sysconfdir
+)
+
+extra_features = []
+make_globals = 'make.globals'
+repos_conf = 'repos.conf'
+
+if get_option('gentoo-dev')
+ extra_features += [
+ 'ipc-sandbox',
+ 'network-sandbox',
+ 'strict-keepdir',
+ 'warn-on-large-env'
+ ]
+endif
+
+if get_option('xattr') and host_machine.system() == 'linux'
+ extra_features += [
+ 'xattr'
+ ]
+endif
+
+if extra_features.length() > 0
+ make_globals = configure_file(
+ input : make_globals,
+ output : 'make.globals#features',
+ command : [sed, '$aFEATURES="${FEATURES} ' + ' '.join(extra_features) + '"', '@INPUT@'],
+ capture : true
+ )
+endif
+
+if not get_option('rsync-verify')
+ repos_conf = configure_file(
+ input : repos_conf,
+ output : 'repos.conf#rsync-verify',
+ command : [sed, '-r', 's:\\b(sync-rsync-verify-metamanifest|sync-webrsync-verify-signature)(\\s*=\\s*).*:\\1\\2no:', '@INPUT@'],
+ capture : true
+ )
+endif
+
+if eprefix != ''
+ make_globals = configure_file(
+ input : make_globals,
+ output : 'make.globals#eprefix',
+ command : hprefixify,
+ capture : true
+ )
+
+ repos_conf = configure_file(
+ input : repos_conf,
+ output : 'repos.conf#eprefix',
+ command : hprefixify,
+ capture : true
+ )
+endif
+
+arch = host_machine.cpu()
+
+arch = {
+ 'aarch64' : 'arm64',
+ 'loongarch64' : 'loong',
+ 'mips64' : 'mips',
+ 'parisc' : 'hppa',
+ 'riscv32' : 'risc',
+ 'riscv64' : 'risc',
+ 's390x' : 's390',
+ 'sh4' : 'sh',
+ 'sparc64' : 'sparc',
+ 'x86_64' : 'amd64'
+}.get(arch, arch)
+
+if host_machine.system() == 'freebsd'
+ arch += '-fbsd'
+endif
+
+make_conf_example = 'make.conf.example'
+diff = make_conf_example + '.' + arch + '.diff'
+fs = import('fs')
+
+if fs.exists(diff)
+ patch = find_program('patch', required : true)
+ make_conf_example = configure_file(
+ input : [make_conf_example, diff],
+ output : 'make.conf.example',
+ command : [patch, '-o', '@OUTPUT@', '@INPUT0@', '@INPUT1@']
+ )
+else
+ warning('Portage does not have an arch-specific configuration for this arch. Please notify the arch maintainer about this issue. Using the generic configuration.')
+endif
+
+# TODO: Use fs.copyfile() when requiring Meson >=0.64.0.
+
+make_globals = configure_file(
+ input : make_globals,
+ output : 'make.globals',
+ copy : true
+)
+
+repos_conf = configure_file(
+ input : repos_conf,
+ output : 'repos.conf',
+ copy : true
+)
+
+# TODO: Use preserve_path option when requiring Meson >=0.64.0.
+
+install_data(
+ [
+ make_conf_example,
+ make_globals,
+ repos_conf,
+ ],
+ install_dir : portage_datadir / 'config'
+)
+
+install_data(
+ [
+ 'repo.postsync.d/example'
+ ],
+ install_dir : portage_datadir / 'config' / 'repo.postsync.d'
+)
+
+install_data(
+ [
+ 'sets/portage.conf'
+ ],
+ install_dir : portage_datadir / 'config' / 'sets'
+)
+
+if not system_wide
+ subdir_done()
+endif
+
+install_data(
+ [
+ 'logrotate.d/elog-save-summary'
+ ],
+ install_dir : sysconfdir / 'logrotate.d'
+)
diff --git a/doc/api/meson.build b/doc/api/meson.build
new file mode 100644
index 000000000..294be6d07
--- /dev/null
+++ b/doc/api/meson.build
@@ -0,0 +1,43 @@
+sphinx_apidoc = find_program('sphinx-apidoc', required : get_option('apidoc'))
+sphinx_build = find_program('sphinx-build', required : get_option('apidoc'))
+
+if not sphinx_apidoc.found() or not sphinx_build.found()
+ subdir_done()
+endif
+
+api_symlinks = custom_target(
+ 'symlinks',
+ input: ['conf.py', 'index.rst'],
+ output : ['conf.py', 'index.rst'],
+ command : ['ln', '-srnf', '@INPUT@', '@OUTDIR@/']
+)
+
+api_rst = custom_target(
+ 'rst',
+ output : ['portage.rst'],
+ command : [sphinx_apidoc, '-TPef', '-o', '@OUTDIR@', '@SOURCE_ROOT@/lib', '@SOURCE_ROOT@/lib/portage/tests'],
+ env : {'SPHINX_APIDOC_OPTIONS' : 'members,private-members,undoc-members,show-inheritance,ignore-module-all,inherited-members'}
+)
+
+# sphinx-build generates a lot of files, and it would be awkward to keep a list
+# updated. This workaround is slightly hacky as you're probably not supposed to
+# set "output" to a directory. Note that it cannot include a /. We use
+# install_subdir here, unlike in the parent directory, because it can use a
+# different name for the destination and can exclude some files.
+
+custom_target(
+ 'html',
+ depends: [api_symlinks, api_rst],
+ output : ['html'],
+ command : [sphinx_build, '-M', 'html', '@OUTDIR@', '@OUTDIR@/html'],
+ build_by_default : get_option('apidoc')
+)
+
+if get_option('apidoc')
+ install_subdir(
+ meson.current_build_dir() / 'html' / 'html',
+ exclude_directories : ['_sources'],
+ strip_directory : true,
+ install_dir : docdir / 'html' / 'api'
+ )
+endif
diff --git a/doc/fragment/meson.build b/doc/fragment/meson.build
new file mode 100644
index 000000000..c5604d3f0
--- /dev/null
+++ b/doc/fragment/meson.build
@@ -0,0 +1,5 @@
+version_doc_fragment = configure_file(
+ input : 'version.in',
+ output : 'version',
+ configuration : conf_data
+)
diff --git a/doc/fragment/version.in b/doc/fragment/version.in
new file mode 100644
index 000000000..2ca11428e
--- /dev/null
+++ b/doc/fragment/version.in
@@ -0,0 +1 @@
+<releaseinfo>@VERSION@</releaseinfo>
diff --git a/doc/meson.build b/doc/meson.build
new file mode 100644
index 000000000..a671d7114
--- /dev/null
+++ b/doc/meson.build
@@ -0,0 +1,57 @@
+subdir('api')
+subdir('fragment')
+
+xmlto = find_program('xmlto', required : get_option('doc'))
+
+if not xmlto.found()
+ subdir_done()
+endif
+
+docbook_src = [
+ version_doc_fragment,
+ 'config.docbook',
+ 'config/bashrc.docbook',
+ 'config/sets.docbook',
+ 'custom.xsl',
+ 'dependency_resolution.docbook',
+ 'dependency_resolution/decision_making.docbook',
+ 'dependency_resolution/package_modeling.docbook',
+ 'dependency_resolution/task_scheduling.docbook',
+ 'package.docbook',
+ 'package/ebuild.docbook',
+ 'package/ebuild/eapi/0.docbook',
+ 'package/ebuild/eapi/1.docbook',
+ 'package/ebuild/eapi/2.docbook',
+ 'package/ebuild/eapi/3.docbook',
+ 'package/ebuild/eapi/4.docbook',
+ 'package/ebuild/eapi/4-slot-abi.docbook',
+ 'package/ebuild/eapi/5.docbook',
+ 'package/ebuild/helper_functions.docbook',
+ 'package/ebuild/phases.docbook',
+ 'portage.docbook',
+ 'qa.docbook'
+]
+
+custom_target(
+ 'xhtml-nochunks',
+ depend_files : docbook_src,
+ output : ['portage.html'],
+ command : ['xmlto', '-o', '@OUTDIR@', '--searchpath', '@OUTDIR@/fragment', '-m', '@CURRENT_SOURCE_DIR@/custom.xsl', 'xhtml-nochunks', '@CURRENT_SOURCE_DIR@/portage.docbook'],
+ install : get_option('doc') and get_option('doc-formats').contains('xhtml-nochunks'),
+ install_dir : docdir / 'html'
+)
+
+# xhtml generates a lot of files, and it would be awkward to keep a list
+# updated. This workaround is slightly hacky as you're probably not supposed to
+# set "output" to a directory. Note that it cannot include a /. Another
+# alternative is to use install_subdir, but it's not much better as you still
+# need to set "output" to a directory.
+
+custom_target(
+ 'xhtml',
+ depend_files : docbook_src,
+ output : ['html'],
+ command : ['xmlto', '-o', '@OUTDIR@/html', '--searchpath', '@OUTDIR@/fragment', '-m', '@CURRENT_SOURCE_DIR@/custom.xsl', 'xhtml', '@CURRENT_SOURCE_DIR@/portage.docbook'],
+ install : get_option('doc') and get_option('doc-formats').contains('xhtml'),
+ install_dir : docdir
+)
diff --git a/doc/portage.docbook b/doc/portage.docbook
index 77cf7e87e..6e9ad74d6 100644
--- a/doc/portage.docbook
+++ b/doc/portage.docbook
@@ -2,7 +2,6 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
- <!ENTITY date SYSTEM "fragment/date">
<!ENTITY version SYSTEM "fragment/version">
<!ENTITY project "portage">
@@ -52,7 +51,6 @@
</authorgroup>
&version;
- &date;
</bookinfo>
&config;
diff --git a/lib/_emerge/AbstractEbuildProcess.py b/lib/_emerge/AbstractEbuildProcess.py
index 97408806c..96d91b5da 100644
--- a/lib/_emerge/AbstractEbuildProcess.py
+++ b/lib/_emerge/AbstractEbuildProcess.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import functools
@@ -10,6 +10,7 @@ from _emerge.EbuildBuildDir import EbuildBuildDir
from _emerge.EbuildIpcDaemon import EbuildIpcDaemon
import portage
from portage.elog import messages as elog_messages
+from portage import installation
from portage.package.ebuild._ipc.ExitCommand import ExitCommand
from portage.package.ebuild._ipc.QueryCommand import QueryCommand
from portage import os
@@ -51,7 +52,9 @@ class AbstractEbuildProcess(SpawnProcess):
# The EbuildIpcDaemon support is well tested, but this variable
# is left so we can temporarily disable it if any issues arise.
- _enable_ipc_daemon = True
+ _enable_ipc_daemon = (
+ installation.TYPE == installation.TYPES.SOURCE or "@IPC@" == "True"
+ )
def __init__(self, **kwargs):
SpawnProcess.__init__(self, **kwargs)
diff --git a/lib/_emerge/create_depgraph_params.py b/lib/_emerge/create_depgraph_params.py
index 1bbca5de9..3e000ae91 100644
--- a/lib/_emerge/create_depgraph_params.py
+++ b/lib/_emerge/create_depgraph_params.py
@@ -1,7 +1,8 @@
-# Copyright 1999-2021 Gentoo Authors
+# Copyright 1999-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
import logging
+from portage import installation
from portage.util import writemsg_level
@@ -106,7 +107,12 @@ def create_depgraph_params(myopts, myaction):
myparams["ignore_soname_deps"] = myopts.get("--ignore-soname-deps", "y")
- dynamic_deps = myopts.get("--dynamic-deps", "y") != "n" and "--nodeps" not in myopts
+ dyn_deps_def = (
+ "y" if installation.TYPE == installation.TYPES.SOURCE else "@DYN_DEPS_DEFAULT@"
+ )
+ dynamic_deps = (
+ myopts.get("--dynamic-deps", dyn_deps_def) != "n" and "--nodeps" not in myopts
+ )
if dynamic_deps:
myparams["dynamic_deps"] = True
diff --git a/lib/_emerge/meson.build b/lib/_emerge/meson.build
new file mode 100644
index 000000000..672a9cbdb
--- /dev/null
+++ b/lib/_emerge/meson.build
@@ -0,0 +1,101 @@
+AbstractEbuildProcess_py = configure_file(
+ input : 'AbstractEbuildProcess.py',
+ output : 'AbstractEbuildProcess.py',
+ configuration : {'IPC' : get_option('ipc')}
+)
+
+create_depgraph_params_py = configure_file(
+ input : 'create_depgraph_params.py',
+ output : 'create_depgraph_params.py',
+ configuration : {'DYN_DEPS_DEFAULT' : get_option('gentoo-dev') ? 'n' : 'y'}
+)
+
+py.install_sources(
+ [
+ 'AbstractDepPriority.py',
+ AbstractEbuildProcess_py,
+ 'AbstractPollTask.py',
+ 'AsynchronousLock.py',
+ 'AsynchronousTask.py',
+ 'AtomArg.py',
+ 'BinpkgEnvExtractor.py',
+ 'BinpkgExtractorAsync.py',
+ 'BinpkgFetcher.py',
+ 'BinpkgPrefetcher.py',
+ 'BinpkgVerifier.py',
+ 'Binpkg.py',
+ 'BlockerCache.py',
+ 'BlockerDB.py',
+ 'BlockerDepPriority.py',
+ 'Blocker.py',
+ 'CompositeTask.py',
+ 'DepPriorityNormalRange.py',
+ 'DepPrioritySatisfiedRange.py',
+ 'DepPriority.py',
+ 'DependencyArg.py',
+ 'Dependency.py',
+ 'EbuildBinpkg.py',
+ 'EbuildBuildDir.py',
+ 'EbuildBuild.py',
+ 'EbuildExecuter.py',
+ 'EbuildFetcher.py',
+ 'EbuildFetchonly.py',
+ 'EbuildIpcDaemon.py',
+ 'EbuildMerge.py',
+ 'EbuildMetadataPhase.py',
+ 'EbuildPhase.py',
+ 'EbuildProcess.py',
+ 'EbuildSpawnProcess.py',
+ 'FakeVartree.py',
+ 'FifoIpcDaemon.py',
+ 'JobStatusDisplay.py',
+ 'MergeListItem.py',
+ 'MetadataRegen.py',
+ 'MiscFunctionsProcess.py',
+ 'PackageArg.py',
+ 'PackageMerge.py',
+ 'PackagePhase.py',
+ 'PackageUninstall.py',
+ 'PackageVirtualDbapi.py',
+ 'Package.py',
+ 'PipeReader.py',
+ 'PollScheduler.py',
+ 'ProgressHandler.py',
+ 'RootConfig.py',
+ 'Scheduler.py',
+ 'SequentialTaskQueue.py',
+ 'SetArg.py',
+ 'SpawnProcess.py',
+ 'SubProcess.py',
+ 'TaskSequence.py',
+ 'Task.py',
+ 'UninstallFailure.py',
+ 'UnmergeDepPriority.py',
+ 'UseFlagDisplay.py',
+ 'UserQuery.py',
+ 'actions.py',
+ 'chk_updated_cfg_files.py',
+ 'clear_caches.py',
+ 'countdown.py',
+ create_depgraph_params_py,
+ 'create_world_atom.py',
+ 'depgraph.py',
+ 'emergelog.py',
+ 'getloadavg.py',
+ 'help.py',
+ 'is_valid_package_atom.py',
+ 'main.py',
+ 'post_emerge.py',
+ 'search.py',
+ 'show_invalid_depstring_notice.py',
+ 'stdout_spinner.py',
+ 'unmerge.py',
+ '_find_deep_system_runtime_deps.py',
+ '_flush_elog_mod_echo.py',
+ '__init__.py',
+ ],
+ subdir : '_emerge',
+ pure : false
+)
+
+subdir('resolver')
diff --git a/lib/_emerge/resolver/meson.build b/lib/_emerge/resolver/meson.build
new file mode 100644
index 000000000..65b519433
--- /dev/null
+++ b/lib/_emerge/resolver/meson.build
@@ -0,0 +1,14 @@
+py.install_sources(
+ [
+ 'DbapiProvidesIndex.py',
+ 'backtracking.py',
+ 'circular_dependency.py',
+ 'output.py',
+ 'output_helpers.py',
+ 'package_tracker.py',
+ 'slot_collision.py',
+ '__init__.py',
+ ],
+ subdir : '_emerge/resolver',
+ pure : false
+)
diff --git a/lib/meson.build b/lib/meson.build
new file mode 100644
index 000000000..aa1a469ca
--- /dev/null
+++ b/lib/meson.build
@@ -0,0 +1,2 @@
+subdir('portage')
+subdir('_emerge')
diff --git a/lib/portage/__init__.py b/lib/portage/__init__.py
index aa7e69920..847a879cd 100644
--- a/lib/portage/__init__.py
+++ b/lib/portage/__init__.py
@@ -1,13 +1,13 @@
-# Copyright 1998-2021 Gentoo Authors
+# Copyright 1998-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# pylint: disable=ungrouped-imports
-VERSION = "HEAD"
-
# ===========================================================================
# START OF IMPORTS -- START OF IMPORTS -- START OF IMPORTS -- START OF IMPORT
# ===========================================================================
+from portage import installation
+
try:
import asyncio
import sys
@@ -716,7 +716,7 @@ def create_trees(
return trees
-if VERSION == "HEAD":
+if installation.TYPE == installation.TYPES.SOURCE:
class _LazyVersion(proxy.objectproxy.ObjectProxy):
def _get_target(self):
@@ -775,6 +775,9 @@ if VERSION == "HEAD":
VERSION = _LazyVersion()
+else:
+ VERSION = "@VERSION@"
+
_legacy_global_var_names = (
"archlist",
"db",
diff --git a/lib/portage/_compat_upgrade/meson.build b/lib/portage/_compat_upgrade/meson.build
new file mode 100644
index 000000000..178de8692
--- /dev/null
+++ b/lib/portage/_compat_upgrade/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'binpkg_compression.py',
+ 'binpkg_multi_instance.py',
+ 'default_locations.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/_compat_upgrade',
+ pure : false
+)
diff --git a/lib/portage/_emirrordist/meson.build b/lib/portage/_emirrordist/meson.build
new file mode 100644
index 000000000..9e097cee8
--- /dev/null
+++ b/lib/portage/_emirrordist/meson.build
@@ -0,0 +1,15 @@
+py.install_sources(
+ [
+ 'Config.py',
+ 'ContentDB.py',
+ 'DeletionIterator.py',
+ 'DeletionTask.py',
+ 'FetchIterator.py',
+ 'FetchTask.py',
+ 'MirrorDistTask.py',
+ 'main.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/_emirrordist',
+ pure : false
+)
diff --git a/lib/portage/_sets/meson.build b/lib/portage/_sets/meson.build
new file mode 100644
index 000000000..8e0a3aa5f
--- /dev/null
+++ b/lib/portage/_sets/meson.build
@@ -0,0 +1,15 @@
+py.install_sources(
+ [
+ 'ProfilePackageSet.py',
+ 'base.py',
+ 'dbapi.py',
+ 'files.py',
+ 'libs.py',
+ 'profiles.py',
+ 'security.py',
+ 'shell.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/_sets',
+ pure : false
+)
diff --git a/lib/portage/binrepo/meson.build b/lib/portage/binrepo/meson.build
new file mode 100644
index 000000000..b46c2afa9
--- /dev/null
+++ b/lib/portage/binrepo/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'config.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/binrepo',
+ pure : false
+)
diff --git a/lib/portage/cache/index/meson.build b/lib/portage/cache/index/meson.build
new file mode 100644
index 000000000..4f23d7e21
--- /dev/null
+++ b/lib/portage/cache/index/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'IndexStreamIterator.py',
+ 'pkg_desc_index.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/cache/index',
+ pure : false
+)
diff --git a/lib/portage/cache/meson.build b/lib/portage/cache/meson.build
new file mode 100644
index 000000000..1fc73f719
--- /dev/null
+++ b/lib/portage/cache/meson.build
@@ -0,0 +1,20 @@
+py.install_sources(
+ [
+ 'anydbm.py',
+ 'cache_errors.py',
+ 'ebuild_xattr.py',
+ 'flat_hash.py',
+ 'fs_template.py',
+ 'mappings.py',
+ 'metadata.py',
+ 'sqlite.py',
+ 'sql_template.py',
+ 'template.py',
+ 'volatile.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/cache',
+ pure : false
+)
+
+subdir('index')
diff --git a/lib/portage/const.py b/lib/portage/const.py
index f7168d996..bf310bb6e 100644
--- a/lib/portage/const.py
+++ b/lib/portage/const.py
@@ -4,6 +4,8 @@
import os
+from portage import installation
+
# ===========================================================================
# START OF CONSTANTS -- START OF CONSTANTS -- START OF CONSTANTS -- START OF
# ===========================================================================
@@ -54,25 +56,53 @@ NEWS_LIB_PATH = "var/lib/gentoo"
# these variables get EPREFIX prepended automagically when they are
# translated into their lowercase variants
DEPCACHE_PATH = f"/{CACHE_PATH}/dep"
-GLOBAL_CONFIG_PATH = "/usr/share/portage/config"
+
+if installation.TYPE == installation.TYPES.MODULE:
+ GLOBAL_CONFIG_PATH = "/share/portage/config"
+else:
+ GLOBAL_CONFIG_PATH = "/usr/share/portage/config"
# these variables are not used with target_root or config_root
# NOTE: Use realpath(__file__) so that python module symlinks in site-packages
# are followed back to the real location of the whole portage installation.
-# NOTE: Please keep PORTAGE_BASE_PATH in one line to help substitutions.
-# fmt:off
-PORTAGE_BASE_PATH = os.path.join(os.sep, os.sep.join(os.path.realpath(__file__.rstrip("co")).split(os.sep)[:-3]))
-# fmt:on
-PORTAGE_BIN_PATH = f"{PORTAGE_BASE_PATH}/bin"
+if installation.TYPE == installation.TYPES.SYSTEM:
+ PORTAGE_BASE_PATH = """@PORTAGE_BASE_PATH@"""
+elif installation.TYPE == installation.TYPES.MODULE:
+ PORTAGE_BASE_PATH = os.path.join(
+ os.path.realpath(__import__("sys").prefix), "lib/portage"
+ )
+else:
+ PORTAGE_BASE_PATH = os.path.join(
+ os.sep, *os.path.realpath(__file__).split(os.sep)[:-3]
+ )
+
+if installation.TYPE == installation.TYPES.SYSTEM:
+ PORTAGE_BIN_PATH = """@PORTAGE_BIN_PATH@"""
+else:
+ PORTAGE_BIN_PATH = f"{PORTAGE_BASE_PATH}/bin"
+
+# The EPREFIX for the current install is hardcoded here, but access to this
+# constant should be minimal, in favor of access via the EPREFIX setting of
+# a config instance (since it's possible to contruct a config instance with
+# a different EPREFIX). Therefore, the EPREFIX constant should *NOT* be used
+# in the definition of any other constants within this file.
+if installation.TYPE == installation.TYPES.SYSTEM:
+ EPREFIX = BINARY_PREFIX = "@EPREFIX@"
+elif installation.TYPE == installation.TYPES.MODULE:
+ EPREFIX = __import__("sys").prefix
+ BINARY_PREFIX = ""
+else:
+ EPREFIX = BINARY_PREFIX = ""
+
PORTAGE_PYM_PATH = os.path.realpath(os.path.join(__file__, "../.."))
LOCALE_DATA_PATH = f"{PORTAGE_BASE_PATH}/locale" # FIXME: not used
EBUILD_SH_BINARY = f"{PORTAGE_BIN_PATH}/ebuild.sh"
MISC_SH_BINARY = f"{PORTAGE_BIN_PATH}/misc-functions.sh"
-SANDBOX_BINARY = "/usr/bin/sandbox"
-FAKEROOT_BINARY = "/usr/bin/fakeroot"
-BASH_BINARY = "/bin/bash"
-MOVE_BINARY = "/bin/mv"
-PRELINK_BINARY = "/usr/sbin/prelink"
+SANDBOX_BINARY = f"{BINARY_PREFIX}/usr/bin/sandbox"
+FAKEROOT_BINARY = f"{BINARY_PREFIX}/usr/bin/fakeroot"
+BASH_BINARY = f"{BINARY_PREFIX}/bin/bash"
+MOVE_BINARY = f"{BINARY_PREFIX}/bin/mv"
+PRELINK_BINARY = f"{BINARY_PREFIX}/usr/sbin/prelink"
INVALID_ENV_FILE = "/etc/spork/is/not/valid/profile.env"
MERGING_IDENTIFIER = "-MERGING-"
@@ -227,14 +257,7 @@ MANIFEST2_HASH_DEFAULT = "BLAKE2B"
MANIFEST2_IDENTIFIERS = ("AUX", "MISC", "DIST", "EBUILD")
-# The EPREFIX for the current install is hardcoded here, but access to this
-# constant should be minimal, in favor of access via the EPREFIX setting of
-# a config instance (since it's possible to contruct a config instance with
-# a different EPREFIX). Therefore, the EPREFIX constant should *NOT* be used
-# in the definition of any other constants within this file.
-EPREFIX = ""
-
-# pick up EPREFIX from the environment if set
+# Redefine EPREFIX from the environment if set
if "PORTAGE_OVERRIDE_EPREFIX" in os.environ:
EPREFIX = os.environ["PORTAGE_OVERRIDE_EPREFIX"]
if EPREFIX:
diff --git a/lib/portage/dbapi/meson.build b/lib/portage/dbapi/meson.build
new file mode 100644
index 000000000..781e29f05
--- /dev/null
+++ b/lib/portage/dbapi/meson.build
@@ -0,0 +1,22 @@
+py.install_sources(
+ [
+ 'DummyTree.py',
+ 'IndexedPortdb.py',
+ 'IndexedVardb.py',
+ 'bintree.py',
+ 'cpv_expand.py',
+ 'dep_expand.py',
+ 'porttree.py',
+ 'vartree.py',
+ 'virtual.py',
+ '_ContentsCaseSensitivityManager.py',
+ '_MergeProcess.py',
+ '_SyncfsProcess.py',
+ '_VdbMetadataDelta.py',
+ '_expand_new_virt.py',
+ '_similar_name_search.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/dbapi',
+ pure : false
+)
diff --git a/lib/portage/dep/meson.build b/lib/portage/dep/meson.build
new file mode 100644
index 000000000..18a605c60
--- /dev/null
+++ b/lib/portage/dep/meson.build
@@ -0,0 +1,12 @@
+py.install_sources(
+ [
+ 'dep_check.py',
+ '_dnf.py',
+ '_slot_operator.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/dep',
+ pure : false
+)
+
+subdir('soname')
diff --git a/lib/portage/dep/soname/meson.build b/lib/portage/dep/soname/meson.build
new file mode 100644
index 000000000..8f62d24fa
--- /dev/null
+++ b/lib/portage/dep/soname/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'SonameAtom.py',
+ 'multilib_category.py',
+ 'parse.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/dep/soname',
+ pure : false
+)
diff --git a/lib/portage/elog/meson.build b/lib/portage/elog/meson.build
new file mode 100644
index 000000000..4f3fd150c
--- /dev/null
+++ b/lib/portage/elog/meson.build
@@ -0,0 +1,16 @@
+py.install_sources(
+ [
+ 'filtering.py',
+ 'messages.py',
+ 'mod_custom.py',
+ 'mod_echo.py',
+ 'mod_mail.py',
+ 'mod_mail_summary.py',
+ 'mod_save.py',
+ 'mod_save_summary.py',
+ 'mod_syslog.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/elog',
+ pure : false
+)
diff --git a/lib/portage/emaint/meson.build b/lib/portage/emaint/meson.build
new file mode 100644
index 000000000..1700bad49
--- /dev/null
+++ b/lib/portage/emaint/meson.build
@@ -0,0 +1,11 @@
+py.install_sources(
+ [
+ 'defaults.py',
+ 'main.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint',
+ pure : false
+)
+
+subdir('modules')
diff --git a/lib/portage/emaint/modules/binhost/meson.build b/lib/portage/emaint/modules/binhost/meson.build
new file mode 100644
index 000000000..63927d393
--- /dev/null
+++ b/lib/portage/emaint/modules/binhost/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'binhost.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/binhost',
+ pure : false
+)
diff --git a/lib/portage/emaint/modules/config/meson.build b/lib/portage/emaint/modules/config/meson.build
new file mode 100644
index 000000000..d1b6ac950
--- /dev/null
+++ b/lib/portage/emaint/modules/config/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'config.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/config',
+ pure : false
+)
diff --git a/lib/portage/emaint/modules/logs/meson.build b/lib/portage/emaint/modules/logs/meson.build
new file mode 100644
index 000000000..856a5705f
--- /dev/null
+++ b/lib/portage/emaint/modules/logs/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'logs.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/logs',
+ pure : false
+)
diff --git a/lib/portage/emaint/modules/merges/meson.build b/lib/portage/emaint/modules/merges/meson.build
new file mode 100644
index 000000000..3223104b2
--- /dev/null
+++ b/lib/portage/emaint/modules/merges/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'merges.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/merges',
+ pure : false
+)
diff --git a/lib/portage/emaint/modules/meson.build b/lib/portage/emaint/modules/meson.build
new file mode 100644
index 000000000..d92b4af48
--- /dev/null
+++ b/lib/portage/emaint/modules/meson.build
@@ -0,0 +1,16 @@
+py.install_sources(
+ [
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules',
+ pure : false
+)
+
+subdir('binhost')
+subdir('config')
+subdir('logs')
+subdir('merges')
+subdir('move')
+subdir('resume')
+subdir('sync')
+subdir('world')
diff --git a/lib/portage/emaint/modules/move/meson.build b/lib/portage/emaint/modules/move/meson.build
new file mode 100644
index 000000000..4f16ddc70
--- /dev/null
+++ b/lib/portage/emaint/modules/move/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'move.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/move',
+ pure : false
+)
diff --git a/lib/portage/emaint/modules/resume/meson.build b/lib/portage/emaint/modules/resume/meson.build
new file mode 100644
index 000000000..e6bf8c12b
--- /dev/null
+++ b/lib/portage/emaint/modules/resume/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'resume.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/resume',
+ pure : false
+)
diff --git a/lib/portage/emaint/modules/sync/meson.build b/lib/portage/emaint/modules/sync/meson.build
new file mode 100644
index 000000000..29617aaef
--- /dev/null
+++ b/lib/portage/emaint/modules/sync/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'sync.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/sync',
+ pure : false
+)
diff --git a/lib/portage/emaint/modules/world/meson.build b/lib/portage/emaint/modules/world/meson.build
new file mode 100644
index 000000000..383457038
--- /dev/null
+++ b/lib/portage/emaint/modules/world/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'world.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/emaint/modules/world',
+ pure : false
+)
diff --git a/lib/portage/env/meson.build b/lib/portage/env/meson.build
new file mode 100644
index 000000000..26c56e34f
--- /dev/null
+++ b/lib/portage/env/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'config.py',
+ 'loaders.py',
+ 'validators.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/env',
+ pure : false
+)
diff --git a/lib/portage/installation.py b/lib/portage/installation.py
new file mode 100644
index 000000000..53396834c
--- /dev/null
+++ b/lib/portage/installation.py
@@ -0,0 +1,21 @@
+# portage: Installation
+# Copyright 2023 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+from enum import Enum
+
+TYPES = Enum(
+ "InstallationType",
+ [
+ "SOURCE", # Portage is not installed, but running from its source tree.
+ "MODULE", # Portage is installed solely as a Python module.
+ "SYSTEM", # Portage is fully installed to the system, possibly prefixed.
+ ],
+)
+
+if "@INSTALL_TYPE@" == "MODULE":
+ TYPE = TYPES.MODULE
+elif "@INSTALL_TYPE@" == "SYSTEM":
+ TYPE = TYPES.SYSTEM
+else:
+ TYPE = TYPES.SOURCE
diff --git a/lib/portage/meson.build b/lib/portage/meson.build
new file mode 100644
index 000000000..31eda1327
--- /dev/null
+++ b/lib/portage/meson.build
@@ -0,0 +1,74 @@
+const_py = configure_file(
+ input : 'const.py',
+ output : 'const.py',
+ configuration : conf_data
+)
+
+installation_py = configure_file(
+ input : 'installation.py',
+ output : 'installation.py',
+ configuration : conf_data
+)
+
+__init__py = configure_file(
+ input : '__init__.py',
+ output : '__init__.py',
+ configuration : conf_data
+)
+
+py.install_sources(
+ [
+ 'binpkg.py',
+ 'checksum.py',
+ const_py,
+ 'cvstree.py',
+ 'data.py',
+ 'debug.py',
+ 'dispatch_conf.py',
+ 'eapi.py',
+ 'eclass_cache.py',
+ 'exception.py',
+ 'getbinpkg.py',
+ 'glsa.py',
+ 'gpg.py',
+ 'gpkg.py',
+ installation_py,
+ 'localization.py',
+ 'locks.py',
+ 'mail.py',
+ 'manifest.py',
+ 'metadata.py',
+ 'module.py',
+ 'news.py',
+ 'output.py',
+ 'process.py',
+ 'progress.py',
+ 'update.py',
+ 'versions.py',
+ 'xpak.py',
+ '_global_updates.py',
+ '_legacy_globals.py',
+ '_selinux.py',
+ __init__py,
+ ],
+ subdir : 'portage',
+ pure : false
+)
+
+subdir('binrepo')
+subdir('cache')
+subdir('dbapi')
+subdir('dep')
+subdir('elog')
+subdir('emaint')
+subdir('env')
+subdir('package')
+subdir('proxy')
+subdir('repository')
+subdir('sync')
+subdir('tests')
+subdir('util')
+subdir('xml')
+subdir('_compat_upgrade')
+subdir('_emirrordist')
+subdir('_sets')
diff --git a/lib/portage/package/ebuild/_config/meson.build b/lib/portage/package/ebuild/_config/meson.build
new file mode 100644
index 000000000..053542917
--- /dev/null
+++ b/lib/portage/package/ebuild/_config/meson.build
@@ -0,0 +1,17 @@
+py.install_sources(
+ [
+ 'KeywordsManager.py',
+ 'LicenseManager.py',
+ 'LocationsManager.py',
+ 'MaskManager.py',
+ 'UseManager.py',
+ 'VirtualsManager.py',
+ 'env_var_validation.py',
+ 'features_set.py',
+ 'helper.py',
+ 'special_env_vars.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/package/ebuild/_config',
+ pure : false
+)
diff --git a/lib/portage/package/ebuild/_ipc/meson.build b/lib/portage/package/ebuild/_ipc/meson.build
new file mode 100644
index 000000000..702130c61
--- /dev/null
+++ b/lib/portage/package/ebuild/_ipc/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'ExitCommand.py',
+ 'IpcCommand.py',
+ 'QueryCommand.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/package/ebuild/_ipc',
+ pure : false
+)
diff --git a/lib/portage/package/ebuild/_parallel_manifest/meson.build b/lib/portage/package/ebuild/_parallel_manifest/meson.build
new file mode 100644
index 000000000..88aa3b29b
--- /dev/null
+++ b/lib/portage/package/ebuild/_parallel_manifest/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'ManifestProcess.py',
+ 'ManifestScheduler.py',
+ 'ManifestTask.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/package/ebuild/_parallel_manifest',
+ pure : false
+)
diff --git a/lib/portage/package/ebuild/meson.build b/lib/portage/package/ebuild/meson.build
new file mode 100644
index 000000000..cf122a911
--- /dev/null
+++ b/lib/portage/package/ebuild/meson.build
@@ -0,0 +1,23 @@
+py.install_sources(
+ [
+ 'config.py',
+ 'deprecated_profile_check.py',
+ 'digestcheck.py',
+ 'digestgen.py',
+ 'doebuild.py',
+ 'fetch.py',
+ 'getmaskingreason.py',
+ 'getmaskingstatus.py',
+ 'prepare_build_dirs.py',
+ 'profile_iuse.py',
+ '_metadata_invalid.py',
+ '_spawn_nofetch.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/package/ebuild',
+ pure : false
+)
+
+subdir('_config')
+subdir('_ipc')
+subdir('_parallel_manifest')
diff --git a/lib/portage/package/meson.build b/lib/portage/package/meson.build
new file mode 100644
index 000000000..d3be4f936
--- /dev/null
+++ b/lib/portage/package/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ '__init__.py',
+ ],
+ subdir : 'portage/package',
+ pure : false
+)
+
+subdir('ebuild')
diff --git a/lib/portage/proxy/meson.build b/lib/portage/proxy/meson.build
new file mode 100644
index 000000000..d23c944c4
--- /dev/null
+++ b/lib/portage/proxy/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'lazyimport.py',
+ 'objectproxy.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/proxy',
+ pure : false
+)
diff --git a/lib/portage/repository/meson.build b/lib/portage/repository/meson.build
new file mode 100644
index 000000000..77b82a526
--- /dev/null
+++ b/lib/portage/repository/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'config.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/repository',
+ pure : false
+)
+
+subdir('storage')
diff --git a/lib/portage/repository/storage/meson.build b/lib/portage/repository/storage/meson.build
new file mode 100644
index 000000000..b5a5208f8
--- /dev/null
+++ b/lib/portage/repository/storage/meson.build
@@ -0,0 +1,11 @@
+py.install_sources(
+ [
+ 'hardlink_quarantine.py',
+ 'hardlink_rcu.py',
+ 'inplace.py',
+ 'interface.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/repository/storage',
+ pure : false
+)
diff --git a/lib/portage/sync/meson.build b/lib/portage/sync/meson.build
new file mode 100644
index 000000000..d2fd9fdc3
--- /dev/null
+++ b/lib/portage/sync/meson.build
@@ -0,0 +1,14 @@
+py.install_sources(
+ [
+ 'config_checks.py',
+ 'controller.py',
+ 'getaddrinfo_validate.py',
+ 'old_tree_timestamp.py',
+ 'syncbase.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/sync',
+ pure : false
+)
+
+subdir('modules')
diff --git a/lib/portage/sync/modules/cvs/meson.build b/lib/portage/sync/modules/cvs/meson.build
new file mode 100644
index 000000000..520d6d810
--- /dev/null
+++ b/lib/portage/sync/modules/cvs/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'cvs.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/sync/modules/cvs',
+ pure : false
+)
diff --git a/lib/portage/sync/modules/git/meson.build b/lib/portage/sync/modules/git/meson.build
new file mode 100644
index 000000000..8a32ad375
--- /dev/null
+++ b/lib/portage/sync/modules/git/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'git.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/sync/modules/git',
+ pure : false
+)
diff --git a/lib/portage/sync/modules/mercurial/meson.build b/lib/portage/sync/modules/mercurial/meson.build
new file mode 100644
index 000000000..c46f26b6f
--- /dev/null
+++ b/lib/portage/sync/modules/mercurial/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'mercurial.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/sync/modules/mercurial',
+ pure : false
+)
diff --git a/lib/portage/sync/modules/meson.build b/lib/portage/sync/modules/meson.build
new file mode 100644
index 000000000..d38a45c9f
--- /dev/null
+++ b/lib/portage/sync/modules/meson.build
@@ -0,0 +1,14 @@
+py.install_sources(
+ [
+ '__init__.py',
+ ],
+ subdir : 'portage/sync/modules',
+ pure : false
+)
+
+subdir('cvs')
+subdir('git')
+subdir('mercurial')
+subdir('rsync')
+subdir('svn')
+subdir('webrsync')
diff --git a/lib/portage/sync/modules/rsync/meson.build b/lib/portage/sync/modules/rsync/meson.build
new file mode 100644
index 000000000..49df8135b
--- /dev/null
+++ b/lib/portage/sync/modules/rsync/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'rsync.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/sync/modules/rsync',
+ pure : false
+)
diff --git a/lib/portage/sync/modules/svn/meson.build b/lib/portage/sync/modules/svn/meson.build
new file mode 100644
index 000000000..0c5ea2c40
--- /dev/null
+++ b/lib/portage/sync/modules/svn/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'svn.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/sync/modules/svn',
+ pure : false
+)
diff --git a/lib/portage/sync/modules/webrsync/meson.build b/lib/portage/sync/modules/webrsync/meson.build
new file mode 100644
index 000000000..047d82d2e
--- /dev/null
+++ b/lib/portage/sync/modules/webrsync/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'webrsync.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/sync/modules/webrsync',
+ pure : false
+)
diff --git a/lib/portage/tests/bin/meson.build b/lib/portage/tests/bin/meson.build
new file mode 100644
index 000000000..eb7713049
--- /dev/null
+++ b/lib/portage/tests/bin/meson.build
@@ -0,0 +1,14 @@
+py.install_sources(
+ [
+ 'setup_env.py',
+ 'test_dobin.py',
+ 'test_dodir.py',
+ 'test_doins.py',
+ 'test_eapi7_ver_funcs.py',
+ 'test_filter_bash_env.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/bin',
+ pure : false
+)
diff --git a/lib/portage/tests/conftest.py b/lib/portage/tests/conftest.py
index 1913d1f06..88fc72b15 100644
--- a/lib/portage/tests/conftest.py
+++ b/lib/portage/tests/conftest.py
@@ -11,7 +11,6 @@ import signal
import tempfile
import shutil
import sys
-from distutils.dir_util import copy_tree
import pytest
@@ -68,8 +67,10 @@ def prepare_environment():
# Copy GPG test keys to temporary directory
gpg_path = tempfile.mkdtemp(prefix="gpg_")
- copy_tree(
- os.path.join(os.path.dirname(os.path.realpath(__file__)), ".gnupg"), gpg_path
+ shutil.copytree(
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), ".gnupg"),
+ gpg_path,
+ dirs_exist_ok=True,
)
os.chmod(gpg_path, 0o700)
diff --git a/lib/portage/tests/dbapi/meson.build b/lib/portage/tests/dbapi/meson.build
new file mode 100644
index 000000000..ddb83ba72
--- /dev/null
+++ b/lib/portage/tests/dbapi/meson.build
@@ -0,0 +1,12 @@
+py.install_sources(
+ [
+ 'test_auxdb.py',
+ 'test_bintree.py',
+ 'test_fakedbapi.py',
+ 'test_portdb_cache.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/dbapi',
+ pure : false
+)
diff --git a/lib/portage/tests/dep/meson.build b/lib/portage/tests/dep/meson.build
new file mode 100644
index 000000000..f96018917
--- /dev/null
+++ b/lib/portage/tests/dep/meson.build
@@ -0,0 +1,28 @@
+py.install_sources(
+ [
+ 'testAtom.py',
+ 'testCheckRequiredUse.py',
+ 'testExtendedAtomDict.py',
+ 'testExtractAffectingUSE.py',
+ 'testStandalone.py',
+ 'test_best_match_to_list.py',
+ 'test_dep_getcpv.py',
+ 'test_dep_getrepo.py',
+ 'test_dep_getslot.py',
+ 'test_dep_getusedeps.py',
+ 'test_dnf_convert.py',
+ 'test_get_operator.py',
+ 'test_get_required_use_flags.py',
+ 'test_isjustname.py',
+ 'test_isvalidatom.py',
+ 'test_match_from_list.py',
+ 'test_overlap_dnf.py',
+ 'test_paren_reduce.py',
+ 'test_soname_atom_pickle.py',
+ 'test_use_reduce.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/dep',
+ pure : false
+)
diff --git a/lib/portage/tests/ebuild/meson.build b/lib/portage/tests/ebuild/meson.build
new file mode 100644
index 000000000..c19a3181a
--- /dev/null
+++ b/lib/portage/tests/ebuild/meson.build
@@ -0,0 +1,17 @@
+py.install_sources(
+ [
+ 'test_array_fromfile_eof.py',
+ 'test_config.py',
+ 'test_doebuild_fd_pipes.py',
+ 'test_doebuild_spawn.py',
+ 'test_fetch.py',
+ 'test_ipc_daemon.py',
+ 'test_shell_quote.py',
+ 'test_spawn.py',
+ 'test_use_expand_incremental.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/ebuild',
+ pure : false
+)
diff --git a/lib/portage/tests/emerge/meson.build b/lib/portage/tests/emerge/meson.build
new file mode 100644
index 000000000..faf6fbb7a
--- /dev/null
+++ b/lib/portage/tests/emerge/meson.build
@@ -0,0 +1,14 @@
+py.install_sources(
+ [
+ 'test_actions.py',
+ 'test_config_protect.py',
+ 'test_emerge_blocker_file_collision.py',
+ 'test_emerge_slot_abi.py',
+ 'test_global_updates.py',
+ 'test_simple.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/emerge',
+ pure : false
+)
diff --git a/lib/portage/tests/env/config/meson.build b/lib/portage/tests/env/config/meson.build
new file mode 100644
index 000000000..e661f6bac
--- /dev/null
+++ b/lib/portage/tests/env/config/meson.build
@@ -0,0 +1,12 @@
+py.install_sources(
+ [
+ 'test_PackageKeywordsFile.py',
+ 'test_PackageMaskFile.py',
+ 'test_PackageUseFile.py',
+ 'test_PortageModulesFile.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/env/config',
+ pure : false
+)
diff --git a/lib/portage/tests/env/meson.build b/lib/portage/tests/env/meson.build
new file mode 100644
index 000000000..c56a6b47c
--- /dev/null
+++ b/lib/portage/tests/env/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/env',
+ pure : false
+)
+
+subdir('config')
diff --git a/lib/portage/tests/glsa/meson.build b/lib/portage/tests/glsa/meson.build
new file mode 100644
index 000000000..4bfdc0873
--- /dev/null
+++ b/lib/portage/tests/glsa/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_security_set.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/glsa',
+ pure : false
+)
diff --git a/lib/portage/tests/gpkg/meson.build b/lib/portage/tests/gpkg/meson.build
new file mode 100644
index 000000000..f5d981936
--- /dev/null
+++ b/lib/portage/tests/gpkg/meson.build
@@ -0,0 +1,15 @@
+py.install_sources(
+ [
+ 'test_gpkg_checksum.py',
+ 'test_gpkg_gpg.py',
+ 'test_gpkg_metadata_update.py',
+ 'test_gpkg_metadata_url.py',
+ 'test_gpkg_path.py',
+ 'test_gpkg_size.py',
+ 'test_gpkg_stream.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/gpkg',
+ pure : false
+)
diff --git a/lib/portage/tests/lafilefixer/meson.build b/lib/portage/tests/lafilefixer/meson.build
new file mode 100644
index 000000000..93969d1c9
--- /dev/null
+++ b/lib/portage/tests/lafilefixer/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_lafilefixer.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/lafilefixer',
+ pure : false
+)
diff --git a/lib/portage/tests/lazyimport/meson.build b/lib/portage/tests/lazyimport/meson.build
new file mode 100644
index 000000000..6d177b9ad
--- /dev/null
+++ b/lib/portage/tests/lazyimport/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'test_lazy_import_portage_baseline.py',
+ 'test_preload_portage_submodules.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/lazyimport',
+ pure : false
+)
diff --git a/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py b/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py
index a7cd93d4f..cbeba37b5 100644
--- a/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py
+++ b/lib/portage/tests/lazyimport/test_lazy_import_portage_baseline.py
@@ -18,6 +18,7 @@ class LazyImportPortageBaselineTestCase(TestCase):
_baseline_imports = frozenset(
[
"portage.const",
+ "portage.installation",
"portage.localization",
"portage.proxy",
"portage.proxy.lazyimport",
diff --git a/lib/portage/tests/lint/meson.build b/lib/portage/tests/lint/meson.build
new file mode 100644
index 000000000..5d7a72675
--- /dev/null
+++ b/lib/portage/tests/lint/meson.build
@@ -0,0 +1,12 @@
+py.install_sources(
+ [
+ 'metadata.py',
+ 'test_bash_syntax.py',
+ 'test_compile_modules.py',
+ 'test_import_modules.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/lint',
+ pure : false
+)
diff --git a/lib/portage/tests/locks/meson.build b/lib/portage/tests/locks/meson.build
new file mode 100644
index 000000000..17ec727a6
--- /dev/null
+++ b/lib/portage/tests/locks/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'test_asynchronous_lock.py',
+ 'test_lock_nonblock.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/locks',
+ pure : false
+)
diff --git a/lib/portage/tests/meson.build b/lib/portage/tests/meson.build
new file mode 100644
index 000000000..86e8e71ce
--- /dev/null
+++ b/lib/portage/tests/meson.build
@@ -0,0 +1,32 @@
+py.install_sources(
+ [
+ 'conftest.py',
+ 'runTests.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/tests',
+ pure : false
+)
+
+subdir('bin')
+subdir('dbapi')
+subdir('dep')
+subdir('ebuild')
+subdir('emerge')
+subdir('env')
+subdir('glsa')
+subdir('gpkg')
+subdir('lafilefixer')
+subdir('lazyimport')
+subdir('lint')
+subdir('locks')
+subdir('news')
+subdir('process')
+subdir('resolver')
+subdir('sets')
+subdir('sync')
+subdir('unicode')
+subdir('update')
+subdir('util')
+subdir('versions')
+subdir('xpak')
diff --git a/lib/portage/tests/news/meson.build b/lib/portage/tests/news/meson.build
new file mode 100644
index 000000000..25e4f9ef4
--- /dev/null
+++ b/lib/portage/tests/news/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_NewsItem.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/news',
+ pure : false
+)
diff --git a/lib/portage/tests/process/meson.build b/lib/portage/tests/process/meson.build
new file mode 100644
index 000000000..08e51d770
--- /dev/null
+++ b/lib/portage/tests/process/meson.build
@@ -0,0 +1,16 @@
+py.install_sources(
+ [
+ 'test_AsyncFunction.py',
+ 'test_PipeLogger.py',
+ 'test_PopenProcessBlockingIO.py',
+ 'test_PopenProcess.py',
+ 'test_poll.py',
+ 'test_spawn_fail_e2big.py',
+ 'test_spawn_warn_large_env.py',
+ 'test_unshare_net.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/process',
+ pure : false
+)
diff --git a/lib/portage/tests/resolver/binpkg_multi_instance/meson.build b/lib/portage/tests/resolver/binpkg_multi_instance/meson.build
new file mode 100644
index 000000000..7c4306e4a
--- /dev/null
+++ b/lib/portage/tests/resolver/binpkg_multi_instance/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'test_build_id_profile_format.py',
+ 'test_rebuilt_binaries.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/resolver/binpkg_multi_instance',
+ pure : false
+)
diff --git a/lib/portage/tests/resolver/meson.build b/lib/portage/tests/resolver/meson.build
new file mode 100644
index 000000000..5653e5066
--- /dev/null
+++ b/lib/portage/tests/resolver/meson.build
@@ -0,0 +1,96 @@
+py.install_sources(
+ [
+ 'ResolverPlayground.py',
+ 'test_aggressive_backtrack_downgrade.py',
+ 'test_autounmask.py',
+ 'test_autounmask_binpkg_use.py',
+ 'test_autounmask_keep_keywords.py',
+ 'test_autounmask_multilib_use.py',
+ 'test_autounmask_parent.py',
+ 'test_autounmask_use_backtrack.py',
+ 'test_autounmask_use_breakage.py',
+ 'test_autounmask_use_slot_conflict.py',
+ 'test_backtracking.py',
+ 'test_bdeps.py',
+ 'test_binary_pkg_ebuild_visibility.py',
+ 'test_blocker.py',
+ 'test_changed_deps.py',
+ 'test_circular_choices.py',
+ 'test_circular_choices_rust.py',
+ 'test_circular_dependencies.py',
+ 'test_complete_graph.py',
+ 'test_complete_if_new_subslot_without_revbump.py',
+ 'test_depclean.py',
+ 'test_depclean_order.py',
+ 'test_depclean_slot_unavailable.py',
+ 'test_depth.py',
+ 'test_disjunctive_depend_order.py',
+ 'test_eapi.py',
+ 'test_features_test_use.py',
+ 'test_imagemagick_graphicsmagick.py',
+ 'test_installkernel.py',
+ 'test_keywords.py',
+ 'test_merge_order.py',
+ 'test_missing_iuse_and_evaluated_atoms.py',
+ 'test_multirepo.py',
+ 'test_multislot.py',
+ 'test_old_dep_chain_display.py',
+ 'test_onlydeps.py',
+ 'test_onlydeps_circular.py',
+ 'test_onlydeps_ideps.py',
+ 'test_onlydeps_minimal.py',
+ 'test_or_choices.py',
+ 'test_or_downgrade_installed.py',
+ 'test_or_upgrade_installed.py',
+ 'test_output.py',
+ 'test_package_tracker.py',
+ 'test_perl_rebuild_bug.py',
+ 'test_profile_default_eapi.py',
+ 'test_profile_package_set.py',
+ 'test_rebuild.py',
+ 'test_regular_slot_change_without_revbump.py',
+ 'test_required_use.py',
+ 'test_runtime_cycle_merge_order.py',
+ 'test_simple.py',
+ 'test_slot_abi.py',
+ 'test_slot_abi_downgrade.py',
+ 'test_slot_change_without_revbump.py',
+ 'test_slot_collisions.py',
+ 'test_slot_conflict_blocked_prune.py',
+ 'test_slot_conflict_force_rebuild.py',
+ 'test_slot_conflict_mask_update.py',
+ 'test_slot_conflict_rebuild.py',
+ 'test_slot_conflict_unsatisfied_deep_deps.py',
+ 'test_slot_conflict_update.py',
+ 'test_slot_conflict_update_virt.py',
+ 'test_slot_operator_autounmask.py',
+ 'test_slot_operator_bdeps.py',
+ 'test_slot_operator_complete_graph.py',
+ 'test_slot_operator_exclusive_slots.py',
+ 'test_slot_operator_missed_update.py',
+ 'test_slot_operator_rebuild.py',
+ 'test_slot_operator_required_use.py',
+ 'test_slot_operator_reverse_deps.py',
+ 'test_slot_operator_runtime_pkg_mask.py',
+ 'test_slot_operator_unsatisfied.py',
+ 'test_slot_operator_unsolved.py',
+ 'test_slot_operator_update_probe_parent_downgrade.py',
+ 'test_solve_non_slot_operator_slot_conflicts.py',
+ 'test_targetroot.py',
+ 'test_unmerge_order.py',
+ 'test_unnecessary_slot_upgrade.py',
+ 'test_update.py',
+ 'test_useflags.py',
+ 'test_use_dep_defaults.py',
+ 'test_virtual_minimize_children.py',
+ 'test_virtual_slot.py',
+ 'test_with_test_deps.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/resolver',
+ pure : false
+)
+
+subdir('binpkg_multi_instance')
+subdir('soname')
diff --git a/lib/portage/tests/resolver/soname/meson.build b/lib/portage/tests/resolver/soname/meson.build
new file mode 100644
index 000000000..3c3245bcc
--- /dev/null
+++ b/lib/portage/tests/resolver/soname/meson.build
@@ -0,0 +1,19 @@
+py.install_sources(
+ [
+ 'test_autounmask.py',
+ 'test_depclean.py',
+ 'test_downgrade.py',
+ 'test_or_choices.py',
+ 'test_reinstall.py',
+ 'test_skip_update.py',
+ 'test_slot_conflict_reinstall.py',
+ 'test_slot_conflict_update.py',
+ 'test_soname_provided.py',
+ 'test_unsatisfiable.py',
+ 'test_unsatisfied.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/resolver/soname',
+ pure : false
+)
diff --git a/lib/portage/tests/runTests.py b/lib/portage/tests/runTests.py
index bf4c2a7c5..36ea3a791 100644
--- a/lib/portage/tests/runTests.py
+++ b/lib/portage/tests/runTests.py
@@ -11,7 +11,6 @@ import signal
import tempfile
import shutil
import sys
-from distutils.dir_util import copy_tree
def debug_signal(signum, frame):
@@ -63,7 +62,11 @@ if insert_bin_path:
# Copy GPG test keys to temporary directory
gpg_path = tempfile.mkdtemp(prefix="gpg_")
-copy_tree(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".gnupg"), gpg_path)
+shutil.copytree(
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), ".gnupg"),
+ gpg_path,
+ dirs_exist_ok=True,
+)
os.chmod(gpg_path, 0o700)
os.environ["PORTAGE_GNUPGHOME"] = gpg_path
diff --git a/lib/portage/tests/sets/base/meson.build b/lib/portage/tests/sets/base/meson.build
new file mode 100644
index 000000000..ba15a8213
--- /dev/null
+++ b/lib/portage/tests/sets/base/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'testInternalPackageSet.py',
+ 'testVariableSet.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/sets/base',
+ pure : false
+)
diff --git a/lib/portage/tests/sets/files/meson.build b/lib/portage/tests/sets/files/meson.build
new file mode 100644
index 000000000..0ac7449db
--- /dev/null
+++ b/lib/portage/tests/sets/files/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'testConfigFileSet.py',
+ 'testStaticFileSet.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/sets/files',
+ pure : false
+)
diff --git a/lib/portage/tests/sets/meson.build b/lib/portage/tests/sets/meson.build
new file mode 100644
index 000000000..a7df0bd82
--- /dev/null
+++ b/lib/portage/tests/sets/meson.build
@@ -0,0 +1,12 @@
+py.install_sources(
+ [
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/sets',
+ pure : false
+)
+
+subdir('base')
+subdir('files')
+subdir('shell')
diff --git a/lib/portage/tests/sets/shell/meson.build b/lib/portage/tests/sets/shell/meson.build
new file mode 100644
index 000000000..6044f3ebe
--- /dev/null
+++ b/lib/portage/tests/sets/shell/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'testShell.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/sets/shell',
+ pure : false
+)
diff --git a/lib/portage/tests/sync/meson.build b/lib/portage/tests/sync/meson.build
new file mode 100644
index 000000000..9de7cc551
--- /dev/null
+++ b/lib/portage/tests/sync/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_sync_local.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/sync',
+ pure : false
+)
diff --git a/lib/portage/tests/unicode/meson.build b/lib/portage/tests/unicode/meson.build
new file mode 100644
index 000000000..94303a376
--- /dev/null
+++ b/lib/portage/tests/unicode/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_string_format.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/unicode',
+ pure : false
+)
diff --git a/lib/portage/tests/update/meson.build b/lib/portage/tests/update/meson.build
new file mode 100644
index 000000000..0c5fb01a9
--- /dev/null
+++ b/lib/portage/tests/update/meson.build
@@ -0,0 +1,11 @@
+py.install_sources(
+ [
+ 'test_move_ent.py',
+ 'test_move_slot_ent.py',
+ 'test_update_dbentry.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/update',
+ pure : false
+)
diff --git a/lib/portage/tests/util/dyn_libs/meson.build b/lib/portage/tests/util/dyn_libs/meson.build
new file mode 100644
index 000000000..51fc7c381
--- /dev/null
+++ b/lib/portage/tests/util/dyn_libs/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_soname_deps.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/util/dyn_libs',
+ pure : false
+)
diff --git a/lib/portage/tests/util/eventloop/meson.build b/lib/portage/tests/util/eventloop/meson.build
new file mode 100644
index 000000000..77ce25afe
--- /dev/null
+++ b/lib/portage/tests/util/eventloop/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_call_soon_fifo.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/util/eventloop',
+ pure : false
+)
diff --git a/lib/portage/tests/util/file_copy/meson.build b/lib/portage/tests/util/file_copy/meson.build
new file mode 100644
index 000000000..4c46e549d
--- /dev/null
+++ b/lib/portage/tests/util/file_copy/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_copyfile.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/util/file_copy',
+ pure : false
+)
diff --git a/lib/portage/tests/util/futures/asyncio/meson.build b/lib/portage/tests/util/futures/asyncio/meson.build
new file mode 100644
index 000000000..347088246
--- /dev/null
+++ b/lib/portage/tests/util/futures/asyncio/meson.build
@@ -0,0 +1,15 @@
+py.install_sources(
+ [
+ 'test_child_watcher.py',
+ 'test_event_loop_in_fork.py',
+ 'test_pipe_closed.py',
+ 'test_policy_wrapper_recursion.py',
+ 'test_run_until_complete.py',
+ 'test_subprocess_exec.py',
+ 'test_wakeup_fd_sigchld.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/util/futures/asyncio',
+ pure : false
+)
diff --git a/lib/portage/tests/util/futures/meson.build b/lib/portage/tests/util/futures/meson.build
new file mode 100644
index 000000000..d927acbdd
--- /dev/null
+++ b/lib/portage/tests/util/futures/meson.build
@@ -0,0 +1,15 @@
+py.install_sources(
+ [
+ 'test_compat_coroutine.py',
+ 'test_done_callback.py',
+ 'test_done_callback_after_exit.py',
+ 'test_iter_completed.py',
+ 'test_retry.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/util/futures',
+ pure : false
+)
+
+subdir('asyncio')
diff --git a/lib/portage/tests/util/meson.build b/lib/portage/tests/util/meson.build
new file mode 100644
index 000000000..65ed3ed1e
--- /dev/null
+++ b/lib/portage/tests/util/meson.build
@@ -0,0 +1,31 @@
+py.install_sources(
+ [
+ 'test_checksum.py',
+ 'test_digraph.py',
+ 'test_file_copier.py',
+ 'test_getconfig.py',
+ 'test_grabdict.py',
+ 'test_install_mask.py',
+ 'test_manifest.py',
+ 'test_mtimedb.py',
+ 'test_normalizedPath.py',
+ 'test_shelve.py',
+ 'test_socks5.py',
+ 'test_stackDictList.py',
+ 'test_stackDicts.py',
+ 'test_stackLists.py',
+ 'test_uniqueArray.py',
+ 'test_varExpand.py',
+ 'test_whirlpool.py',
+ 'test_xattr.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/util',
+ pure : false
+)
+
+subdir('dyn_libs')
+subdir('eventloop')
+subdir('file_copy')
+subdir('futures')
diff --git a/lib/portage/tests/versions/meson.build b/lib/portage/tests/versions/meson.build
new file mode 100644
index 000000000..4d1ed6e8d
--- /dev/null
+++ b/lib/portage/tests/versions/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'test_cpv_sort_key.py',
+ 'test_vercmp.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/versions',
+ pure : false
+)
diff --git a/lib/portage/tests/xpak/meson.build b/lib/portage/tests/xpak/meson.build
new file mode 100644
index 000000000..6563693d8
--- /dev/null
+++ b/lib/portage/tests/xpak/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'test_decodeint.py',
+ '__init__.py',
+ '__test__.py',
+ ],
+ subdir : 'portage/tests/xpak',
+ pure : false
+)
diff --git a/lib/portage/util/_async/meson.build b/lib/portage/util/_async/meson.build
new file mode 100644
index 000000000..1847cab26
--- /dev/null
+++ b/lib/portage/util/_async/meson.build
@@ -0,0 +1,20 @@
+py.install_sources(
+ [
+ 'AsyncFunction.py',
+ 'AsyncScheduler.py',
+ 'AsyncTaskFuture.py',
+ 'BuildLogger.py',
+ 'FileCopier.py',
+ 'FileDigester.py',
+ 'ForkProcess.py',
+ 'PipeLogger.py',
+ 'PipeReaderBlockingIO.py',
+ 'PopenProcess.py',
+ 'SchedulerInterface.py',
+ 'TaskScheduler.py',
+ 'run_main_scheduler.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/_async',
+ pure : false
+)
diff --git a/lib/portage/util/_dyn_libs/meson.build b/lib/portage/util/_dyn_libs/meson.build
new file mode 100644
index 000000000..f9261b41c
--- /dev/null
+++ b/lib/portage/util/_dyn_libs/meson.build
@@ -0,0 +1,14 @@
+py.install_sources(
+ [
+ 'LinkageMapELF.py',
+ 'NeededEntry.py',
+ 'PreservedLibsRegistry.py',
+ 'display_preserved_libs.py',
+ 'dyn_libs.py',
+ 'soname_deps.py',
+ 'soname_deps_qa.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/_dyn_libs',
+ pure : false
+)
diff --git a/lib/portage/util/_eventloop/meson.build b/lib/portage/util/_eventloop/meson.build
new file mode 100644
index 000000000..8a7f1d71b
--- /dev/null
+++ b/lib/portage/util/_eventloop/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'asyncio_event_loop.py',
+ 'global_event_loop.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/_eventloop',
+ pure : false
+)
diff --git a/lib/portage/util/elf/meson.build b/lib/portage/util/elf/meson.build
new file mode 100644
index 000000000..cc6aa1e38
--- /dev/null
+++ b/lib/portage/util/elf/meson.build
@@ -0,0 +1,9 @@
+py.install_sources(
+ [
+ 'constants.py',
+ 'header.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/elf',
+ pure : false
+)
diff --git a/lib/portage/util/endian/meson.build b/lib/portage/util/endian/meson.build
new file mode 100644
index 000000000..8bdda8052
--- /dev/null
+++ b/lib/portage/util/endian/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'decode.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/endian',
+ pure : false
+)
diff --git a/lib/portage/util/file_copy/meson.build b/lib/portage/util/file_copy/meson.build
new file mode 100644
index 000000000..fcaeee21f
--- /dev/null
+++ b/lib/portage/util/file_copy/meson.build
@@ -0,0 +1,7 @@
+py.install_sources(
+ [
+ '__init__.py',
+ ],
+ subdir : 'portage/util/file_copy',
+ pure : false
+)
diff --git a/lib/portage/util/futures/_asyncio/meson.build b/lib/portage/util/futures/_asyncio/meson.build
new file mode 100644
index 000000000..5eb23c61f
--- /dev/null
+++ b/lib/portage/util/futures/_asyncio/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'streams.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/futures/_asyncio',
+ pure : false
+)
diff --git a/lib/portage/util/futures/executor/meson.build b/lib/portage/util/futures/executor/meson.build
new file mode 100644
index 000000000..ab166e935
--- /dev/null
+++ b/lib/portage/util/futures/executor/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'fork.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/futures/executor',
+ pure : false
+)
diff --git a/lib/portage/util/futures/meson.build b/lib/portage/util/futures/meson.build
new file mode 100644
index 000000000..3296c25b2
--- /dev/null
+++ b/lib/portage/util/futures/meson.build
@@ -0,0 +1,17 @@
+py.install_sources(
+ [
+ 'compat_coroutine.py',
+ 'extendedfutures.py',
+ 'futures.py',
+ 'iter_completed.py',
+ 'retry.py',
+ 'unix_events.py',
+ '_sync_decorator.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/futures',
+ pure : false
+)
+
+subdir('executor')
+subdir('_asyncio')
diff --git a/lib/portage/util/iterators/meson.build b/lib/portage/util/iterators/meson.build
new file mode 100644
index 000000000..013fe7a58
--- /dev/null
+++ b/lib/portage/util/iterators/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'MultiIterGroupBy.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util/iterators',
+ pure : false
+)
diff --git a/lib/portage/util/meson.build b/lib/portage/util/meson.build
new file mode 100644
index 000000000..c0a4942f1
--- /dev/null
+++ b/lib/portage/util/meson.build
@@ -0,0 +1,49 @@
+py.install_sources(
+ [
+ 'ExtractKernelVersion.py',
+ 'SlotObject.py',
+ 'backoff.py',
+ 'bin_entry_point.py',
+ 'changelog.py',
+ 'compression_probe.py',
+ 'configparser.py',
+ 'cpuinfo.py',
+ 'digraph.py',
+ 'env_update.py',
+ 'formatter.py',
+ 'hooks.py',
+ 'install_mask.py',
+ 'lafilefixer.py',
+ 'listdir.py',
+ 'locale.py',
+ 'movefile.py',
+ 'mtimedb.py',
+ 'netlink.py',
+ 'path.py',
+ 'shelve.py',
+ 'socks5.py',
+ 'whirlpool.py',
+ 'writeable_check.py',
+ '_compare_files.py',
+ '_ctypes.py',
+ '_desktop_entry.py',
+ '_get_vm_info.py',
+ '_info_files.py',
+ '_path.py',
+ '_pty.py',
+ '_urlopen.py',
+ '_xattr.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/util',
+ pure : false
+)
+
+subdir('elf')
+subdir('endian')
+subdir('file_copy')
+subdir('futures')
+subdir('iterators')
+subdir('_async')
+subdir('_dyn_libs')
+subdir('_eventloop')
diff --git a/lib/portage/xml/meson.build b/lib/portage/xml/meson.build
new file mode 100644
index 000000000..40c92385f
--- /dev/null
+++ b/lib/portage/xml/meson.build
@@ -0,0 +1,8 @@
+py.install_sources(
+ [
+ 'metadata.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/xml',
+ pure : false
+)
diff --git a/man/color.map.5 b/man/color.map.5
index 92a1baa91..eccb0c5a9 100644
--- a/man/color.map.5
+++ b/man/color.map.5
@@ -1,4 +1,4 @@
-.TH "COLOR.MAP" "5" "Jul 2013" "Portage VERSION" "Portage"
+.TH "COLOR.MAP" "5" "Jul 2013" "Portage @VERSION@" "Portage"
.SH "NAME"
color.map \- custom color settings for Portage
.SH "SYNOPSIS"
diff --git a/man/dispatch-conf.1 b/man/dispatch-conf.1
index b877b6942..146b86540 100644
--- a/man/dispatch-conf.1
+++ b/man/dispatch-conf.1
@@ -1,4 +1,4 @@
-.TH "DISPATCH-CONF" "1" "Jan 2011" "Portage VERSION" "Portage"
+.TH "DISPATCH-CONF" "1" "Jan 2011" "Portage @VERSION@" "Portage"
.SH "NAME"
dispatch\-conf \- Sanely update configuration files after emerging new packages
.SH "SYNOPSIS"
diff --git a/man/ebuild.1 b/man/ebuild.1
index 09974b755..165a48219 100644
--- a/man/ebuild.1
+++ b/man/ebuild.1
@@ -1,4 +1,4 @@
-.TH "EBUILD" "1" "Mar 2023" "Portage VERSION" "Portage"
+.TH "EBUILD" "1" "Mar 2023" "Portage @VERSION@" "Portage"
.SH "NAME"
ebuild \- a low level interface to the Portage system
.SH "SYNOPSIS"
diff --git a/man/ebuild.5 b/man/ebuild.5
index fe70e40f5..d4187a8a4 100644
--- a/man/ebuild.5
+++ b/man/ebuild.5
@@ -1,4 +1,4 @@
-.TH "EBUILD" "5" "Apr 2023" "Portage VERSION" "Portage"
+.TH "EBUILD" "5" "Apr 2023" "Portage @VERSION@" "Portage"
.SH "NAME"
ebuild \- the internal format, variables, and functions in an ebuild script
diff --git a/man/egencache.1 b/man/egencache.1
index 79c1c3159..977ccca07 100644
--- a/man/egencache.1
+++ b/man/egencache.1
@@ -1,4 +1,4 @@
-.TH "EGENCACHE" "1" "Sep 2020" "Portage VERSION" "Portage"
+.TH "EGENCACHE" "1" "Sep 2020" "Portage @VERSION@" "Portage"
.SH "NAME"
egencache \- generate metadata cache for ebuild repositories
.SH "SYNOPSIS"
diff --git a/man/emaint.1 b/man/emaint.1
index 682487c74..2abba9d47 100644
--- a/man/emaint.1
+++ b/man/emaint.1
@@ -1,4 +1,4 @@
-.TH "EMAINT" "1" "Feb 2021" "Portage VERSION" "Portage"
+.TH "EMAINT" "1" "Feb 2021" "Portage @VERSION@" "Portage"
.SH NAME
emaint \- performs package management related system health checks and maintenance
.SH SYNOPSIS
diff --git a/man/emerge.1 b/man/emerge.1
index 191ea4757..c9f4e4542 100644
--- a/man/emerge.1
+++ b/man/emerge.1
@@ -1,4 +1,4 @@
-.TH "EMERGE" "1" "Mar 2023" "Portage VERSION" "Portage"
+.TH "EMERGE" "1" "Mar 2023" "Portage @VERSION@" "Portage"
.SH "NAME"
emerge \- Command\-line interface to the Portage system
.SH "SYNOPSIS"
diff --git a/man/emirrordist.1 b/man/emirrordist.1
index d66a1849d..37a56a8bb 100644
--- a/man/emirrordist.1
+++ b/man/emirrordist.1
@@ -1,4 +1,4 @@
-.TH "EMIRRORDIST" "1" "Feb 2021" "Portage VERSION" "Portage"
+.TH "EMIRRORDIST" "1" "Feb 2021" "Portage @VERSION@" "Portage"
.SH "NAME"
emirrordist \- a fetch tool for mirroring of package distfiles
.SH SYNOPSIS
diff --git a/man/env-update.1 b/man/env-update.1
index f60945d18..ba9a4c9ab 100644
--- a/man/env-update.1
+++ b/man/env-update.1
@@ -1,4 +1,4 @@
-.TH "ENV-UPDATE" "1" "Aug 2008" "Portage VERSION" "Portage"
+.TH "ENV-UPDATE" "1" "Aug 2008" "Portage @VERSION@" "Portage"
.SH "NAME"
env\-update \- updates environment settings automatically
.SH "SYNOPSIS"
diff --git a/man/etc-update.1 b/man/etc-update.1
index fd6568a03..9b82e1f5c 100644
--- a/man/etc-update.1
+++ b/man/etc-update.1
@@ -1,4 +1,4 @@
-.TH "ETC-UPDATE" "1" "Mar 2012" "Portage VERSION" "Portage"
+.TH "ETC-UPDATE" "1" "Mar 2012" "Portage @VERSION@" "Portage"
.SH "NAME"
etc\-update \- handle configuration file updates
.SH "SYNOPSIS"
diff --git a/man/fixpackages.1 b/man/fixpackages.1
index 4797810cb..8ae6f900b 100644
--- a/man/fixpackages.1
+++ b/man/fixpackages.1
@@ -1,4 +1,4 @@
-.TH "FIXPACKAGES" "1" "Dec 2011" "Portage VERSION" "Portage"
+.TH "FIXPACKAGES" "1" "Dec 2011" "Portage @VERSION@" "Portage"
.SH NAME
fixpackages \- Perform package move updates for all packages
.SH SYNOPSIS
diff --git a/man/glsa-check.1 b/man/glsa-check.1
index 4fe1ad506..acd9bbada 100644
--- a/man/glsa-check.1
+++ b/man/glsa-check.1
@@ -1,4 +1,4 @@
-.TH "GLSA-CHECK" "1" "September 2019" "Portage VERSION" "Portage"
+.TH "GLSA-CHECK" "1" "September 2019" "Portage @VERSION@" "Portage"
.SH "NAME"
\fBglsa\-check\fR \- Tool to locally monitor and manage GLSAs
.SH "SYNOPSIS"
diff --git a/man/make.conf.5 b/man/make.conf.5
index dbab292fb..75206d5e7 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -1,4 +1,4 @@
-.TH "MAKE.CONF" "5" "Mar 2023" "Portage VERSION" "Portage"
+.TH "MAKE.CONF" "5" "Mar 2023" "Portage @VERSION@" "Portage"
.SH "NAME"
make.conf \- custom settings for Portage
.SH "SYNOPSIS"
diff --git a/man/meson.build b/man/meson.build
new file mode 100644
index 000000000..0ae8df70a
--- /dev/null
+++ b/man/meson.build
@@ -0,0 +1,31 @@
+man_pages_out = []
+man_pages_in = [
+ 'color.map.5',
+ 'dispatch-conf.1',
+ 'ebuild.1',
+ 'ebuild.5',
+ 'egencache.1',
+ 'emaint.1',
+ 'emerge.1',
+ 'emirrordist.1',
+ 'env-update.1',
+ 'etc-update.1',
+ 'fixpackages.1',
+ 'glsa-check.1',
+ 'make.conf.5',
+ 'portage.5',
+ 'quickpkg.1',
+ 'xpak.5',
+]
+
+foreach man_page : man_pages_in
+ man_pages_out += configure_file(
+ input : man_page,
+ output : man_page,
+ configuration : conf_data
+ )
+endforeach
+
+install_man(man_pages_out)
+
+subdir('ru')
diff --git a/man/portage.5 b/man/portage.5
index a1358e376..4bae67720 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -1,4 +1,4 @@
-.TH "PORTAGE" "5" "Apr 2023" "Portage VERSION" "Portage"
+.TH "PORTAGE" "5" "Apr 2023" "Portage @VERSION@" "Portage"
.SH NAME
portage \- the heart of Gentoo
.SH "DESCRIPTION"
diff --git a/man/quickpkg.1 b/man/quickpkg.1
index a6aff7c50..5f3797a04 100644
--- a/man/quickpkg.1
+++ b/man/quickpkg.1
@@ -1,4 +1,4 @@
-.TH "QUICKPKG" "1" "Apr 2019" "Portage VERSION" "Portage"
+.TH "QUICKPKG" "1" "Apr 2019" "Portage @VERSION@" "Portage"
.SH NAME
quickpkg \- creates portage packages
.SH SYNOPSIS
diff --git a/man/ru/color.map.5 b/man/ru/color.map.5
index 7849ffdba..14a915ddd 100644
--- a/man/ru/color.map.5
+++ b/man/ru/color.map.5
@@ -1,4 +1,4 @@
-.TH "COLOR.MAP" "5" "Jul 2013" "Portage VERSION" "Portage"
+.TH "COLOR.MAP" "5" "Jul 2013" "Portage @VERSION@" "Portage"
.SH "НАЗВАНИЕ"
color.map \- пользовательские настройки цвета в Portage
.SH "ПАРАМЕТРЫ"
diff --git a/man/ru/dispatch-conf.1 b/man/ru/dispatch-conf.1
index 99ab06932..b587f87d2 100644
--- a/man/ru/dispatch-conf.1
+++ b/man/ru/dispatch-conf.1
@@ -1,4 +1,4 @@
-.TH "DISPATCH-CONF" "1" "Jan 2011" "Portage VERSION" "Portage"
+.TH "DISPATCH-CONF" "1" "Jan 2011" "Portage @VERSION@" "Portage"
.SH "НАЗВАНИЕ"
dispatch\-conf \- безопасное обновление конфигурационных файлов после
установки новых пакетов
diff --git a/man/ru/ebuild.1 b/man/ru/ebuild.1
index b82a90814..39de5b0b9 100644
--- a/man/ru/ebuild.1
+++ b/man/ru/ebuild.1
@@ -1,4 +1,4 @@
-.TH "EBUILD" "1" "Mar 2023" "Portage VERSION" "Portage"
+.TH "EBUILD" "1" "Mar 2023" "Portage @VERSION@" "Portage"
.SH "НАЗВАНИЕ"
ebuild \- низкоуровневый интерфейс системы Portage
.SH "СИНТАКСИС"
diff --git a/man/ru/env-update.1 b/man/ru/env-update.1
index e1e584c9a..316590f0c 100644
--- a/man/ru/env-update.1
+++ b/man/ru/env-update.1
@@ -1,4 +1,4 @@
-.TH "ENV-UPDATE" "1" "Aug 2008" "Portage VERSION" "Portage"
+.TH "ENV-UPDATE" "1" "Aug 2008" "Portage @VERSION@" "Portage"
.SH "НАЗВАНИЕ"
env\-update \- автоматическое обновление настроек окружения
.SH "СИНТАКСИС"
diff --git a/man/ru/etc-update.1 b/man/ru/etc-update.1
index dcc641fbb..ba276a431 100644
--- a/man/ru/etc-update.1
+++ b/man/ru/etc-update.1
@@ -1,4 +1,4 @@
-.TH "ETC-UPDATE" "1" "Mar 2012" "Portage VERSION" "Portage"
+.TH "ETC-UPDATE" "1" "Mar 2012" "Portage @VERSION@" "Portage"
.SH "НАЗВАНИЕ"
etc\-update \- обработка изменений конфигурационных файлов
.SH "СИНТАКСИС"
diff --git a/man/ru/fixpackages.1 b/man/ru/fixpackages.1
index 606e285c5..e4d1fcecd 100644
--- a/man/ru/fixpackages.1
+++ b/man/ru/fixpackages.1
@@ -1,4 +1,4 @@
-.TH "FIXPACKAGES" "1" "Dec 2011" "Portage VERSION" "Portage"
+.TH "FIXPACKAGES" "1" "Dec 2011" "Portage @VERSION@" "Portage"
.SH "НАЗВАНИЕ"
fixpackages \- выполняет переносы пакетов при обновлениях
для всех пакетов
diff --git a/man/ru/meson.build b/man/ru/meson.build
new file mode 100644
index 000000000..3a97db377
--- /dev/null
+++ b/man/ru/meson.build
@@ -0,0 +1,19 @@
+man_pages_out = []
+man_pages_in = [
+ 'color.map.5',
+ 'dispatch-conf.1',
+ 'ebuild.1',
+ 'env-update.1',
+ 'etc-update.1',
+ 'fixpackages.1',
+]
+
+foreach man_page : man_pages_in
+ man_pages_out += configure_file(
+ input : man_page,
+ output : man_page,
+ configuration : conf_data
+ )
+endforeach
+
+install_man(man_pages_out, locale : 'ru')
diff --git a/meson.build b/meson.build
new file mode 100644
index 000000000..db812eeaf
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,123 @@
+project(
+ 'portage',
+ 'c',
+ version : '3.0.49',
+ license : 'GPL-2.0-or-later',
+ meson_version : '>=0.58.0'
+)
+
+py_mod = import('python')
+# TODO: Add "pure : false" here instead of py.install_sources() when requiring Meson >=0.64.0.
+py = py_mod.find_installation()
+
+sed = find_program('sed', required : true)
+
+system_wide = get_option('system-wide')
+eprefix = get_option('eprefix')
+prefixdir = get_option('prefix')
+datadir = get_option('datadir')
+docdir = get_option('docdir')
+portage_base = get_option('portage-base')
+portage_bindir = get_option('portage-bindir')
+portage_datadir = get_option('portage-datadir')
+
+sysconfdir = system_wide ? get_option('sysconfdir') \
+ : datadir / 'etc'
+
+if docdir == ''
+ docdir = system_wide ? datadir / 'doc' / 'portage' \
+ : datadir / 'share' / 'portage' / 'doc'
+endif
+
+if portage_base == ''
+ # This path must be absolute when system-wide.
+ portage_base = system_wide ? prefixdir / 'lib' / 'portage' \
+ : datadir / 'lib' / 'portage'
+endif
+
+if portage_bindir == ''
+ portage_bindir = portage_base / 'bin'
+endif
+
+if portage_datadir == ''
+ portage_datadir = system_wide ? datadir / 'portage' \
+ : datadir / 'share' / 'portage'
+endif
+
+# hprefixify is copied from prefix.eclass.
+dirs = '/(usr|lib(|[onx]?32|n?64)|etc|bin|sbin|var|opt|run)'
+hprefixify = [
+ sed, '-r',
+ '-e', 's,([^[:alnum:]}\\)\\.])' + dirs + ',\\1' + eprefix.replace(',', '\\,') + '/\\2,g',
+ '-e', 's,^' + dirs + ',' + eprefix.replace(',', '\\,') + '/\\1,',
+ '@INPUT@'
+]
+
+# Use Portage's own code to determine the version from git, if possible.
+version = run_command(
+ [py, '-c', 'import portage; print(portage.VERSION)'],
+ env : { 'PYTHONPATH' : meson.current_source_dir() / 'lib' },
+ capture : true,
+ check : false
+)
+
+# Fall back to the Meson project version above.
+if version.returncode() == 0
+ version = version.stdout().strip()
+ if version == 'HEAD'
+ version = ''
+ endif
+else
+ version = ''
+endif
+
+conf_data = configuration_data({
+ 'VERSION' : version == '' ? meson.project_version() : version
+})
+
+if system_wide
+ conf_data.set('INSTALL_TYPE', 'SYSTEM')
+ conf_data.set('PORTAGE_BASE_PATH', portage_base)
+ conf_data.set('PORTAGE_BIN_PATH', portage_bindir)
+ conf_data.set('EPREFIX', eprefix)
+else
+ conf_data.set('INSTALL_TYPE', 'MODULE')
+ conf_data.set('PORTAGE_BASE_PATH', '')
+ conf_data.set('PORTAGE_BIN_PATH', '')
+ conf_data.set('EPREFIX', '')
+endif
+
+subdir('bin')
+subdir('lib')
+
+if get_option('native-extensions')
+ subdir('src')
+endif
+
+test(
+ 'python',
+ py,
+ args : ['-bWd', meson.current_source_dir() / 'lib' / 'portage' / 'tests' / 'runTests.py'],
+ timeout : 0
+)
+
+if get_option('code-only')
+ subdir_done()
+endif
+
+subdir('cnf')
+
+install_data(
+ [
+ 'NEWS',
+ 'RELEASE-NOTES'
+ ],
+ install_dir : docdir
+)
+
+if not system_wide
+ subdir_done()
+endif
+
+subdir('doc')
+subdir('man')
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 000000000..a433a52e9
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,59 @@
+option('system-wide', type : 'boolean', value : true,
+ description : 'Install system-wide rather than isolated inside a Python environment'
+)
+
+option('code-only', type : 'boolean', value : false,
+ description : 'Do not install additional files such as configuration or documentation'
+)
+
+option('eprefix', type : 'string',
+ description : 'Prefix directory for Portage to operate under'
+)
+
+option('portage-base', type : 'string',
+ description : 'Portage installation base directory'
+)
+
+option('portage-bindir', type : 'string',
+ description : 'Internal Portage executables directory'
+)
+
+option('portage-datadir', type : 'string',
+ description : 'Data files directory'
+)
+
+option('docdir', type : 'string',
+ description : 'Documentation directory'
+)
+
+option('doc', type : 'boolean', value : false,
+ description : 'Build and install documentation'
+)
+
+option('doc-formats', type : 'array', choices : ['xhtml', 'xhtml-nochunks'],
+ description : 'Documentation formats to build'
+)
+
+option('apidoc', type : 'boolean', value : false,
+ description : 'Build and install API documentation'
+)
+
+option('native-extensions', type : 'boolean', value : true,
+ description : 'Build and install the native extensions for better performance'
+)
+
+option('gentoo-dev', type : 'boolean', value : false,
+ description : 'Enable features required for Gentoo ebuild development'
+)
+
+option('ipc', type : 'boolean', value : true,
+ description : 'Use inter-process communication between Portage and running ebuilds'
+)
+
+option('rsync-verify', type : 'boolean', value : true,
+ description : 'Enable full-tree cryptographic verification of Gentoo repository rsync checkouts'
+)
+
+option('xattr', type : 'boolean', value : false,
+ description : 'Preserve extended attributes when installing files'
+)
diff --git a/pyproject.toml b/pyproject.toml
index 646e59c96..635fca25c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,33 @@
-[build-system]
-requires = [
- "setuptools",
- "wheel",
+[project]
+name = 'portage'
+dynamic = ['version']
+description = 'Portage is the package management and distribution system for Gentoo'
+readme = 'README.md'
+requires-python = '>=3.9'
+license = {file = "LICENSE"}
+authors = [
+ {name = 'Gentoo Portage Development Team', email = 'dev-portage@gentoo.org'},
]
-build-backend = "setuptools.build_meta"
+
+[build-system]
+build-backend = 'mesonpy'
+requires = ['meson-python']
+
+[tool.meson-python.args]
+setup = ['-Dsystem-wide=false']
+
+[project.scripts]
+archive-conf = 'portage.util.bin_entry_point:bin_entry_point'
+dispatch-conf = 'portage.util.bin_entry_point:bin_entry_point'
+ebuild = 'portage.util.bin_entry_point:bin_entry_point'
+egencache = 'portage.util.bin_entry_point:bin_entry_point'
+emaint = 'portage.util.bin_entry_point:bin_entry_point'
+emerge = 'portage.util.bin_entry_point:bin_entry_point'
+emirrordist = 'portage.util.bin_entry_point:bin_entry_point'
+env-update = 'portage.util.bin_entry_point:bin_entry_point'
+fixpackages = 'portage.util.bin_entry_point:bin_entry_point'
+glsa-check = 'portage.util.bin_entry_point:bin_entry_point'
+gpkg-sign = 'portage.util.bin_entry_point:bin_entry_point'
+portageq = 'portage.util.bin_entry_point:bin_entry_point'
+quickpkg = 'portage.util.bin_entry_point:bin_entry_point'
+regenworld = 'portage.util.bin_entry_point:bin_entry_point'
diff --git a/setup.py b/setup.py
deleted file mode 100755
index 4525264c7..000000000
--- a/setup.py
+++ /dev/null
@@ -1,925 +0,0 @@
-#!/usr/bin/env python
-# Copyright 1998-2023 Gentoo Authors
-# Distributed under the terms of the GNU General Public License v2
-
-try:
- from setuptools.core import setup, Command, Extension
- from setuptools.command.build import build
- from setuptools.command.build_ext import build_ext as _build_ext
- from setuptools.command.build_scripts import build_scripts
- from setuptools.command.clean import clean
- from setuptools.command.install import install
- from setuptools.command.install_data import install_data
- from setuptools.command.install_lib import install_lib
- from setuptools.command.install_scripts import install_scripts
- from setuptools.command.sdist import sdist
- from setuptools.dep_util import newer
- from setuptools.dir_util import mkpath, remove_tree, copy_tree
- from setuptools.util import change_root, subst_vars
-except ImportError:
- from distutils.core import setup, Command, Extension
- from distutils.command.build import build
- from distutils.command.build_ext import build_ext as _build_ext
- from distutils.command.build_scripts import build_scripts
- from distutils.command.clean import clean
- from distutils.command.install import install
- from distutils.command.install_data import install_data
- from distutils.command.install_lib import install_lib
- from distutils.command.install_scripts import install_scripts
- from distutils.command.sdist import sdist
- from distutils.dep_util import newer
- from distutils.dir_util import mkpath, remove_tree, copy_tree
- from distutils.util import change_root, subst_vars
-
-import codecs
-import collections
-import glob
-import itertools
-import os
-import os.path
-import platform
-import re
-import subprocess
-import sys
-
-autodetect_pip = os.path.basename(os.environ.get("_", "")) == "pip" or os.path.basename(
- os.path.dirname(__file__)
-).startswith("pip-")
-venv_prefix = "" if sys.prefix == sys.base_prefix else sys.prefix
-create_entry_points = bool(autodetect_pip or venv_prefix)
-with open(os.path.join(os.path.dirname(__file__), "README.md")) as f:
- long_description = f.read()
-
-# TODO:
-# - smarter rebuilds of docs w/ 'install_docbook' and 'install_apidoc'.
-
-# Dictionary of scripts. The structure is
-# key = location in filesystem to install the scripts
-# value = list of scripts, path relative to top source directory
-x_scripts = {
- "bin": [
- "bin/ebuild",
- "bin/egencache",
- "bin/emerge",
- "bin/emerge-webrsync",
- "bin/emirrordist",
- "bin/glsa-check",
- "bin/portageq",
- "bin/quickpkg",
- "bin/gpkg-sign",
- ],
- "sbin": [
- "bin/archive-conf",
- "bin/dispatch-conf",
- "bin/emaint",
- "bin/env-update",
- "bin/etc-update",
- "bin/fixpackages",
- "bin/regenworld",
- ],
-}
-
-# Dictionary custom modules written in C/C++ here. The structure is
-# key = module name
-# value = list of C/C++ source code, path relative to top source directory
-x_c_helpers = {
- "portage.util.libc": [
- "src/portage_util_libc.c",
- ],
- "portage.util._whirlpool": [
- "src/portage_util__whirlpool.c",
- ],
-}
-
-if platform.system() == "Linux":
- x_c_helpers.update(
- {
- "portage.util.file_copy.reflink_linux": [
- "src/portage_util_file_copy_reflink_linux.c",
- ],
- }
- )
-
-
-class x_build(build):
- """Build command with extra build_man call."""
-
- def run(self):
- build.run(self)
- self.run_command("build_man")
-
-
-class build_man(Command):
- """Perform substitutions in manpages."""
-
- user_options = []
-
- def initialize_options(self):
- self.build_base = None
-
- def finalize_options(self):
- self.set_undefined_options("build", ("build_base", "build_base"))
-
- def run(self):
- for d, files in self.distribution.data_files:
- if not d.startswith("$mandir/"):
- continue
-
- for source in files:
- target = os.path.join(self.build_base, source)
- mkpath(os.path.dirname(target))
-
- if not newer(source, target) and not newer(__file__, target):
- continue
-
- print(f"copying and updating {source} -> {target}")
-
- with codecs.open(source, "r", "utf8") as f:
- data = f.readlines()
- data[0] = data[0].replace("VERSION", self.distribution.get_version())
- with codecs.open(target, "w", "utf8") as f:
- f.writelines(data)
-
-
-class docbook(Command):
- """Build docs using docbook."""
-
- user_options = [
- (
- "doc-formats=",
- None,
- "Documentation formats to build (all xmlto formats for docbook are allowed, comma-separated",
- ),
- ]
-
- def initialize_options(self):
- self.doc_formats = "xhtml,xhtml-nochunks"
-
- def finalize_options(self):
- self.doc_formats = self.doc_formats.replace(",", " ").split()
-
- def run(self):
- if not os.path.isdir("doc/fragment"):
- mkpath("doc/fragment")
-
- with open("doc/fragment/date", "w"):
- pass
- with open("doc/fragment/version", "w") as f:
- f.write(f"<releaseinfo>{self.distribution.get_version()}</releaseinfo>")
-
- for f in self.doc_formats:
- print(f"Building docs in {f} format...")
- subprocess.check_call(
- ["xmlto", "-o", "doc", "-m", "doc/custom.xsl", f, "doc/portage.docbook"]
- )
-
-
-class apidoc(Command):
- """Build API docs using apidoc."""
-
- user_options = []
-
- def initialize_options(self):
- self.build_lib = None
-
- def finalize_options(self):
- self.set_undefined_options("build_py", ("build_lib", "build_lib"))
-
- def run(self):
- self.run_command("build_py")
-
- print("Building API documentation...")
-
- process_env = os.environ.copy()
- pythonpath = self.build_lib
- try:
- pythonpath += ":" + process_env["PYTHONPATH"]
- except KeyError:
- pass
- process_env["PYTHONPATH"] = pythonpath
-
- subprocess.check_call(["make", "-C", "doc/api", "html"], env=process_env)
-
-
-class install_docbook(install_data):
- """install_data for docbook docs"""
-
- user_options = install_data.user_options + [
- ("htmldir=", None, "HTML documentation install directory"),
- ]
-
- def initialize_options(self):
- install_data.initialize_options(self)
- self.htmldir = None
-
- def finalize_options(self):
- self.set_undefined_options("install", ("htmldir", "htmldir"))
- install_data.finalize_options(self)
-
- def run(self):
- if not os.path.exists("doc/portage.html"):
- self.run_command("docbook")
- self.data_files = [
- (self.htmldir, glob.glob("doc/*.html")),
- ]
- install_data.run(self)
-
-
-class install_apidoc(install_data):
- """install_data for apidoc docs"""
-
- user_options = install_data.user_options + [
- ("htmldir=", None, "HTML documentation install directory"),
- ]
-
- def initialize_options(self):
- install_data.initialize_options(self)
- self.htmldir = None
-
- def finalize_options(self):
- self.set_undefined_options("install", ("htmldir", "htmldir"))
- install_data.finalize_options(self)
-
- def run(self):
- if not os.path.exists("doc/api/build/html/index.html"):
- self.run_command("apidoc")
- self.data_files = [
- (
- os.path.join(self.htmldir, "api"),
- glob.glob("doc/api/build/html/*.html")
- + glob.glob("doc/api/build/html/*.js"),
- ),
- (
- os.path.join(self.htmldir, "api/_static"),
- glob.glob("doc/api/build/html/_static/*"),
- ),
- ]
- install_data.run(self)
-
-
-class x_build_scripts_custom(build_scripts):
- def finalize_options(self):
- build_scripts.finalize_options(self)
- if "dir_name" in dir(self):
- self.build_dir = os.path.join(self.build_dir, self.dir_name)
- if self.dir_name in x_scripts:
- self.scripts = x_scripts[self.dir_name]
- else:
- self.scripts = set(self.scripts)
- if not (create_entry_points and self.dir_name == "portage"):
- for other_files in x_scripts.values():
- self.scripts.difference_update(other_files)
-
- def run(self):
- # group scripts by subdirectory
- split_scripts = collections.defaultdict(list)
- for f in self.scripts:
- dir_name = os.path.dirname(f[len("bin/") :])
- split_scripts[dir_name].append(f)
-
- base_dir = self.build_dir
- base_scripts = self.scripts
- for d, files in split_scripts.items():
- self.build_dir = os.path.join(base_dir, d)
- self.scripts = files
- self.copy_scripts()
-
- # restore previous values
- self.build_dir = base_dir
- self.scripts = base_scripts
-
-
-class x_build_scripts_bin(x_build_scripts_custom):
- dir_name = "bin"
-
-
-class x_build_scripts_sbin(x_build_scripts_custom):
- dir_name = "sbin"
-
-
-class x_build_scripts_portagebin(x_build_scripts_custom):
- dir_name = "portage"
-
-
-class x_build_scripts(build_scripts):
- def initialize_option(self):
- build_scripts.initialize_options(self)
-
- def finalize_options(self):
- build_scripts.finalize_options(self)
-
- def run(self):
- self.run_command("build_scripts_bin")
- self.run_command("build_scripts_portagebin")
- self.run_command("build_scripts_sbin")
-
-
-class x_clean(clean):
- """clean extended for doc & post-test cleaning"""
-
- @staticmethod
- def clean_docs():
- def get_doc_outfiles():
- for dirpath, _dirnames, filenames in os.walk("doc"):
- for f in filenames:
- if f.endswith(".docbook") or f == "custom.xsl":
- pass
- else:
- yield os.path.join(dirpath, f)
-
- # do not recurse
- break
-
- for f in get_doc_outfiles():
- print(f"removing {repr(f)}")
- os.remove(f)
-
- if os.path.isdir("doc/fragment"):
- remove_tree("doc/fragment")
-
- if os.path.isdir("doc/api/build"):
- remove_tree("doc/api/build")
-
- def clean_tests(self):
- # do not remove incorrect dirs accidentally
- top_dir = os.path.normpath(os.path.join(self.build_lib, ".."))
- cprefix = os.path.commonprefix((self.build_base, top_dir))
- if cprefix != self.build_base:
- return
-
- bin_dir = os.path.join(top_dir, "bin")
- if os.path.exists(bin_dir):
- remove_tree(bin_dir)
-
- conf_dir = os.path.join(top_dir, "cnf")
- if os.path.islink(conf_dir):
- print(f"removing {repr(conf_dir)} symlink")
- os.unlink(conf_dir)
-
- pni_file = os.path.join(top_dir, ".portage_not_installed")
- if os.path.exists(pni_file):
- print(f"removing {repr(pni_file)}")
- os.unlink(pni_file)
-
- def clean_man(self):
- man_dir = os.path.join(self.build_base, "man")
- if os.path.exists(man_dir):
- remove_tree(man_dir)
-
- def run(self):
- if self.all:
- self.clean_tests()
- self.clean_docs()
- self.clean_man()
-
- clean.run(self)
-
-
-class x_install(install):
- """install command with extra Portage paths"""
-
- user_options = install.user_options + [
- # note: $prefix and $exec_prefix are reserved for Python install
- ("system-prefix=", None, "Prefix for architecture-independent data"),
- ("system-exec-prefix=", None, "Prefix for architecture-specific data"),
- ("bindir=", None, "Install directory for main executables"),
- ("datarootdir=", None, "Data install root directory"),
- ("docdir=", None, "Documentation install directory"),
- ("htmldir=", None, "HTML documentation install directory"),
- ("mandir=", None, "Manpage root install directory"),
- ("portage-base=", "b", "Portage install base"),
- (
- "portage-bindir=",
- None,
- "Install directory for Portage internal-use executables",
- ),
- ("portage-datadir=", None, "Install directory for data files"),
- ("sbindir=", None, "Install directory for superuser-intended executables"),
- ("sysconfdir=", None, "System configuration path"),
- ]
-
- # note: the order is important for proper substitution
- paths = [
- ("system_prefix", "/usr"),
- ("system_exec_prefix", "$system_prefix"),
- ("bindir", "$system_exec_prefix/bin"),
- ("sbindir", "$system_exec_prefix/sbin"),
- ("sysconfdir", "/etc"),
- ("datarootdir", "$system_prefix/share"),
- ("docdir", "$datarootdir/doc/$package-$version"),
- ("htmldir", "$docdir/html"),
- ("mandir", "$datarootdir/man"),
- ("portage_base", "$system_exec_prefix/lib/portage"),
- ("portage_bindir", "$portage_base/bin"),
- ("portage_datadir", "$datarootdir/portage"),
- # not customized at the moment
- ("logrotatedir", "$sysconfdir/logrotate.d"),
- ("portage_confdir", "$portage_datadir/config"),
- ("portage_setsdir", "$portage_confdir/sets"),
- ]
-
- def initialize_options(self):
- install.initialize_options(self)
-
- for key, default in self.paths:
- setattr(self, key, default)
- self.subst_paths = {}
-
- def finalize_options(self):
- install.finalize_options(self)
-
- # substitute variables
- new_paths = {
- "package": self.distribution.get_name(),
- "version": self.distribution.get_version(),
- }
- for key, _default in self.paths:
- new_paths[key] = subst_vars(getattr(self, key), new_paths)
- setattr(self, key, new_paths[key])
- self.subst_paths = new_paths
-
-
-class x_install_data(install_data):
- """install_data with customized path support"""
-
- user_options = install_data.user_options
-
- def initialize_options(self):
- install_data.initialize_options(self)
- self.build_base = None
- self.paths = None
-
- def finalize_options(self):
- install_data.finalize_options(self)
- self.set_undefined_options("build", ("build_base", "build_base"))
- self.set_undefined_options("install", ("subst_paths", "paths"))
-
- def run(self):
- def re_sub_file(path, pattern, repl):
- print(f"Rewriting {path}")
- with codecs.open(path, "r", "utf-8") as f:
- data = f.read()
- data = re.sub(pattern, repl, data, flags=re.MULTILINE)
- with codecs.open(path, "w", "utf-8") as f:
- f.write(data)
-
- if create_entry_points:
- re_sub_file("cnf/repos.conf", r"= /", "= %(EPREFIX)s/")
- re_sub_file("cnf/make.globals", r'DIR="/', 'DIR="${EPREFIX}/')
-
- self.run_command("build_man")
-
- def process_data_files(df):
- for d, files in df:
- # substitute man sources
- if d.startswith("$mandir/"):
- files = [os.path.join(self.build_base, v) for v in files]
-
- # substitute variables in path
- d = subst_vars(d, self.paths)
- yield (d, files)
-
- old_data_files = self.data_files
- self.data_files = process_data_files(self.data_files)
-
- install_data.run(self)
- self.data_files = old_data_files
-
-
-class x_install_lib(install_lib):
- """install_lib command with Portage path substitution"""
-
- user_options = install_lib.user_options
-
- def initialize_options(self):
- install_lib.initialize_options(self)
- self.portage_base = None
- self.portage_bindir = None
- self.portage_confdir = None
-
- def finalize_options(self):
- install_lib.finalize_options(self)
- self.set_undefined_options(
- "install",
- ("portage_base", "portage_base"),
- ("portage_bindir", "portage_bindir"),
- ("portage_confdir", "portage_confdir"),
- )
-
- def install(self):
- ret = install_lib.install(self)
-
- def rewrite_file(path, val_dict):
- path = os.path.join(self.install_dir, path)
- print(f"Rewriting {path}")
- with codecs.open(path, "r", "utf-8") as f:
- data = f.read()
-
- for varname, val in val_dict.items():
- regexp = r"(?m)^(%s\s*=).*$" % varname
- repl = r"\1 %s" % repr(val)
-
- data = re.sub(regexp, repl, data)
-
- with codecs.open(path, "w", "utf-8") as f:
- f.write(data)
-
- rewrite_file(
- "portage/__init__.py",
- {
- "VERSION": self.distribution.get_version(),
- },
- )
-
- def re_sub_file(path, pattern_repl_items):
- path = os.path.join(self.install_dir, path)
- print(f"Rewriting {path}")
- with codecs.open(path, "r", "utf-8") as f:
- data = f.read()
- for pattern, repl in pattern_repl_items:
- data = re.sub(pattern, repl, data, flags=re.MULTILINE)
- with codecs.open(path, "w", "utf-8") as f:
- f.write(data)
-
- val_dict = {}
- if create_entry_points:
- re_sub_file(
- "portage/const.py",
- (
- (
- r"^(GLOBAL_CONFIG_PATH\s*=\s*[\"'])(.*)([\"'])",
- lambda m: "{}{}{}".format(
- m.group(1),
- m.group(2).partition("/usr")[-1],
- m.group(3),
- ),
- ),
- (
- r"^(PORTAGE_BASE_PATH\s*=\s*)(.*)",
- lambda m: "{}{}".format(
- m.group(1),
- 'os.path.join(os.path.realpath(__import__("sys").prefix), "lib/portage")',
- ),
- ),
- (
- r"^(EPREFIX\s*=\s*)(.*)",
- lambda m: f'{m.group(1)}__import__("sys").prefix',
- ),
- ),
- )
- else:
- val_dict.update(
- {
- "PORTAGE_BASE_PATH": self.portage_base,
- "PORTAGE_BIN_PATH": self.portage_bindir,
- }
- )
- rewrite_file("portage/const.py", val_dict)
-
- return ret
-
-
-class x_install_scripts_custom(install_scripts):
- def initialize_options(self):
- install_scripts.initialize_options(self)
- self.root = None
-
- def finalize_options(self):
- self.set_undefined_options(
- "install", ("root", "root"), (self.var_name, "install_dir")
- )
- install_scripts.finalize_options(self)
- self.build_dir = os.path.join(self.build_dir, self.dir_name)
-
- # prepend root
- if self.root is not None:
- self.install_dir = change_root(self.root, self.install_dir)
-
- def run(self):
- if not create_entry_points:
- install_scripts.run(self)
-
-
-class x_install_scripts_bin(x_install_scripts_custom):
- dir_name = "bin"
- var_name = "bindir"
-
-
-class x_install_scripts_sbin(x_install_scripts_custom):
- dir_name = "sbin"
- var_name = "sbindir"
-
-
-class x_install_scripts_portagebin(x_install_scripts_custom):
- dir_name = "portage"
- var_name = "portage_bindir"
-
-
-class x_install_scripts(install_scripts):
- def initialize_option(self):
- pass
-
- def finalize_options(self):
- pass
-
- def run(self):
- self.run_command("install_scripts_bin")
- self.run_command("install_scripts_portagebin")
- self.run_command("install_scripts_sbin")
-
-
-class x_sdist(sdist):
- """sdist defaulting to .tar.bz2 format, and archive files owned by root"""
-
- def finalize_options(self):
- if self.owner is None:
- self.owner = "root"
- if self.group is None:
- self.group = "root"
-
- sdist.finalize_options(self)
-
-
-class build_tests(x_build_scripts_custom):
- """Prepare build dir for running tests."""
-
- def initialize_options(self):
- x_build_scripts_custom.initialize_options(self)
- self.build_base = None
- self.build_lib = None
-
- def finalize_options(self):
- x_build_scripts_custom.finalize_options(self)
- self.set_undefined_options(
- "build", ("build_base", "build_base"), ("build_lib", "build_lib")
- )
-
- # since we will be writing to $build_lib/.., it is important
- # that we do not leave $build_base
- self.top_dir = os.path.normpath(os.path.join(self.build_lib, ".."))
- cprefix = os.path.commonprefix((self.build_base, self.top_dir))
- if cprefix != self.build_base:
- raise SystemError("build_lib must be a subdirectory of build_base")
-
- self.build_dir = os.path.join(self.top_dir, "bin")
-
- def run(self):
- self.run_command("build_py")
-
- # install all scripts $build_lib/../bin
- # (we can't do a symlink since we want shebangs corrected)
- x_build_scripts_custom.run(self)
-
- # symlink 'cnf' directory
- conf_dir = os.path.join(self.top_dir, "cnf")
- if os.path.exists(conf_dir):
- if not os.path.islink(conf_dir):
- raise SystemError(
- f"{repr(conf_dir)} exists and is not a symlink (collision)"
- )
- os.unlink(conf_dir)
- conf_src = os.path.relpath("cnf", self.top_dir)
- print(f"Symlinking {conf_dir} -> {conf_src}")
- os.symlink(conf_src, conf_dir)
-
- source_path = os.path.realpath(__file__)
-
- # copy GPG test keys
- copy_tree(
- os.path.join(
- os.path.dirname(source_path), "lib", "portage", "tests", ".gnupg"
- ),
- os.path.join(self.build_lib, "portage", "tests", ".gnupg"),
- )
- os.chmod(os.path.join(self.build_lib, "portage", "tests", ".gnupg"), 0o700)
-
- # create $build_lib/../.portage_not_installed
- # to enable proper paths in tests
- with open(os.path.join(self.top_dir, ".portage_not_installed"), "w"):
- pass
-
-
-class test(Command):
- """run tests"""
-
- user_options = []
-
- def initialize_options(self):
- self.build_lib = None
-
- def finalize_options(self):
- self.set_undefined_options("build", ("build_lib", "build_lib"))
-
- def run(self):
- self.run_command("build_tests")
-
- subprocess.check_call(
- [
- sys.executable,
- "-bWd",
- os.path.join(self.build_lib, "portage/tests/runTests.py"),
- ]
- )
-
-
-def find_packages():
- for dirpath, _dirnames, filenames in os.walk("lib"):
- if "__init__.py" in filenames:
- yield os.path.relpath(dirpath, "lib")
-
-
-def find_scripts():
- for dirpath, _dirnames, filenames in os.walk("bin"):
- for f in filenames:
- yield os.path.join(dirpath, f)
-
-
-def get_manpages():
- linguas = os.environ.get("LINGUAS")
- if linguas is not None:
- linguas = linguas.split()
-
- for dirpath, _dirnames, filenames in os.walk("man"):
- groups = collections.defaultdict(list)
- for f in filenames:
- _fn, suffix = f.rsplit(".", 1)
- groups[suffix].append(os.path.join(dirpath, f))
-
- topdir = dirpath[len("man/") :]
- if not topdir or linguas is None or topdir in linguas:
- for g, mans in groups.items():
- yield [os.path.join("$mandir", topdir, f"man{g}"), mans]
-
-
-class build_ext(_build_ext):
- user_options = _build_ext.user_options + [
- (
- "portage-ext-modules",
- None,
- "enable portage's C/C++ extensions (cross-compiling is not supported)",
- ),
- ]
-
- boolean_options = _build_ext.boolean_options + [
- "portage_ext_modules",
- ]
-
- def initialize_options(self):
- _build_ext.initialize_options(self)
- self.portage_ext_modules = None
-
- def run(self):
- if self.portage_ext_modules:
- _build_ext.run(self)
-
-
-def venv_data_files(locations):
- if not create_entry_points:
- return
- for dest_prefix, source_path, file_args in locations:
- specific_files = []
- mode_arg = None
- for arg in file_args:
- if arg.startswith("-m"):
- mode_arg = int(arg[2:], 8)
- else:
- specific_files.append(arg)
-
- abs_source_path = os.path.abspath(source_path)
- for root, dirs, files in os.walk(abs_source_path):
- root_offset = root[len(abs_source_path) :].lstrip("/")
- dest_path = os.path.join(dest_prefix, root_offset)
-
- if specific_files:
- matched_files = list(
- itertools.chain.from_iterable(
- glob.glob(os.path.join(root, x)) for x in specific_files
- )
- )
- else:
- matched_files = [os.path.join(root, x) for x in files]
-
- if mode_arg:
- for filename in matched_files:
- if not os.path.islink(filename):
- os.chmod(filename, mode_arg)
-
- yield (dest_path, matched_files)
-
-
-def get_data_files(regular_files, venv_files):
- if create_entry_points:
- return list(venv_data_files(venv_files))
-
- return regular_files
-
-
-setup(
- name="portage",
- version="3.0.49",
- url="https://wiki.gentoo.org/wiki/Project:Portage",
- project_urls={
- "Release Notes": "https://gitweb.gentoo.org/proj/portage.git/plain/NEWS",
- "Documentation": "https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/Portage",
- },
- author="Gentoo Portage Development Team",
- author_email="dev-portage@gentoo.org",
- description="Portage is the package management and distribution system for Gentoo",
- license="GPLV2",
- long_description=long_description,
- long_description_content_type="text/markdown",
- package_dir={"": "lib"},
- packages=list(find_packages()),
- # something to cheat build & install commands
- scripts=list(find_scripts()),
- data_files=get_data_files(
- list(get_manpages())
- + [
- ["$sysconfdir", ["cnf/etc-update.conf", "cnf/dispatch-conf.conf"]],
- ["$logrotatedir", ["cnf/logrotate.d/elog-save-summary"]],
- [
- "$portage_confdir",
- ["cnf/make.conf.example", "cnf/make.globals", "cnf/repos.conf"],
- ],
- ["$portage_setsdir", ["cnf/sets/portage.conf"]],
- ["$docdir", ["NEWS", "RELEASE-NOTES"]],
- ["$portage_confdir/repo.postsync.d", ["cnf/repo.postsync.d/example"]],
- ],
- [
- ("etc", "cnf", ("etc-update.conf", "dispatch-conf.conf")),
- ("etc/logrotate.d", "cnf/logrotate.d", ("elog-save-summary",)),
- (
- "share/portage/config/repo.postsync.d",
- "cnf/repo.postsync.d",
- ("example",),
- ),
- (
- "share/portage/config",
- "cnf",
- ("make.conf.example", "make.globals", "repos.conf"),
- ),
- ("share/portage/config/sets", "cnf/sets", ("*.conf",)),
- ("share/man/man1", "man", ("*.1",)),
- ("share/man/man5", "man", ("*.5",)),
- ("share/portage/doc", "", ("NEWS", "RELEASE-NOTES")),
- ("lib/portage/bin", "bin", ("-m0755",)),
- ],
- ),
- entry_points={
- "console_scripts": [
- f"{os.path.basename(path)}=portage.util.bin_entry_point:bin_entry_point"
- for path in itertools.chain.from_iterable(x_scripts.values())
- ],
- }
- if create_entry_points
- else {},
- # create_entry_points disables ext_modules, for pure python
- ext_modules=[]
- if create_entry_points
- else [
- Extension(
- name=n,
- sources=m,
- extra_compile_args=[
- "-D_FILE_OFFSET_BITS=64",
- "-D_LARGEFILE_SOURCE",
- "-D_LARGEFILE64_SOURCE",
- ],
- )
- for n, m in x_c_helpers.items()
- ],
- cmdclass={
- "build": x_build,
- "build_ext": build_ext,
- "build_man": build_man,
- "build_scripts": x_build_scripts,
- "build_scripts_bin": x_build_scripts_bin,
- "build_scripts_portagebin": x_build_scripts_portagebin,
- "build_scripts_sbin": x_build_scripts_sbin,
- "build_tests": build_tests,
- "clean": x_clean,
- "docbook": docbook,
- "apidoc": apidoc,
- "install": x_install,
- "install_data": x_install_data,
- "install_docbook": install_docbook,
- "install_apidoc": install_apidoc,
- "install_lib": x_install_lib,
- "install_scripts": x_install_scripts,
- "install_scripts_bin": x_install_scripts_bin,
- "install_scripts_portagebin": x_install_scripts_portagebin,
- "install_scripts_sbin": x_install_scripts_sbin,
- "sdist": x_sdist,
- "test": test,
- },
- classifiers=[
- "Development Status :: 5 - Production/Stable",
- "Environment :: Console",
- "Intended Audience :: System Administrators",
- "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
- "Operating System :: POSIX",
- "Programming Language :: Python :: 3",
- "Topic :: System :: Installation/Setup",
- ],
- python_requires=">=3.9",
-)
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 000000000..cbc7aa611
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,50 @@
+# We create symlinks to the native extensions in the source tree for the tests
+# and for development. Meson does not allow you to build in-place and Python
+# cannot create a single namespace from two identically-named paths.
+
+libc_ext = py.extension_module(
+ 'libc',
+ 'portage_util_libc.c',
+ dependencies : py.dependency(),
+ subdir : 'portage' / 'util',
+ install : true
+)
+
+whirlpool_ext = py.extension_module(
+ '_whirlpool',
+ 'portage_util__whirlpool.c',
+ dependencies : py.dependency(),
+ subdir : 'portage' / 'util',
+ install : true
+)
+
+run_command(
+ [
+ 'ln', '-srnf',
+ libc_ext.full_path(),
+ whirlpool_ext.full_path(),
+ meson.project_source_root() / 'lib' / 'portage' / 'util/'
+ ],
+ capture : false,
+ check : true
+)
+
+if host_machine.system() == 'linux'
+ reflink_ext = py.extension_module(
+ 'reflink_linux',
+ 'portage_util_file_copy_reflink_linux.c',
+ dependencies : py.dependency(),
+ subdir : 'portage' / 'util' / 'file_copy',
+ install : true
+ )
+
+ run_command(
+ [
+ 'ln', '-srnf',
+ reflink_ext.full_path(),
+ meson.project_source_root() / 'lib' / 'portage' / 'util' / 'file_copy/'
+ ],
+ capture : false,
+ check : true
+ )
+endif
diff --git a/tox.ini b/tox.ini
index 4ea388e35..6a52a8a0a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -28,4 +28,4 @@ allowlist_externals =
./run-pylint
commands =
pylint: ./run-pylint
- test: python -b -Wd setup.py test
+ test: python -bWd lib/portage/tests/runTests.py