├── .clang-format ├── .clang-format-ignore ├── .clang-tidy ├── .clang-tidy-ignore ├── .devcontainer └── devcontainer.json ├── .github ├── Dockerfile ├── actions │ └── install-boost │ │ └── action.yml ├── gentoo.conf ├── localrepo.conf ├── localrepo │ ├── dev-libs │ │ └── pms-utils │ │ │ └── pms-utils-9999.ebuild │ ├── metadata │ │ └── layout.conf │ └── profiles │ │ └── repo_name └── workflows │ ├── alpine.yml │ ├── clang-tidy.yml │ ├── codechecker.yml │ ├── codeql.yml │ ├── docs.yml │ ├── emscripten.yml │ ├── format.yml │ ├── gentoo-container.yml │ ├── gentoo.yml │ ├── macos.yml │ ├── pypi.yml │ └── sonarlint.yml ├── .sonarlint └── connectedMode.json ├── LICENSE ├── README.md ├── docs └── meson.build ├── include ├── meson.build └── pms-utils │ ├── atom │ ├── atom.hpp │ └── atom_parser.hpp │ ├── depend │ ├── depend.hpp │ └── depend_parser.hpp │ ├── ebuild │ ├── ebuild.hpp │ └── ebuild_parser.hpp │ ├── misc │ ├── macro-begin.hpp │ ├── macro-end.hpp │ ├── meta.hpp │ ├── try_parse.hpp │ └── x3_utils.hpp │ ├── profile │ ├── profile.hpp │ └── profile_parser.hpp │ └── repo │ ├── repo.hpp │ └── repo_parser.hpp ├── lib ├── atom │ ├── atom.cpp │ └── meson.build ├── depend │ ├── depend.cpp │ └── meson.build ├── ebuild │ ├── ebuild.cpp │ └── meson.build ├── meson.build ├── profile │ ├── meson.build │ ├── profile.cpp │ ├── profile_wildcard.cpp │ └── profile_wildcard.hpp ├── repo │ ├── meson.build │ └── repo.cpp └── util │ └── readfile.hpp ├── meson.build ├── meson.format ├── meson.options ├── pyproject.toml ├── sonar-project.properties ├── subprojects ├── .gitignore ├── bindings-python │ ├── docs │ │ ├── meson.build │ │ └── source │ │ │ ├── .gitignore │ │ │ ├── _static │ │ │ └── custom.css │ │ │ ├── atom.rst │ │ │ ├── atom │ │ │ ├── Atom.rst │ │ │ ├── Blocker.rst │ │ │ ├── Category.rst │ │ │ ├── Name.rst │ │ │ ├── Slot.rst │ │ │ ├── SlotExpr.rst │ │ │ ├── SlotVariant.rst │ │ │ ├── Usedep.rst │ │ │ ├── UsedepCond.rst │ │ │ ├── UsedepNegate.rst │ │ │ ├── UsedepSign.rst │ │ │ ├── Usedeps.rst │ │ │ ├── Useflag.rst │ │ │ ├── Version.rst │ │ │ ├── VersionNumber.rst │ │ │ ├── VersionRevision.rst │ │ │ ├── VersionSpecifier.rst │ │ │ ├── VersionSuffix.rst │ │ │ └── VersionSuffixWord.rst │ │ │ ├── autoclass.py │ │ │ ├── autoproperty.py │ │ │ ├── conf.py │ │ │ ├── depend.rst │ │ │ ├── depend │ │ │ ├── DependExpr.rst │ │ │ ├── GroupHeaderOp.rst │ │ │ └── UseConditional.rst │ │ │ ├── ebuild.rst │ │ │ ├── ebuild │ │ │ ├── Metadata.rst │ │ │ ├── URI.rst │ │ │ ├── defined_phases.rst │ │ │ ├── eapi.rst │ │ │ ├── homepage.rst │ │ │ ├── inherited.rst │ │ │ ├── inherited_elem.rst │ │ │ ├── iuse.rst │ │ │ ├── iuse_elem.rst │ │ │ ├── keyword.rst │ │ │ ├── keywords.rst │ │ │ ├── license.rst │ │ │ ├── license_elem.rst │ │ │ ├── phases.rst │ │ │ ├── properties.rst │ │ │ ├── properties_elem.rst │ │ │ ├── properties_elem_type.rst │ │ │ ├── required_use.rst │ │ │ ├── restrict.rst │ │ │ ├── restrict_elem.rst │ │ │ ├── restrict_elem_type.rst │ │ │ ├── src_uri.rst │ │ │ └── uri_elem.rst │ │ │ ├── examples │ │ │ ├── atom.rst │ │ │ ├── depend.rst │ │ │ ├── equery.rst │ │ │ └── repo.rst │ │ │ ├── index.rst │ │ │ ├── profile.rst │ │ │ ├── profile │ │ │ ├── Filters.rst │ │ │ ├── PortageProfile.rst │ │ │ ├── Profile.rst │ │ │ └── expand_package_expr.rst │ │ │ ├── repo.rst │ │ │ └── repo │ │ │ ├── Category.rst │ │ │ ├── Ebuild.rst │ │ │ ├── Package.rst │ │ │ ├── Repository.rst │ │ │ └── parse_metadata.rst │ ├── lib │ │ ├── atom │ │ │ ├── atom.cpp │ │ │ ├── atom.hpp │ │ │ └── meson.build │ │ ├── common.hpp │ │ ├── depend │ │ │ ├── depend.cpp │ │ │ ├── depend.hpp │ │ │ └── meson.build │ │ ├── ebuild │ │ │ ├── ebuild.cpp │ │ │ ├── ebuild.hpp │ │ │ └── meson.build │ │ ├── internal.hpp │ │ ├── meson.build │ │ ├── pms_utils.cpp │ │ ├── profile │ │ │ ├── meson.build │ │ │ ├── profile.cpp │ │ │ └── profile.hpp │ │ ├── py.typed │ │ └── repo │ │ │ ├── meson.build │ │ │ ├── repo.cpp │ │ │ └── repo.hpp │ ├── meson.build │ ├── meson.format │ ├── meson.options │ ├── stubs │ │ └── meson.build │ ├── subprojects │ │ ├── .gitignore │ │ ├── nanobind.wrap │ │ ├── packagefiles │ │ │ ├── nanobind-meson-stubgen.patch │ │ │ └── nanobind-meson.build.patch │ │ └── robin-map.wrap │ ├── test │ │ ├── import.py │ │ ├── issue_26.py │ │ ├── issue_36.py │ │ └── meson.build │ └── version ├── nanobind.wrap └── robin-map.wrap ├── test ├── atom │ ├── atom.cpp │ ├── blocker.cpp │ ├── meson.build │ ├── name_like_ver.cpp │ ├── slot.cpp │ ├── slot_no_subslot.cpp │ ├── usedeps.cpp │ ├── useflag.cpp │ └── version_specifier.cpp ├── depend │ ├── ast_iterator.cpp │ ├── depend.cpp │ └── meson.build ├── ebuild │ ├── iuse.cpp │ ├── meson.build │ ├── src_uri.cpp │ └── uri.cpp ├── meson.build ├── misc │ ├── meson.build │ ├── try_parse.cpp │ └── try_readfile.cpp ├── profile │ ├── expand_wildcard.cpp │ ├── make_defaults.cpp │ ├── meson.build │ ├── package_use.cpp │ ├── parse_profiles.cpp │ ├── shlex_single.cpp │ └── wildcard_atom.cpp ├── repo │ ├── iter.cpp │ ├── meson.build │ └── metadata.cpp └── utils │ ├── misc.hpp │ ├── threadpool.hpp │ └── x3_util.hpp ├── version └── wasm.ini /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IncludeBlocks: Regroup 3 | IndentWidth: 4 4 | TabWidth: 4 5 | AccessModifierOffset: -4 6 | 7 | ColumnLimit: 110 8 | InsertNewlineAtEOF: True 9 | QualifierAlignment: Custom 10 | QualifierOrder: ["friend", "constexpr", "static", "inline", "const", "type"] 11 | -------------------------------------------------------------------------------- /.clang-format-ignore: -------------------------------------------------------------------------------- 1 | subprojects/nanobind* 2 | subprojects/robin-map* 3 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: "-*, bugprone-*, -bugprone-unchecked-optional-access, cppcoreguidelines-*, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-non-private-member-variables-in-classes, misc-*, -misc-non-private-member-variables-in-classes, -misc-no-recursion, modernize-*, -modernize-use-trailing-return-type, performance-*, portability-*, readability-*, -readability-magic-numbers, -readability-qualified-auto, clang-*, -clang-analyzer-optin.core.EnumCastOutOfRange, -clang*" 2 | # TODO: clang* crashes on some files as of 19.1.7 3 | WarningsAsErrors: "*" 4 | 5 | FormatStyle: "file" 6 | ExtraArgsBefore: ["-DPMS_UTILS_CLANG_TIDY", "-fno-caret-diagnostics"] 7 | CheckOptions: 8 | misc-include-cleaner.IgnoreHeaders: "boost/.*/detail/.*" 9 | -------------------------------------------------------------------------------- /.clang-tidy-ignore: -------------------------------------------------------------------------------- 1 | subprojects/nanobind* 2 | subprojects/robin-map* 3 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "ghcr.io/jannik2099/pms-utils-gentoo-ci:latest", 3 | "capAdd": ["SYS_PTRACE"], 4 | "securityOpt": ["seccomp=unconfined"], 5 | "customizations": { 6 | "vscode": { 7 | "extensions": [ 8 | "llvm-vs-code-extensions.vscode-clangd", 9 | "mesonbuild.mesonbuild", 10 | "sonarsource.sonarlint-vscode", 11 | "github.vscode-codeql" 12 | ] 13 | } 14 | }, 15 | "remoteUser": "root", 16 | "onCreateCommand": "emerge bash-completion nodejs" 17 | } 18 | -------------------------------------------------------------------------------- /.github/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/gentoo/stage3:latest 2 | # all one single step because Docker is a medieval piece of crap that still can't squash (at least the action can't) 3 | RUN <> make.conf 7 | mkdir -p profile/use.stable.mask 8 | echo "-python_targets_pypy3" >> profile/use.stable.mask/pms-utils 9 | echo "-python_single_target_pypy3" >> profile/use.stable.mask/pms-utils 10 | mkdir -p package.mask 11 | # to prefer -bin 12 | echo "dev-python/pypy3_*-exe" >> package.mask/pypy3 13 | 14 | mkdir -p package.env 15 | mkdir -p env 16 | 17 | mkdir -p package.accept_keywords 18 | echo " package.accept_keywords/boost 19 | 20 | emerge-webrsync -q 21 | getuto 22 | emerge --autounmask --autounmask-write --autounmask-continue -q -j8 dev-vcs/git dev-util/pkgdev llvm-core/clang llvm-core/lld dev-libs/boost dev-build/meson dev-python/pip dev-util/cppcheck dev-util/gcovr 23 | env-update 24 | . /etc/profile 25 | rm -rf /var/cache/distfiles 26 | rm -rf /var/cache/binpkgs 27 | EOF 28 | -------------------------------------------------------------------------------- /.github/actions/install-boost/action.yml: -------------------------------------------------------------------------------- 1 | name: Install Boost 2 | description: Installs Boost 3 | 4 | inputs: 5 | version: 6 | description: Boost version 7 | required: true 8 | default: "1.88.0" 9 | 10 | components: 11 | description: Boost components 12 | required: true 13 | default: describe;mp11;parser;regex 14 | 15 | cmake_wrapper: 16 | description: CMake wrapper 17 | 18 | CMAKE_BUILD_TYPE: 19 | description: CMake build type 20 | default: Release 21 | 22 | CXXFLAGS: 23 | description: C++ compiler flags 24 | default: "-flto -O3" 25 | 26 | LDFLAGS: 27 | description: linker flags 28 | default: "-flto -O3" 29 | 30 | runs: 31 | using: composite 32 | steps: 33 | - name: Download 34 | shell: bash 35 | run: | 36 | cd 37 | curl -s -L https://github.com/boostorg/boost/releases/download/boost-${BOOST_VERSION}/boost-${BOOST_VERSION}-cmake.tar.xz | tar --xz --extract 38 | env: 39 | BOOST_VERSION: ${{ inputs.version }} 40 | 41 | - name: Configure 42 | shell: bash 43 | run: | 44 | cd 45 | cd boost-${BOOST_VERSION} 46 | ${CMAKE_WRAPPER} cmake -GNinja -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBOOST_INCLUDE_LIBRARIES="describe;json;lockfree;mp11;parser;program_options;regex" -B build 47 | env: 48 | BOOST_VERSION: ${{ inputs.version }} 49 | COMPONENTS: ${{ inputs.components }} 50 | CMAKE_WRAPPER: ${{ inputs.cmake_wrapper }} 51 | CMAKE_BUILD_TYPE: ${{ inputs.CMAKE_BUILD_TYPE }} 52 | CXXFLAGS: ${{ inputs.CXXFLAGS }} 53 | LDFLAGS: ${{ inputs.LDFLAGS }} 54 | 55 | - name: Install 56 | shell: bash 57 | run: | 58 | cd 59 | cd boost-${BOOST_VERSION} 60 | cmake --build build --target install 61 | env: 62 | BOOST_VERSION: ${{ inputs.version }} 63 | 64 | - name: Update BOOST_ROOT 65 | shell: bash 66 | run: | 67 | cd 68 | cd boost-${BOOST_VERSION} 69 | CMAKE_INSTALL_PREFIX=$(grep '^CMAKE_INSTALL_PREFIX' build/CMakeCache.txt | cut -d '=' -f 2) 70 | echo "BOOST_ROOT=${CMAKE_INSTALL_PREFIX}" | tee -a "${GITHUB_ENV}" 71 | env: 72 | BOOST_VERSION: ${{ inputs.version }} 73 | -------------------------------------------------------------------------------- /.github/gentoo.conf: -------------------------------------------------------------------------------- 1 | [gentoo] 2 | location = /var/db/repos/gentoo 3 | -------------------------------------------------------------------------------- /.github/localrepo.conf: -------------------------------------------------------------------------------- 1 | [localrepo] 2 | location = /var/db/repos/localrepo 3 | -------------------------------------------------------------------------------- /.github/localrepo/dev-libs/pms-utils/pms-utils-9999.ebuild: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Gentoo Authors 2 | # Distributed under the terms of the GNU General Public License v2 3 | 4 | EAPI=8 5 | 6 | PYTHON_COMPAT=( python3_{12..13} pypy3 ) 7 | 8 | inherit meson python-r1 9 | 10 | if [[ "${PV}" == "9999" ]]; then 11 | inherit git-r3 12 | EGIT_REPO_URI="https://github.com/Jannik2099/pms-utils.git" 13 | else 14 | SRC_URI="https://github.com/Jannik2099/pms-utils/archive/refs/tags/${PV}.tar.gz -> ${P}.tar.gz" 15 | KEYWORDS="~amd64" 16 | fi 17 | 18 | DESCRIPTION="A helper library to implement the Gentoo Package Manager Specification" 19 | HOMEPAGE="https://github.com/Jannik2099/pms-utils" 20 | 21 | LICENSE="GPL-2+" 22 | SLOT="0" 23 | 24 | IUSE="+python" 25 | REQUIRED_USE="python? ( ${PYTHON_REQUIRED_USE} )" 26 | 27 | DEPEND=" 28 | python? ( 29 | ${PYTHON_DEPS} 30 | $(python_gen_cond_dep 'dev-python/typing-extensions[${PYTHON_USEDEP}]' pypy3) 31 | ) 32 | >=dev-libs/boost-1.87 33 | " 34 | RDEPEND="${DEPEND}" 35 | BDEPEND="" 36 | 37 | bindings_python_configure() { 38 | unset emesonargs 39 | local TEST="false" 40 | if has test ${FEATURES}; then 41 | TEST="true" 42 | fi 43 | local emesonargs=( 44 | "-Dtest=${TEST}" 45 | ) 46 | local EMESON_SOURCE="${S}/subprojects/bindings-python" 47 | python_foreach_impl meson_src_configure --pkg-config-path="${WORKDIR}/${P}-build/meson-uninstalled" 48 | } 49 | 50 | bindings_python_compile() { 51 | local EMESON_SOURCE="${S}/subprojects/bindings-python" 52 | python_foreach_impl meson_src_compile 53 | } 54 | 55 | bindings_python_install() { 56 | local EMESON_SOURCE="${S}/subprojects/bindings-python" 57 | python_foreach_impl meson_src_install 58 | } 59 | 60 | bindings_python_test() { 61 | local EMESON_SOURCE="${S}/subprojects/bindings-python" 62 | python_foreach_impl meson_src_test 63 | } 64 | 65 | if [[ ${PV} == 9999 ]]; then 66 | src_unpack() { 67 | git-r3_src_unpack 68 | cd "${S}/subprojects/bindings-python" || die 69 | meson subprojects download || die 70 | } 71 | fi 72 | 73 | src_configure() { 74 | local TEST="false" 75 | if has test ${FEATURES}; then 76 | TEST="true" 77 | fi 78 | local emesonargs=( 79 | '-Dbindings=[]' 80 | "-Dtest=${TEST}" 81 | ) 82 | meson_src_configure 83 | use python && bindings_python_configure 84 | } 85 | 86 | src_compile() { 87 | meson_src_compile 88 | use python && LD_LIBRARY_PATH="${WORKDIR}/${P}-build/lib;${LD_LIBRARY_PATH}" bindings_python_compile 89 | } 90 | 91 | src_install() { 92 | meson_src_install 93 | use python && bindings_python_install 94 | } 95 | 96 | src_test() { 97 | meson_src_test 98 | use python && LD_LIBRARY_PATH="${WORKDIR}/${P}-build/lib;${LD_LIBRARY_PATH}" bindings_python_test 99 | } 100 | -------------------------------------------------------------------------------- /.github/localrepo/metadata/layout.conf: -------------------------------------------------------------------------------- 1 | thin-manifests = false 2 | masters = gentoo 3 | auto-sync = false 4 | -------------------------------------------------------------------------------- /.github/localrepo/profiles/repo_name: -------------------------------------------------------------------------------- 1 | localrepo -------------------------------------------------------------------------------- /.github/workflows/alpine.yml: -------------------------------------------------------------------------------- 1 | # GitHub actions workflow. 2 | # https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions 3 | 4 | name: Alpine 5 | 6 | on: 7 | pull_request: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | build: 15 | strategy: 16 | matrix: 17 | cc: [gcc, clang] 18 | valgrind: [valgrind, vanilla] 19 | fail-fast: false 20 | runs-on: ubuntu-latest 21 | container: alpine:latest 22 | env: 23 | CC: ${{ matrix.cc }} 24 | steps: 25 | - name: Install dependencies 26 | run: | 27 | apk --no-cache add meson pkgconf boost-dev python3-dev build-base valgrind ${{ matrix.cc }} 28 | 29 | - uses: actions/checkout@v4 30 | 31 | - name: Configure 32 | run: | 33 | case "${{ matrix.cc }}" in 34 | gcc) 35 | export CC=gcc 36 | export CXX=g++ 37 | ;; 38 | clang) 39 | export CC=clang 40 | export CXX=clang++ 41 | ;; 42 | esac 43 | 44 | meson setup -Dtest=true -Dwerror=true build 45 | 46 | - name: Build 47 | run: | 48 | ninja -C build 49 | 50 | - name: Test 51 | run: | 52 | case "${{ matrix.valgrind }}" in 53 | valgrind) 54 | args="--wrapper valgrind" 55 | ;; 56 | vanilla) 57 | ;; 58 | esac 59 | 60 | meson test -C build --print-errorlogs ${args} 61 | -------------------------------------------------------------------------------- /.github/workflows/clang-tidy.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | clang-tidy: 12 | runs-on: ubuntu-latest 13 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | # we need meson >= 1.7 for nicer clang-tidy output 19 | - name: install meson 20 | run: | 21 | python -m venv .venv 22 | .venv/bin/pip install meson 23 | 24 | # we want clang-tidy >= 20 for some misc-include-cleaner fixes, but that's not in binhost yet 25 | # extract to /usr/local so that clang recognizes it's own headers as system headers 26 | - name: install clang-tidy 27 | run: | 28 | curl -s -L https://github.com/llvm/llvm-project/releases/download/llvmorg-20.1.0/LLVM-20.1.0-Linux-X64.tar.xz | tar --xz --extract -C /usr/local --wildcards 'LLVM-20.1.0-Linux-X64/bin/clang*' 'LLVM-20.1.0-Linux-X64/lib/clang/*' 29 | 30 | - name: configure 31 | run: | 32 | . /etc/profile 33 | . .venv/bin/activate 34 | export PATH="/usr/local/LLVM-20.1.0-Linux-X64/bin:${PATH}" 35 | CC=clang CXX=clang++ meson setup -Dtest=true build 36 | 37 | - name: analyze 38 | run: | 39 | . /etc/profile 40 | . .venv/bin/activate 41 | export PATH="/usr/local/LLVM-20.1.0-Linux-X64/bin:${PATH}" 42 | ninja -C build clang-tidy 43 | -------------------------------------------------------------------------------- /.github/workflows/codechecker.yml: -------------------------------------------------------------------------------- 1 | name: "CodeChecker" 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | analyze: 12 | runs-on: ubuntu-latest 13 | container: 14 | image: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 15 | volumes: 16 | # https://github.com/actions/runner/issues/716 17 | - /home/runner/work/_actions/whisperity/codechecker-analysis-action/:/home/runner/work/_actions/whisperity/codechecker-analysis-action/ 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Prepare CodeChecker 22 | run: | 23 | git config --global --add safe.directory . 24 | # CodeChecker calls pip install directly 25 | rm -f /usr/lib/python*/EXTERNALLY-MANAGED 26 | # CodeChecker seems to hardcode PATH 27 | ln -s $(command -v clang-tidy) /usr/bin/clang-tidy 28 | 29 | - name: Configure 30 | run: | 31 | meson setup build 32 | 33 | - uses: whisperity/codechecker-analysis-action@v1 34 | id: codechecker 35 | with: 36 | logfile: build/compile_commands.json 37 | llvm-version: ignore 38 | 39 | - uses: actions/upload-artifact@v4 40 | with: 41 | name: "CodeChecker Bug Reports" 42 | path: ${{ steps.codechecker.outputs.result-html-dir }} 43 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | # GitHub wants this to happen on a push to main... 5 | push: 6 | branches: [main] 7 | pull_request: 8 | schedule: 9 | - cron: "00 00 * * 1" 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze 18 | runs-on: ubuntu-latest 19 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 20 | permissions: 21 | # required for all workflows 22 | security-events: write 23 | 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | language: ["c-cpp"] 28 | 29 | steps: 30 | - name: Checkout repository 31 | uses: actions/checkout@v4 32 | 33 | - name: fixup git permissions 34 | run: git config --global --add safe.directory $(realpath .) 35 | 36 | - name: configure 37 | run: | 38 | . /etc/profile 39 | CC=clang CXX=clang++ meson setup -Dtest=true build 40 | 41 | - name: Initialize CodeQL 42 | id: init_codeql 43 | uses: github/codeql-action/init@v3 44 | with: 45 | languages: ${{ matrix.language }} 46 | 47 | - name: fixup LD_PRELOAD 48 | run: | 49 | echo "LD_PRELOAD=/__t/CodeQL/${{ steps.init_codeql.outputs.codeql-version }}/x64/codeql/tools/linux64/lib64trace.so" >> ${GITHUB_ENV} 50 | 51 | - name: compile 52 | if: ${{ matrix.language == 'c-cpp' }} 53 | run: | 54 | . /etc/profile 55 | ninja -C build 56 | 57 | - name: Perform CodeQL Analysis 58 | uses: github/codeql-action/analyze@v3 59 | with: 60 | category: "/language:${{matrix.language}}" 61 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: "docs" 2 | 3 | on: 4 | push: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | docs: 12 | name: Build documentation 13 | permissions: 14 | contents: read 15 | deployments: write 16 | runs-on: ubuntu-latest 17 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 18 | strategy: 19 | fail-fast: false 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Configure venv 26 | run: | 27 | python -m venv .venv 28 | .venv/bin/pip install Sphinx sphinx-toolbox sphinx-rtd-theme enum-tools 29 | 30 | - name: Configure 31 | run: | 32 | . .venv/bin/activate 33 | meson setup -Dtest=false -Ddocs=true -Dbuildtype=debugoptimized build 34 | 35 | - name: Build 36 | run: | 37 | . .venv/bin/activate 38 | meson compile -C build docs 39 | 40 | - name: Test 41 | run: | 42 | . .venv/bin/activate 43 | meson test -C build 44 | 45 | - name: Install npm 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version: latest 49 | 50 | - name: Upload docs 51 | uses: cloudflare/pages-action@v1 52 | with: 53 | directory: build/subprojects/bindings-python/docs 54 | apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 55 | accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 56 | projectName: pms-utils 57 | gitHubToken: ${{ secrets.GITHUB_TOKEN }} 58 | wranglerVersion: "3" 59 | branch: ${{ github.head_ref || github.ref_name }} 60 | -------------------------------------------------------------------------------- /.github/workflows/emscripten.yml: -------------------------------------------------------------------------------- 1 | name: Emscripten 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 14 | defaults: 15 | run: 16 | shell: bash 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | # we need a newer node than provided in emsdk for navigator to be available 22 | # also, this needs to run before emsdk, as otherwise it adds its own node to PATH 23 | - name: install nodejs 24 | run: | 25 | emerge nodejs 26 | 27 | - name: install emscripten 28 | run: | 29 | cd 30 | git clone https://github.com/emscripten-core/emsdk.git 31 | cd emsdk 32 | ./emsdk install latest 33 | ./emsdk activate latest 34 | source emsdk_env.sh 35 | echo "PATH=${PATH}" >> ${GITHUB_ENV} 36 | 37 | - uses: ./.github/actions/install-boost 38 | with: 39 | CXXFLAGS: "-pthread -fwasm-exceptions -flto -O3" 40 | LDFLAGS: "-pthread -sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency -fwasm-exceptions -flto -O3" 41 | cmake_wrapper: emcmake 42 | 43 | - name: configure 44 | run: | 45 | meson setup --cross-file wasm.ini -Db_lto=true --buildtype=release -Dtest=true build 46 | 47 | - name: compile 48 | run: | 49 | meson compile -C build 50 | 51 | - name: test 52 | run: | 53 | meson test -C build 54 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | prettier: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - uses: actions/setup-node@v4 18 | 19 | - run: npm install prettier 20 | 21 | - run: npx prettier --check . 22 | 23 | clang-format: 24 | runs-on: ubuntu-latest 25 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - name: configure 31 | run: | 32 | . /etc/profile 33 | meson setup -Dtest=true build 34 | 35 | - name: check 36 | run: | 37 | . /etc/profile 38 | ninja -C build clang-format-check 39 | 40 | meson: 41 | runs-on: ubuntu-latest 42 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 43 | 44 | steps: 45 | - uses: actions/checkout@v4 46 | 47 | - run: | 48 | meson format --recursive --check-only . 49 | meson format --recursive --check-only subprojects/bindings-python 50 | 51 | black: 52 | runs-on: ubuntu-latest 53 | 54 | steps: 55 | - uses: actions/checkout@v4 56 | 57 | - run: | 58 | pipx install black 59 | black --diff . 60 | -------------------------------------------------------------------------------- /.github/workflows/gentoo-container.yml: -------------------------------------------------------------------------------- 1 | name: Gentoo-CI-Container 2 | 3 | on: 4 | schedule: 5 | - cron: "00 00 * * 0" 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: ${{ github.workflow }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | packages: write 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Login 24 | uses: docker/login-action@v3 25 | with: 26 | registry: ghcr.io 27 | username: ${{ github.actor }} 28 | password: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Extract metadata (tags, labels) for Docker 31 | id: meta 32 | uses: docker/metadata-action@v5 33 | with: 34 | images: ghcr.io/Jannik2099/pms-utils-gentoo-ci 35 | tags: | 36 | type=raw,value=latest 37 | 38 | - name: Build and push 39 | uses: docker/build-push-action@v5 40 | with: 41 | push: true 42 | tags: ${{ steps.meta.outputs.tags }} 43 | labels: ${{ steps.meta.outputs.labels }} 44 | file: .github/Dockerfile 45 | -------------------------------------------------------------------------------- /.github/workflows/gentoo.yml: -------------------------------------------------------------------------------- 1 | name: Gentoo 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | matrix: 14 | cc: [gcc, clang] 15 | sanitizer: [none, address, undefined] 16 | type: [debugoptimized, release] 17 | exclude: 18 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71962 19 | - cc: gcc 20 | sanitizer: undefined 21 | fail-fast: false 22 | runs-on: ubuntu-latest 23 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 24 | env: 25 | SANITIZER: ${{ matrix.sanitizer }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - name: Set up ebuild repo 30 | run: | 31 | mkdir /etc/portage/repos.conf 32 | cp .github/gentoo.conf /etc/portage/repos.conf 33 | cp .github/localrepo.conf /etc/portage/repos.conf 34 | pushd /etc/portage 35 | 36 | mkdir -p package.accept_keywords 37 | echo "dev-libs/pms-utils **" > package.accept_keywords/pms-utils 38 | 39 | mkdir -p package.env 40 | mkdir -p env 41 | 42 | echo "dev-libs/pms-utils myenv" > package.env/pms-utils 43 | TEMP=\"${{ github.repositoryUrl }}\" 44 | TEMP="$(echo ${TEMP} | sed 's/git/https/')" 45 | echo EGIT_OVERRIDE_REPO_JANNIK2099_PMS_UTILS=\"${TEMP}\" >> env/myenv 46 | echo EGIT_OVERRIDE_COMMIT_JANNIK2099_PMS_UTILS=\"${{ github.ref }}\" >> env/myenv 47 | 48 | case "${{ matrix.cc }}" in 49 | gcc) 50 | echo CC=gcc >> env/myenv 51 | echo CXX=g++ >> env/myenv 52 | ;; 53 | clang) 54 | echo CC=clang >> env/myenv 55 | echo CXX=clang++ >> env/myenv 56 | echo LDFLAGS=\"-fuse-ld=lld\" >> env/myenv 57 | args+="-Db_lto_mode=thin -Db_lundef=false " 58 | ;; 59 | esac 60 | 61 | echo FEATURES='"${FEATURES} test"' >> env/myenv 62 | echo PYTHON_TARGETS='"${PYTHON_TARGETS} pypy3"' >> env/myenv 63 | echo "MYMESONARGS=\"-Dbuildtype=${{ matrix.type }} -Dwerror=true -Db_lto=true -Db_sanitize=${SANITIZER} ${args}\"" >> env/myenv 64 | popd 65 | 66 | cp -r .github/localrepo /var/db/repos/localrepo 67 | cd /var/db/repos/localrepo/dev-libs/pms-utils 68 | pkgdev manifest -f 69 | 70 | - name: Install package dependencies 71 | run: | 72 | emerge --autounmask --autounmask-write --autounmask-continue -q -j8 -o dev-libs/pms-utils 73 | 74 | - name: Configure 75 | run: | 76 | cd /var/db/repos/localrepo/dev-libs/pms-utils 77 | ebuild pms-utils-9999.ebuild configure 78 | 79 | - name: Compile 80 | run: | 81 | cd /var/db/repos/localrepo/dev-libs/pms-utils 82 | ebuild pms-utils-9999.ebuild compile 83 | 84 | - name: Test 85 | run: | 86 | if [[ "${{ matrix.sanitizer }}" == "address" ]]; then 87 | export FEATURES="${FEATURES} -sandbox -usersandbox" 88 | fi 89 | cd /var/db/repos/localrepo/dev-libs/pms-utils 90 | ebuild pms-utils-9999.ebuild test 91 | 92 | - name: Install 93 | run: | 94 | cd /var/db/repos/localrepo/dev-libs/pms-utils 95 | ebuild pms-utils-9999.ebuild install 96 | 97 | - name: Merge 98 | run: | 99 | cd /var/db/repos/localrepo/dev-libs/pms-utils 100 | ebuild pms-utils-9999.ebuild merge 101 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | # GitHub actions workflow. 2 | # https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions 3 | 4 | name: macOS 5 | 6 | on: 7 | pull_request: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | build: 15 | strategy: 16 | matrix: 17 | os: [macos-latest] 18 | cc: [gcc, clang] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: Install dependencies 22 | run: | 23 | brew install meson ninja boost 24 | 25 | case "${{ matrix.cc }}" in 26 | gcc) 27 | brew install gcc 28 | ;; 29 | clang) 30 | brew install llvm 31 | ;; 32 | esac 33 | 34 | - uses: actions/checkout@v4 35 | 36 | - name: Configure 37 | run: | 38 | # Silly hacks to get brew-installed GCC and Clang on macOS 39 | if [[ -d /usr/local/bin ]] ; then 40 | export PATH="/usr/local/bin:${PATH}" 41 | # It's fine if this already exists. 42 | ln -s $(ls -1v /usr/local/bin/gcc-* | head -n 1) /usr/local/bin/gcc || true 43 | ln -s $(ls -1v /usr/local/bin/g++-* | head -n 1) /usr/local/bin/g++ || true 44 | fi 45 | if [[ -d /usr/local/opt/llvm ]] ; then 46 | export CPPFLAGS="-I/usr/local/opt/llvm/include ${CPPFLAGS}" 47 | export LDFLAGS="-L/usr/local/opt/llvm/lib ${LDFLAGS}" 48 | export PATH="/usr/local/opt/llvm/bin:${PATH}" 49 | fi 50 | 51 | case "${{ matrix.cc }}" in 52 | gcc) 53 | export CC=/usr/local/bin/gcc 54 | export CXX=/usr/local/bin/g++ 55 | ;; 56 | clang) 57 | export LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -Wl,-rpath,/usr/local/opt/llvm/lib/c++ ${LDFLAGS}" 58 | export CC=/usr/local/opt/llvm/bin/clang 59 | export CXX=/usr/local/opt/llvm/bin/clang++ 60 | ;; 61 | esac 62 | 63 | meson setup -Dtest=true -Dwerror=true build 64 | 65 | - name: Build 66 | run: | 67 | ninja -C build 68 | 69 | - name: Test 70 | run: | 71 | meson test -C build --print-errorlogs 72 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | name: "PyPI" 2 | 3 | on: 4 | pull_request: 5 | release: 6 | types: [published] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | include: 17 | - platform: ubuntu-latest 18 | arch: x86_64 19 | - platform: ubuntu-24.04-arm 20 | arch: aarch64 21 | 22 | runs-on: ${{ matrix.platform }} 23 | container: quay.io/pypa/manylinux_2_34_${{ matrix.arch }} 24 | env: 25 | MESONARGS: -Db_lto=true --prefer-static -Dcpp_link_args=['-static-libgcc','-static-libstdc++'] --default-library=static 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - name: install ninja 31 | run: | 32 | dnf install -y ninja-build 33 | 34 | # the version packaged in the container image is way too old 35 | - name: install meson 36 | run: | 37 | pipx install meson 38 | 39 | - uses: ./.github/actions/install-boost 40 | 41 | - name: build pms-utils 42 | run: | 43 | meson setup ${MESONARGS} --prefix=/usr --buildtype=release -Dbindings="[]" build 44 | meson install -C build 45 | 46 | - name: build python extensions 47 | run: | 48 | git config --global safe.directory "*" 49 | 50 | MYARGS="-Csetup-args=-Donly-wheels=true " 51 | for ARG in ${MESONARGS}; do 52 | MYARGS+="-Csetup-args=${ARG} " 53 | done 54 | for VER in /opt/python/*/bin/python; do 55 | # meson-python requires >= 3.7, nanobind >= 3.8 56 | if ${VER} -c 'import platform; assert int(platform.python_version_tuple()[1]) < 8' 2&> /dev/null; then 57 | continue 58 | fi 59 | 60 | "${VER}" -m build --wheel ${MYARGS} --outdir dist . 61 | done 62 | 63 | - name: fixup wheels 64 | run: | 65 | auditwheel repair --strip --plat manylinux_2_34_${{ matrix.arch }} dist/*.whl 66 | 67 | - name: upload artifacts 68 | uses: actions/upload-artifact@v4 69 | with: 70 | name: pypi wheels ${{ matrix.arch }} 71 | path: wheelhouse/ 72 | 73 | publish: 74 | runs-on: ubuntu-latest 75 | if: github.event_name == 'release' 76 | needs: build 77 | permissions: 78 | id-token: write 79 | 80 | steps: 81 | - name: download artifacts 82 | uses: actions/download-artifact@v4 83 | with: 84 | pattern: pypi wheels * 85 | path: wheelhouse/ 86 | merge-multiple: true 87 | 88 | - uses: pypa/gh-action-pypi-publish@release/v1 89 | with: 90 | packages-dir: wheelhouse 91 | skip-existing: true 92 | -------------------------------------------------------------------------------- /.github/workflows/sonarlint.yml: -------------------------------------------------------------------------------- 1 | name: "SonarLint" 2 | 3 | on: 4 | push: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | analyze: 12 | name: Analyze 13 | runs-on: ubuntu-latest 14 | container: ghcr.io/jannik2099/pms-utils-gentoo-ci:latest 15 | strategy: 16 | fail-fast: false 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 23 | 24 | - name: Configure 25 | run: meson setup -Dtest=true -Db_coverage=true -Dbuildtype=debugoptimized build 26 | 27 | - name: Build 28 | run: meson compile -C build 29 | 30 | - name: Test 31 | run: meson test -C build 32 | 33 | - name: Collect coverage data 34 | run: ninja -C build coverage-sonarqube 35 | 36 | - name: SonarQube Scan 37 | uses: SonarSource/sonarqube-scan-action@v5 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 41 | with: 42 | args: > 43 | --define sonar.cfamily.compile-commands="build/compile_commands.json" 44 | --define sonar.coverageReportPaths="build/meson-logs/sonarqube.xml" 45 | -------------------------------------------------------------------------------- /.sonarlint/connectedMode.json: -------------------------------------------------------------------------------- 1 | { 2 | "sonarCloudOrganization": "jannik2099", 3 | "projectKey": "Jannik2099_pms-utils" 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PMS-Utils 2 | 3 | WARNING: this is merely a proof of concept, don't rely on it for anything. 4 | 5 | PMS-Utils is a collection of utilities and algorithms to implement Gentoo's [Package Manager Specification](https://wiki.gentoo.org/wiki/Package_Manager_Specification). 6 | 7 | ## Documentation 8 | 9 | See the [documentation](https://pms-utils.pages.dev) for usage and coverage of the provided APIs. 10 | Only the Python bindings are documented as of now, the C++ headers are undocumented. 11 | -------------------------------------------------------------------------------- /docs/meson.build: -------------------------------------------------------------------------------- 1 | bindings_python_docs_tgt = bindings_python.get_variable('bindings_python_docs') 2 | alias_target('docs', bindings_python_docs_tgt) 3 | -------------------------------------------------------------------------------- /include/meson.build: -------------------------------------------------------------------------------- 1 | inc_lib = include_directories('.') 2 | 3 | install_headers( 4 | [ 5 | 'pms-utils/atom/atom.hpp', 6 | 'pms-utils/atom/atom_parser.hpp', 7 | 'pms-utils/depend/depend.hpp', 8 | 'pms-utils/depend/depend_parser.hpp', 9 | 'pms-utils/ebuild/ebuild.hpp', 10 | 'pms-utils/ebuild/ebuild_parser.hpp', 11 | 'pms-utils/repo/repo.hpp', 12 | 'pms-utils/repo/repo_parser.hpp', 13 | 'pms-utils/profile/profile.hpp', 14 | 'pms-utils/profile/profile_parser.hpp', 15 | 'pms-utils/misc/macro-begin.hpp', 16 | 'pms-utils/misc/macro-end.hpp', 17 | 'pms-utils/misc/meta.hpp', 18 | 'pms-utils/misc/x3_utils.hpp', 19 | 'pms-utils/misc/try_parse.hpp', 20 | ], 21 | preserve_path: true, 22 | ) 23 | -------------------------------------------------------------------------------- /include/pms-utils/atom/atom.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pms-utils/misc/meta.hpp" 4 | 5 | #include 6 | #include 7 | #include // IWYU pragma: keep 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace [[gnu::visibility("default")]] pms_utils { 16 | namespace atom { 17 | 18 | // BEGIN ast types 19 | 20 | enum class VersionSpecifier : std::uint8_t { 21 | lt, // < 22 | le, // <= 23 | eq, // = 24 | ea, // =* 25 | td, // ~ 26 | ge, // > 27 | gt, // >= 28 | }; 29 | 30 | struct VersionNumber : public std::vector { 31 | private: 32 | std::ostream &ostream_impl(std::ostream &out) const; 33 | 34 | public: 35 | [[nodiscard]] explicit operator std::string() const; 36 | friend std::ostream &operator<<(std::ostream &out, const VersionNumber &versionNumber) { 37 | return versionNumber.ostream_impl(out); 38 | } 39 | 40 | BOOST_DESCRIBE_CLASS(VersionNumber, (), (), (), (ostream_impl)); 41 | }; 42 | 43 | using VersionLetter = char; 44 | 45 | enum class VersionSuffixWord : std::uint8_t { 46 | alpha, 47 | beta, 48 | pre, 49 | rc, 50 | p, 51 | }; 52 | // Sorted as per PMS, _alpha < _beta < _pre < _rc < _p 53 | [[nodiscard]] std::strong_ordering operator<=>(VersionSuffixWord lhs, VersionSuffixWord rhs); 54 | 55 | struct VersionSuffix { 56 | private: 57 | std::ostream &ostream_impl(std::ostream &out) const; 58 | 59 | public: 60 | VersionSuffixWord word{}; 61 | std::string number; 62 | 63 | [[nodiscard]] explicit operator std::string() const; 64 | friend std::ostream &operator<<(std::ostream &out, const VersionSuffix &suffix) { 65 | return suffix.ostream_impl(out); 66 | } 67 | 68 | BOOST_DESCRIBE_CLASS(VersionSuffix, (), (word, number), (), (ostream_impl)); 69 | }; 70 | struct VersionRevision : public std::string {}; 71 | 72 | struct Version { 73 | private: 74 | [[nodiscard]] static std::strong_ordering compare_impl(const Version &lhs, const Version &rhs, 75 | bool revision) noexcept; 76 | [[nodiscard]] static bool compare_td_impl(const Version &lhs, const Version &rhs) noexcept; 77 | std::ostream &ostream_impl(std::ostream &out) const; 78 | 79 | public: 80 | VersionNumber numbers; 81 | std::optional letter; 82 | std::vector suffixes; 83 | // an absent version is implicitly -r0, but we need to know presence to give an accurate string 84 | // representation for =* matching 85 | std::optional revision; 86 | 87 | [[nodiscard]] explicit operator std::string() const; 88 | friend std::ostream &operator<<(std::ostream &out, const Version &version) { 89 | return version.ostream_impl(out); 90 | } 91 | 92 | [[nodiscard]] friend std::strong_ordering operator<=>(const Version &lhs, const Version &rhs) noexcept { 93 | return compare_impl(lhs, rhs, true); 94 | } 95 | 96 | // lhs =~ rhs (equal modulo revision) 97 | [[nodiscard]] friend bool compare_td(const Version &lhs, const Version &rhs) noexcept { 98 | return compare_td_impl(lhs, rhs); 99 | } 100 | 101 | BOOST_DESCRIBE_CLASS(Version, (), (numbers, letter, suffixes, revision), (), 102 | (compare_impl, compare_td_impl, ostream_impl)); 103 | }; 104 | 105 | enum class Blocker : std::uint8_t { 106 | weak, // ! 107 | strong, // !! 108 | }; 109 | 110 | struct SlotNoSubslot : public std::string {}; 111 | 112 | struct Slot { 113 | private: 114 | std::ostream &ostream_impl(std::ostream &out) const; 115 | 116 | public: 117 | std::string slot; 118 | std::string subslot; 119 | 120 | [[nodiscard]] explicit operator std::string() const; 121 | friend std::ostream &operator<<(std::ostream &out, const Slot &slot_) { return slot_.ostream_impl(out); } 122 | 123 | BOOST_DESCRIBE_CLASS(Slot, (), (slot, subslot), (), (ostream_impl)); 124 | }; 125 | 126 | enum class SlotVariant : std::uint8_t { 127 | none, // :slot 128 | star, // :* 129 | equal, // :slot= or := 130 | }; 131 | struct SlotExpr { 132 | private: 133 | std::ostream &ostream_impl(std::ostream &out) const; 134 | 135 | public: 136 | SlotVariant slotVariant{}; 137 | std::optional slot; 138 | 139 | [[nodiscard]] explicit operator std::string() const; 140 | friend std::ostream &operator<<(std::ostream &out, const SlotExpr &slotExpr) { 141 | return slotExpr.ostream_impl(out); 142 | } 143 | 144 | BOOST_DESCRIBE_CLASS(SlotExpr, (), (slotVariant, slot), (), (ostream_impl)); 145 | }; 146 | 147 | struct Category : public std::string {}; 148 | struct Name : public std::string {}; 149 | 150 | struct Useflag : public std::string {}; 151 | enum class UsedepNegate : std::uint8_t { 152 | minus, // -use 153 | exclamation, // !use 154 | }; 155 | enum class UsedepSign : std::uint8_t { 156 | plus, // use(+) 157 | minus, // use(-) 158 | }; 159 | enum class UsedepCond : std::uint8_t { 160 | eqal, // use= 161 | question, // use? 162 | }; 163 | struct Usedep { 164 | private: 165 | std::ostream &ostream_impl(std::ostream &out) const; 166 | 167 | public: 168 | std::optional negate; 169 | Useflag useflag; 170 | std::optional sign; 171 | std::optional conditional; 172 | 173 | [[nodiscard]] explicit operator std::string() const; 174 | friend std::ostream &operator<<(std::ostream &out, const Usedep &usedep) { 175 | return usedep.ostream_impl(out); 176 | } 177 | 178 | BOOST_DESCRIBE_CLASS(Usedep, (), (negate, useflag, sign, conditional), (), (ostream_impl)); 179 | }; 180 | struct Usedeps : public std::vector { 181 | private: 182 | std::ostream &ostream_impl(std::ostream &out) const; 183 | 184 | public: 185 | [[nodiscard]] explicit operator std::string() const; 186 | friend std::ostream &operator<<(std::ostream &out, const Usedeps &usedeps) { 187 | return usedeps.ostream_impl(out); 188 | } 189 | 190 | BOOST_DESCRIBE_CLASS(Usedeps, (), (), (), (ostream_impl)); 191 | }; 192 | 193 | struct PackageExpr { 194 | private: 195 | std::ostream &ostream_impl(std::ostream &out) const; 196 | 197 | public: 198 | std::optional blocker; 199 | Category category; 200 | Name name; 201 | std::optional verspec; 202 | std::optional version; 203 | std::optional slotExpr; 204 | Usedeps usedeps; 205 | 206 | [[nodiscard]] explicit operator std::string() const; 207 | friend std::ostream &operator<<(std::ostream &out, const PackageExpr &package) { 208 | return package.ostream_impl(out); 209 | } 210 | 211 | BOOST_DESCRIBE_CLASS(PackageExpr, (), (blocker, category, name, verspec, version, slotExpr, usedeps), (), 212 | (ostream_impl)); 213 | }; 214 | 215 | // END ast types 216 | 217 | // BEGIN DESCRIBE 218 | 219 | BOOST_DESCRIBE_ENUM(VersionSpecifier, lt, le, eq, ea, td, ge, gt); 220 | 221 | BOOST_DESCRIBE_ENUM(VersionSuffixWord, alpha, beta, pre, rc, p); 222 | 223 | BOOST_DESCRIBE_STRUCT(VersionRevision, (), ()); 224 | 225 | BOOST_DESCRIBE_ENUM(Blocker, weak, strong); 226 | 227 | BOOST_DESCRIBE_STRUCT(SlotNoSubslot, (), ()); 228 | 229 | BOOST_DESCRIBE_ENUM(SlotVariant, none, star, equal); 230 | 231 | BOOST_DESCRIBE_STRUCT(Category, (), ()); 232 | 233 | BOOST_DESCRIBE_STRUCT(Name, (), ()); 234 | 235 | BOOST_DESCRIBE_STRUCT(Useflag, (), ()); 236 | 237 | BOOST_DESCRIBE_ENUM(UsedepNegate, minus, exclamation); 238 | BOOST_DESCRIBE_ENUM(UsedepSign, plus, minus); 239 | BOOST_DESCRIBE_ENUM(UsedepCond, eqal, question); 240 | 241 | namespace meta { 242 | 243 | using all = 244 | boost::mp11::mp_list; 247 | 248 | } // namespace meta 249 | 250 | // END DESCRIBE 251 | 252 | // BEGIN IO 253 | 254 | [[nodiscard]] std::string to_string(VersionSpecifier versionSpecifier); 255 | std::ostream &operator<<(std::ostream &out, VersionSpecifier versionSpecifier); 256 | 257 | [[nodiscard]] std::string to_string(VersionSuffixWord versionSuffixWord); 258 | std::ostream &operator<<(std::ostream &out, VersionSuffixWord versionSuffixWord); 259 | 260 | [[nodiscard]] std::string to_string(Blocker blocker); 261 | std::ostream &operator<<(std::ostream &out, Blocker blocker); 262 | 263 | [[nodiscard]] std::string to_string(UsedepNegate usedepNegate); 264 | std::ostream &operator<<(std::ostream &out, UsedepNegate usedepNegate); 265 | 266 | [[nodiscard]] std::string to_string(UsedepSign usedepSign); 267 | std::ostream &operator<<(std::ostream &out, UsedepSign usedepSign); 268 | 269 | [[nodiscard]] std::string to_string(UsedepCond usedepCond); 270 | std::ostream &operator<<(std::ostream &out, UsedepCond usedepCond); 271 | 272 | // END IO 273 | 274 | } // namespace atom 275 | } // namespace pms_utils 276 | 277 | PMS_UTILS_FOOTER(atom); 278 | -------------------------------------------------------------------------------- /include/pms-utils/atom/atom_parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "atom.hpp" 4 | #include "pms-utils/misc/x3_utils.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace pms_utils::parsers::atom { 14 | 15 | PARSER_RULE_T(version_specifier, pms_utils::atom::VersionSpecifier); 16 | PARSER_RULE_T(blocker, pms_utils::atom::Blocker); 17 | PARSER_RULE_T(slot_no_subslot, pms_utils::atom::SlotNoSubslot); 18 | PARSER_RULE_T(slot, pms_utils::atom::Slot); 19 | PARSER_RULE_T(slot_expr, pms_utils::atom::SlotExpr); 20 | 21 | PARSER_RULE_T(ver_num, pms_utils::atom::VersionNumber); 22 | PARSER_RULE_T(ver_letter, pms_utils::atom::VersionLetter); 23 | PARSER_RULE_T(ver_suffix_word, pms_utils::atom::VersionSuffixWord); 24 | PARSER_RULE_T(ver_suffix, pms_utils::atom::VersionSuffix); 25 | PARSER_RULE_T(ver_rev, pms_utils::atom::VersionRevision); 26 | PARSER_RULE_T(package_version, pms_utils::atom::Version); 27 | 28 | PARSER_RULE_T(category, pms_utils::atom::Category); 29 | PARSER_RULE_T(name, pms_utils::atom::Name); 30 | 31 | PARSER_RULE_T(useflag, pms_utils::atom::Useflag); 32 | PARSER_RULE_T(use_dep, pms_utils::atom::Usedep); 33 | PARSER_RULE_T(use_deps, pms_utils::atom::Usedeps); 34 | 35 | PARSER_RULE_T(package_dep, pms_utils::atom::PackageExpr); 36 | PARSER_RULE_T(atom, pms_utils::atom::PackageExpr); 37 | 38 | namespace _internal { 39 | 40 | inline const boost::parser::symbols VerSpec{ 41 | // This intentionally lacks VersionSpecifier::ea because we cannot match against it directly 42 | {"<", pms_utils::atom::VersionSpecifier::lt}, {"<=", pms_utils::atom::VersionSpecifier::le}, 43 | {"=", pms_utils::atom::VersionSpecifier::eq}, {"~", pms_utils::atom::VersionSpecifier::td}, 44 | {">=", pms_utils::atom::VersionSpecifier::ge}, {">", pms_utils::atom::VersionSpecifier::gt}, 45 | }; 46 | 47 | inline const boost::parser::symbols Blocker{ 48 | {"!", pms_utils::atom::Blocker::weak}, 49 | {"!!", pms_utils::atom::Blocker::strong}, 50 | }; 51 | 52 | inline const boost::parser::symbols VersionSuffixWord{ 53 | {"_alpha", pms_utils::atom::VersionSuffixWord::alpha}, 54 | {"_beta", pms_utils::atom::VersionSuffixWord::beta}, 55 | {"_pre", pms_utils::atom::VersionSuffixWord::pre}, 56 | {"_rc", pms_utils::atom::VersionSuffixWord::rc}, 57 | {"_p", pms_utils::atom::VersionSuffixWord::p}, 58 | }; 59 | 60 | inline const boost::parser::symbols UsedepNegate{ 61 | {"-", pms_utils::atom::UsedepNegate::minus}, 62 | {"!", pms_utils::atom::UsedepNegate::exclamation}, 63 | }; 64 | inline const boost::parser::symbols UsedepSign{ 65 | {"+", pms_utils::atom::UsedepSign::plus}, 66 | {"-", pms_utils::atom::UsedepSign::minus}, 67 | }; 68 | 69 | inline const boost::parser::symbols UsedepConditional{ 70 | {"=", pms_utils::atom::UsedepCond::eqal}, 71 | {"?", pms_utils::atom::UsedepCond::question}, 72 | }; 73 | 74 | constexpr inline auto slot_helper = [](T &ctx) { 75 | std::tuple> &attr = boost::parser::_attr(ctx); 76 | pms_utils::atom::Slot &val = boost::parser::_val(ctx); 77 | 78 | val = {.slot = std::get<0>(attr), .subslot = std::get<1>(attr).value_or(std::string{})}; 79 | }; 80 | 81 | constexpr inline auto slot_expr_helper = [](T &ctx) { 82 | std::variant, bool>> &attr = 83 | boost::parser::_attr(ctx); 84 | 85 | class Visitor { 86 | public: 87 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) 88 | T &ctx; 89 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) 90 | pms_utils::atom::SlotExpr &val = boost::parser::_val(ctx); 91 | void operator()(char /**/) { val.slotVariant = pms_utils::atom::SlotVariant::star; } 92 | void operator()(std::tuple, bool> &var2) { 93 | std::optional &slot = std::get<0>(var2); 94 | const bool equal = std::get<1>(var2); 95 | 96 | val.slot = std::move(slot); 97 | val.slotVariant = 98 | equal ? pms_utils::atom::SlotVariant::equal : pms_utils::atom::SlotVariant::none; 99 | 100 | if ((!equal) && (!val.slot.has_value())) { 101 | // TODO 102 | boost::parser::_pass(ctx) = false; 103 | } 104 | } 105 | }; 106 | std::visit(Visitor{ctx}, attr); 107 | }; 108 | 109 | constexpr inline auto package_dep_helper = [](T &ctx, bool requireVerSpec) { 110 | // Parser semantic actions are a pathway to many abilities some consider to be unnatural 111 | std::tuple, std::optional, 112 | pms_utils::atom::Category, pms_utils::atom::Name, 113 | std::optional>, 114 | std::optional, std::optional> &attr = 115 | boost::parser::_attr(ctx); 116 | pms_utils::atom::PackageExpr &val = boost::parser::_val(ctx); 117 | 118 | val.blocker = std::get<0>(attr); 119 | std::optional &version_specifier = std::get<1>(attr); 120 | val.category = std::get<2>(attr); 121 | val.name = std::get<3>(attr); 122 | std::optional> &versionPart = std::get<4>(attr); 123 | val.slotExpr = std::get<5>(attr); 124 | val.usedeps = std::get<6>(attr).value_or(pms_utils::atom::Usedeps{}); 125 | 126 | if (versionPart.has_value()) { 127 | const pms_utils::atom::Version &version = std::get<0>(versionPart.value()); 128 | const bool asterisk = std::get<1>(versionPart.value()); 129 | 130 | if ((!version_specifier.has_value()) && requireVerSpec) { 131 | // TODO 132 | boost::parser::_pass(ctx) = false; 133 | } 134 | if (asterisk) { 135 | if (version_specifier == pms_utils::atom::VersionSpecifier::eq) { 136 | version_specifier = pms_utils::atom::VersionSpecifier::ea; 137 | } else { 138 | // TODO 139 | boost::parser::_pass(ctx) = false; 140 | } 141 | } 142 | val.version = version; 143 | val.verspec = version_specifier; 144 | } 145 | }; 146 | 147 | } // namespace _internal 148 | 149 | PARSER_DEFINE(version_specifier, _internal::VerSpec); 150 | PARSER_DEFINE(blocker, _internal::Blocker); 151 | 152 | namespace _internal { 153 | PARSER_RULE_T(slot_impl, std::string); 154 | PARSER_DEFINE(slot_impl, 155 | (aux::alnum | boost::parser::char_('_')) >> *(aux::alnum | boost::parser::char_("_-+."))); 156 | } // namespace _internal 157 | PARSER_DEFINE(slot_no_subslot, _internal::slot_impl); 158 | PARSER_DEFINE(slot, (_internal::slot_impl >> 159 | -(boost::parser::lit('/') >> _internal::slot_impl))[_internal::slot_helper]); 160 | PARSER_DEFINE(slot_expr, 161 | boost::parser::lit(':') >> 162 | (boost::parser::char_('*') | (-slot >> aux::matches('=')))[_internal::slot_expr_helper]); 163 | 164 | PARSER_DEFINE(ver_num, +boost::parser::digit % '.'); 165 | PARSER_DEFINE(ver_letter, boost::parser::lower); 166 | PARSER_DEFINE(ver_suffix_word, _internal::VersionSuffixWord); 167 | PARSER_DEFINE(ver_suffix, ver_suffix_word >> *boost::parser::digit); 168 | PARSER_DEFINE(ver_rev, boost::parser::lit("-r") >> +boost::parser::digit); 169 | PARSER_DEFINE(package_version, ver_num >> -ver_letter >> *ver_suffix >> -ver_rev); 170 | 171 | PARSER_DEFINE(category, 172 | (aux::alnum | boost::parser::char_('_')) >> *(aux::alnum | boost::parser::char_("_-+."))); 173 | // this basically means "name not followed by ( -version which itself is followed by legal atom name charset ) 174 | // Otherwise e.g. name(foo-1-1) would match fully 175 | PARSER_DEFINE(name, (aux::alnum | boost::parser::char_('_')) >> 176 | *(aux::alnum | boost::parser::char_("_+") | 177 | (boost::parser::char_('-') - (+(boost::parser::lit('-') >> package_version) >> 178 | !(aux::alnum | boost::parser::char_("_-+")))))); 179 | 180 | PARSER_DEFINE(useflag, aux::alnum >> *(aux::alnum | boost::parser::char_("_-+@"))); 181 | // TODO: reinstate "UsedepCond needs !, not -" 182 | PARSER_DEFINE(use_dep, -_internal::UsedepNegate >> useflag >> 183 | -(boost::parser::lit('(') >> _internal::UsedepSign >> boost::parser::lit(')')) >> 184 | -_internal::UsedepConditional) 185 | PARSER_DEFINE(use_deps, boost::parser::lit('[') >> use_dep % ',' >> boost::parser::lit(']')); 186 | 187 | // unsure about how to handle the "duplicate rules for requireVerSpec" 188 | namespace _internal { 189 | inline const auto atom_helper = [](bool requireVerSpec) { 190 | return (-blocker >> -version_specifier >> category >> boost::parser::lit('/') >> name >> 191 | -(boost::parser::lit('-') >> package_version >> aux::matches('*')) >> -slot_expr >> 192 | -use_deps)[([requireVerSpec](T &ctx) { package_dep_helper(ctx, requireVerSpec); })]; 193 | }; 194 | } // namespace _internal 195 | 196 | // this way it's still two types, can I pass an arg to the bp rule perhaps? 197 | PARSER_DEFINE(package_dep, _internal::atom_helper(true)); 198 | PARSER_DEFINE(atom, _internal::atom_helper(false)); 199 | } // namespace pms_utils::parsers::atom 200 | -------------------------------------------------------------------------------- /include/pms-utils/depend/depend_parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "depend.hpp" 4 | #include "pms-utils/atom/atom_parser.hpp" 5 | #include "pms-utils/misc/x3_utils.hpp" 6 | 7 | #include 8 | #include 9 | 10 | namespace pms_utils::parsers::depend { 11 | 12 | PARSER_RULE_T(use_cond, pms_utils::depend::UseConditional); 13 | PARSER_RULE_T(conds, pms_utils::depend::GroupHeader); 14 | 15 | // myeah this is kinda icky. Suggestions welcome 16 | template constexpr static auto GroupTemplate1(Rule rule) { 17 | return -(conds >> boost::parser::omit[+aux::space]) >> boost::parser::lit('(') >> 18 | boost::parser::omit[+aux::space] >> rule % +aux::space >> boost::parser::omit[+aux::space] >> 19 | boost::parser::lit(')'); 20 | } 21 | template constexpr static auto GroupTemplate2(Rule rule) { 22 | return boost::parser::attr(std::optional{}) >> 23 | boost::parser::omit[*aux::space] >> rule % +aux::space >> boost::parser::omit[*aux::space]; 24 | } 25 | 26 | PARSER_RULE_T(group, pms_utils::depend::DependExpr); 27 | PARSER_RULE_T(node, pms_utils::depend::DependExpr::Node); 28 | PARSER_RULE_T(nodes, pms_utils::depend::DependExpr); 29 | 30 | namespace _internal { 31 | inline const boost::parser::symbols GroupHeaderOp{ 32 | {"||", pms_utils::depend::GroupHeaderOp::any_of}, 33 | {"^^", pms_utils::depend::GroupHeaderOp::exactly_one_of}, 34 | {"??", pms_utils::depend::GroupHeaderOp::at_most_one_of}, 35 | }; 36 | } // namespace _internal 37 | 38 | PARSER_DEFINE(use_cond, aux::matches('!') >> atom::useflag >> boost::parser::lit('?')); 39 | PARSER_DEFINE(conds, use_cond | _internal::GroupHeaderOp); 40 | 41 | namespace _internal { 42 | PARSER_RULE_T(group_impl, pms_utils::depend::DependExpr::Base); 43 | PARSER_RULE_T(nodes_impl, pms_utils::depend::DependExpr::Base); 44 | 45 | PARSER_DEFINE(group_impl, GroupTemplate1(node)); 46 | PARSER_DEFINE(nodes_impl, GroupTemplate2(node)); 47 | } // namespace _internal 48 | 49 | PARSER_DEFINE(group, _internal::group_impl); 50 | PARSER_DEFINE(node, atom::package_dep | group); 51 | PARSER_DEFINE(nodes, _internal::nodes_impl); 52 | 53 | } // namespace pms_utils::parsers::depend 54 | -------------------------------------------------------------------------------- /include/pms-utils/ebuild/ebuild.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pms-utils/atom/atom.hpp" 4 | #include "pms-utils/depend/depend.hpp" 5 | #include "pms-utils/misc/meta.hpp" 6 | 7 | #include 8 | #include 9 | #include // IWYU pragma: keep 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace [[gnu::visibility("default")]] pms_utils { 19 | namespace ebuild { 20 | 21 | struct URI : public std::string {}; 22 | 23 | struct uri_elem { 24 | private: 25 | std::ostream &ostream_impl(std::ostream &out) const; 26 | 27 | public: 28 | std::variant uri; 29 | std::optional filename; 30 | 31 | [[nodiscard]] explicit operator std::string() const; 32 | friend std::ostream &operator<<(std::ostream &out, const uri_elem &uri_elem) { 33 | return uri_elem.ostream_impl(out); 34 | } 35 | }; 36 | 37 | struct src_uri : public depend::GroupExpr { 38 | using Base = depend::GroupExpr; 39 | }; 40 | 41 | struct restrict_elem { 42 | private: 43 | std::ostream &ostream_impl(std::ostream &out) const; 44 | 45 | public: 46 | std::string string; 47 | enum class Type : std::uint8_t { UNKNOWN, mirror, fetch, strip, userpriv, test }; 48 | Type type = restrict_elem::Type::UNKNOWN; 49 | 50 | [[nodiscard]] explicit operator std::string() const; 51 | friend std::ostream &operator<<(std::ostream &out, const restrict_elem &restrict_elem) { 52 | return restrict_elem.ostream_impl(out); 53 | } 54 | }; 55 | struct restrict : public depend::GroupExpr { 56 | using Base = depend::GroupExpr; 57 | }; 58 | 59 | struct homepage : public depend::GroupExpr { 60 | using Base = depend::GroupExpr; 61 | }; 62 | 63 | struct license_elem : public std::string {}; 64 | struct license : public depend::GroupExpr { 65 | using Base = depend::GroupExpr; 66 | }; 67 | 68 | struct keyword : public std::string {}; 69 | struct keywords : public std::vector { 70 | private: 71 | std::ostream &ostream_impl(std::ostream &out) const; 72 | 73 | public: 74 | [[nodiscard]] explicit operator std::string() const; 75 | friend std::ostream &operator<<(std::ostream &out, const keywords &keywords) { 76 | return keywords.ostream_impl(out); 77 | } 78 | }; 79 | 80 | struct inherited_elem : public std::string {}; 81 | struct inherited : public std::vector { 82 | private: 83 | std::ostream &ostream_impl(std::ostream &out) const; 84 | 85 | public: 86 | [[nodiscard]] explicit operator std::string() const; 87 | friend std::ostream &operator<<(std::ostream &out, const inherited &inherited) { 88 | return inherited.ostream_impl(out); 89 | } 90 | }; 91 | 92 | struct iuse_elem { 93 | private: 94 | std::ostream &ostream_impl(std::ostream &out) const; 95 | 96 | public: 97 | std::optional default_enabled; 98 | atom::Useflag useflag; 99 | 100 | [[nodiscard]] explicit operator std::string() const; 101 | friend std::ostream &operator<<(std::ostream &out, const iuse_elem &iuse_elem) { 102 | return iuse_elem.ostream_impl(out); 103 | } 104 | }; 105 | struct iuse : public std::vector { 106 | private: 107 | std::ostream &ostream_impl(std::ostream &out) const; 108 | 109 | public: 110 | [[nodiscard]] explicit operator std::string() const; 111 | friend std::ostream &operator<<(std::ostream &out, const iuse &iuse) { return iuse.ostream_impl(out); } 112 | }; 113 | 114 | struct required_use : public depend::GroupExpr { 115 | using Base = depend::GroupExpr; 116 | }; 117 | 118 | struct eapi : public std::string {}; 119 | 120 | struct properties_elem { 121 | private: 122 | std::ostream &ostream_impl(std::ostream &out) const; 123 | 124 | public: 125 | std::string string; 126 | enum class Type : std::uint8_t { UNKNOWN, interactive, live, test_network }; 127 | Type type = properties_elem::Type::UNKNOWN; 128 | 129 | [[nodiscard]] explicit operator std::string() const; 130 | friend std::ostream &operator<<(std::ostream &out, const properties_elem &properties_elem) { 131 | return properties_elem.ostream_impl(out); 132 | } 133 | }; 134 | struct properties : public depend::GroupExpr { 135 | using Base = depend::GroupExpr; 136 | }; 137 | 138 | BOOST_DEFINE_FIXED_ENUM_CLASS(phases, std::uint8_t, pretend, setup, unpack, prepare, configure, compile, test, 139 | install, preinst, postinst, prerm, postrm, config, info, nofetch); 140 | struct defined_phases : public std::vector { 141 | private: 142 | std::ostream &ostream_impl(std::ostream &out) const; 143 | 144 | public: 145 | [[nodiscard]] explicit operator std::string() const; 146 | friend std::ostream &operator<<(std::ostream &out, const defined_phases &defined_phases) { 147 | return defined_phases.ostream_impl(out); 148 | } 149 | }; 150 | 151 | struct Metadata { 152 | depend::DependExpr DEPEND; 153 | depend::DependExpr RDEPEND; 154 | atom::Slot SLOT; 155 | src_uri SRC_URI; 156 | restrict RESTRICT; 157 | homepage HOMEPAGE; 158 | license LICENSE; 159 | std::string DESCRIPTION; 160 | keywords KEYWORDS; 161 | inherited INHERITED; 162 | iuse IUSE; 163 | required_use REQUIRED_USE; 164 | depend::DependExpr PDEPEND; 165 | depend::DependExpr BDEPEND; 166 | eapi EAPI; 167 | properties PROPERTIES; 168 | defined_phases DEFINED_PHASES; 169 | depend::DependExpr IDEPEND; 170 | }; 171 | 172 | // BEGIN DESCRIBE 173 | 174 | BOOST_DESCRIBE_STRUCT(URI, (), ()); 175 | 176 | BOOST_DESCRIBE_STRUCT(uri_elem, (), (uri, filename)); 177 | BOOST_DESCRIBE_STRUCT(src_uri, (src_uri::Base), ()); 178 | 179 | BOOST_DESCRIBE_ENUM(restrict_elem::Type, UNKNOWN, mirror, fetch, strip, userpriv, test); 180 | BOOST_DESCRIBE_STRUCT(restrict_elem, (), (string, type)); 181 | BOOST_DESCRIBE_STRUCT(restrict, (restrict ::Base), ()); 182 | 183 | BOOST_DESCRIBE_STRUCT(homepage, (homepage::Base), ()); 184 | 185 | BOOST_DESCRIBE_STRUCT(license_elem, (), ()); 186 | BOOST_DESCRIBE_STRUCT(license, (license::Base), ()); 187 | 188 | BOOST_DESCRIBE_STRUCT(keyword, (), ()); 189 | BOOST_DESCRIBE_STRUCT(keywords, (), ()); 190 | 191 | BOOST_DESCRIBE_STRUCT(inherited_elem, (), ()); 192 | BOOST_DESCRIBE_STRUCT(inherited, (), ()); 193 | 194 | BOOST_DESCRIBE_STRUCT(iuse_elem, (), (default_enabled, useflag)); 195 | BOOST_DESCRIBE_STRUCT(iuse, (), ()); 196 | 197 | BOOST_DESCRIBE_STRUCT(required_use, (required_use::Base), ()); 198 | 199 | BOOST_DESCRIBE_STRUCT(eapi, (), ()); 200 | 201 | BOOST_DESCRIBE_ENUM(properties_elem::Type, UNKNOWN, interactive, live, test_network); 202 | BOOST_DESCRIBE_STRUCT(properties_elem, (), (string, type)); 203 | BOOST_DESCRIBE_STRUCT(properties, (properties::Base), ()); 204 | 205 | BOOST_DESCRIBE_STRUCT(defined_phases, (), ()); 206 | 207 | BOOST_DESCRIBE_STRUCT(Metadata, (), 208 | (DEPEND, RDEPEND, SLOT, SRC_URI, RESTRICT, HOMEPAGE, LICENSE, DESCRIPTION, KEYWORDS, 209 | INHERITED, IUSE, REQUIRED_USE, PDEPEND, BDEPEND, EAPI, PROPERTIES, DEFINED_PHASES, 210 | IDEPEND)); 211 | 212 | namespace meta { 213 | 214 | using all = boost::mp11::mp_list; 218 | 219 | } // namespace meta 220 | 221 | // END DESCRIBE 222 | 223 | // BEGIN IO 224 | 225 | [[nodiscard]] std::string to_string(restrict_elem::Type type); 226 | std::ostream &operator<<(std::ostream &out, restrict_elem::Type type); 227 | 228 | [[nodiscard]] std::string to_string(properties_elem::Type type); 229 | std::ostream &operator<<(std::ostream &out, properties_elem::Type type); 230 | 231 | [[nodiscard]] std::string to_string(phases _phases); 232 | std::ostream &operator<<(std::ostream &out, phases _phases); 233 | 234 | // END IO 235 | 236 | } // namespace ebuild 237 | } // namespace pms_utils 238 | 239 | PMS_UTILS_FOOTER(ebuild); 240 | -------------------------------------------------------------------------------- /include/pms-utils/misc/macro-begin.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PMS_UTILS_MACRO_ACTIVE 2 | #ifndef __clang__ 3 | _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored_attributes \"clang::lifetimebound\"") 4 | #endif 5 | #define PMS_UTILS_MACRO_ACTIVE 6 | #else 7 | #ifndef PMS_UTILS_CLANG_TIDY 8 | _Pragma("GCC error \"macro header already active\"") 9 | #endif 10 | #endif 11 | -------------------------------------------------------------------------------- /include/pms-utils/misc/macro-end.hpp: -------------------------------------------------------------------------------- 1 | #ifdef PMS_UTILS_MACRO_ACTIVE 2 | #ifndef __clang__ 3 | _Pragma("GCC diagnostic pop") 4 | #endif 5 | #undef PMS_UTILS_MACRO_ACTIVE 6 | #else 7 | #ifndef PMS_UTILS_CLANG_TIDY 8 | _Pragma("GCC error \"macro header not active\"") 9 | #endif 10 | #endif 11 | -------------------------------------------------------------------------------- /include/pms-utils/misc/meta.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // IWYU pragma: keep 4 | #include 5 | #include 6 | #include // IWYU pragma: keep 7 | #include // IWYU pragma: keep 8 | #include 9 | #include // IWYU pragma: keep 10 | #include // IWYU pragma: keep 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace pms_utils::meta::_internal { 18 | 19 | template 20 | using is_described = boost::mp11::mp_if, boost::describe::has_describe_members, 21 | boost::describe::has_describe_enumerators>; 22 | 23 | template class Ref> 24 | struct is_specialization : public std::false_type {}; 25 | template