diff --git a/.github/workflows/resolver.yml b/.github/workflows/resolver.yml new file mode 100644 index 0000000..b7acfe3 --- /dev/null +++ b/.github/workflows/resolver.yml @@ -0,0 +1,78 @@ +--- +# SPDX-License-Identifier: LGPL-2.1-or-later +# +name: Resolver compatibility +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + linux-build: + runs-on: ubuntu-latest + name: Linux build and wheel smoke + steps: + - name: Repository checkout + uses: actions/checkout@v4 + + - name: Configure Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install system and Python build dependencies + run: | + sudo apt -y update + sudo apt -y install gcc libsystemd-dev pkg-config + python -m pip install -U pip build + + - name: Build artifacts and wheel + run: | + python -m build --sdist --wheel + python -m pip wheel --use-pep517 --no-deps . + + macos-metadata-lock: + runs-on: macos-14 + name: macOS metadata and lock smoke + steps: + - name: Repository checkout + uses: actions/checkout@v4 + + - name: Configure Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install Python packaging tools + run: python -m pip install -U pip build + + - name: Setup uv + uses: astral-sh/setup-uv@v6 + + - name: sdist build should not require libsystemd + run: python -m build --sdist + + - name: PEP 517 wheel build should not hard-fail without libsystemd + run: python -m pip wheel --use-pep517 --no-deps . + + - name: uv lock smoke for linux marker dependency + run: | + mkdir -p /tmp/uv-lock-smoke + cat > /tmp/uv-lock-smoke/pyproject.toml <<'EOF' + [project] + name = "uv-lock-smoke" + version = "0.0.0" + dependencies = [ + "systemd-python; sys_platform == 'linux'", + ] + + [tool.uv.sources] + systemd-python = { path = "${{ github.workspace }}" } + EOF + cd /tmp/uv-lock-smoke + uv lock + grep -q 'name = "systemd-python"' uv.lock diff --git a/meson.build b/meson.build index 1d8a921..13947c2 100644 --- a/meson.build +++ b/meson.build @@ -15,14 +15,23 @@ python_dep = python.dependency() fs = import('fs') -libsystemd_dep = dependency('libsystemd') +libsystemd_dep = dependency('libsystemd', required: false) +build_extensions = host_machine.system() == 'linux' and libsystemd_dep.found() -add_project_arguments( - '-D_GNU_SOURCE=1', - '-DPACKAGE_VERSION="@0@"'.format(meson.project_version()), - '-DLIBSYSTEMD_VERSION=@0@'.format(libsystemd_dep.version()), - language : 'c', -) +if host_machine.system() != 'linux' + warning('libsystemd bindings are skipped on non-Linux platforms. This build provides package metadata and pure Python files only.') +elif not libsystemd_dep.found() + warning('libsystemd development files were not found; native extension modules will not be built. Install libsystemd dev packages for full functionality.') +endif + +if build_extensions + add_project_arguments( + '-D_GNU_SOURCE=1', + '-DPACKAGE_VERSION="@0@"'.format(meson.project_version()), + '-DLIBSYSTEMD_VERSION=@0@'.format(libsystemd_dep.version()), + language : 'c', + ) +endif update_constants_py = files('update-constants.py') @@ -39,7 +48,9 @@ test( env: { 'PYTHONPATH': meson.current_build_dir() / 'src' }, ) -alias_target('update-constants', update_constants) +if build_extensions + alias_target('update-constants', update_constants) +endif # Docs sphinx_build = find_program('sphinx-build', disabler: true, required: get_option('docs')) diff --git a/src/systemd/daemon.py b/src/systemd/daemon.py index 178680c..c43903b 100644 --- a/src/systemd/daemon.py +++ b/src/systemd/daemon.py @@ -2,18 +2,25 @@ from socket import AF_UNSPEC as _AF_UNSPEC -from ._daemon import (__version__, - booted, - notify, - _listen_fds, - _listen_fds_with_names, - _is_fifo, - _is_socket, - _is_socket_inet, - _is_socket_sockaddr, - _is_socket_unix, - _is_mq, - LISTEN_FDS_START) +try: + from ._daemon import (__version__, + booted, + notify, + _listen_fds, + _listen_fds_with_names, + _is_fifo, + _is_socket, + _is_socket_inet, + _is_socket_sockaddr, + _is_socket_unix, + _is_mq, + LISTEN_FDS_START) +except ModuleNotFoundError as e: + raise ImportError( + "systemd.daemon requires native libsystemd bindings that are not available " + "in this build. Build/install on Linux with libsystemd development files " + "to enable this module." + ) from e def _convert_fileobj(fileobj): try: diff --git a/src/systemd/journal.py b/src/systemd/journal.py index da3404a..7c4b6ea 100644 --- a/src/systemd/journal.py +++ b/src/systemd/journal.py @@ -9,13 +9,20 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG) -from ._journal import __version__, sendv, stream_fd -from ._reader import (_Reader, NOP, APPEND, INVALIDATE, - LOCAL_ONLY, RUNTIME_ONLY, - SYSTEM, SYSTEM_ONLY, CURRENT_USER, - OS_ROOT, - _get_catalog, Monotonic) -from . import id128 as _id128 +try: + from ._journal import __version__, sendv, stream_fd + from ._reader import (_Reader, NOP, APPEND, INVALIDATE, + LOCAL_ONLY, RUNTIME_ONLY, + SYSTEM, SYSTEM_ONLY, CURRENT_USER, + OS_ROOT, + _get_catalog, Monotonic) + from . import id128 as _id128 +except ModuleNotFoundError as e: + raise ImportError( + "systemd.journal requires native libsystemd bindings that are not " + "available in this build. Build/install on Linux with libsystemd " + "development files to enable this module." + ) from e def _convert_monotonic(m): diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 842be36..3bbf2a5 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -1,49 +1,51 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -# Build _journal extension module -python.extension_module( - '_journal', - ['_journal.c', 'pyutil.c'], - dependencies: [libsystemd_dep], - install: true, - subdir: 'systemd', -) +if build_extensions + # Build _journal extension module + python.extension_module( + '_journal', + ['_journal.c', 'pyutil.c'], + dependencies: [libsystemd_dep], + install: true, + subdir: 'systemd', + ) -# Build _reader extension module -python.extension_module( - '_reader', - ['_reader.c', 'pyutil.c', 'strv.c'], - dependencies: [libsystemd_dep], - install: true, - subdir: 'systemd', -) + # Build _reader extension module + python.extension_module( + '_reader', + ['_reader.c', 'pyutil.c', 'strv.c'], + dependencies: [libsystemd_dep], + install: true, + subdir: 'systemd', + ) -# Build _daemon extension module -python.extension_module( - '_daemon', - ['_daemon.c', 'pyutil.c', 'util.c'], - dependencies: [libsystemd_dep], - install: true, - subdir: 'systemd', -) + # Build _daemon extension module + python.extension_module( + '_daemon', + ['_daemon.c', 'pyutil.c', 'util.c'], + dependencies: [libsystemd_dep], + install: true, + subdir: 'systemd', + ) -# Build id128 extension module -python.extension_module( - 'id128', - ['id128.c', 'pyutil.c'], - dependencies: [libsystemd_dep], - install: true, - subdir: 'systemd', -) + # Build id128 extension module + python.extension_module( + 'id128', + ['id128.c', 'pyutil.c'], + dependencies: [libsystemd_dep], + install: true, + subdir: 'systemd', + ) -# Build login extension module -python.extension_module( - 'login', - ['login.c', 'pyutil.c', 'strv.c'], - dependencies: [libsystemd_dep], - install: true, - subdir: 'systemd', -) + # Build login extension module + python.extension_module( + 'login', + ['login.c', 'pyutil.c', 'strv.c'], + dependencies: [libsystemd_dep], + install: true, + subdir: 'systemd', + ) +endif # Install Python modules py_files = files( @@ -67,15 +69,17 @@ python.install_sources( subdir: 'systemd/test', ) -# The 'update-constants' target -include_dir = libsystemd_dep.get_variable(pkgconfig: 'includedir') +if build_extensions + # The 'update-constants' target + include_dir = libsystemd_dep.get_variable(pkgconfig: 'includedir') -update_constants = custom_target( - output: 'update-constants', - command: [ - update_constants_py, - meson.current_source_dir() / 'id128-constants.h', - meson.project_source_root() / 'docs/id128.rst', - meson.current_source_dir() / 'id128-defines.h', - include_dir / 'systemd/sd-messages.h', - ]) + update_constants = custom_target( + output: 'update-constants', + command: [ + update_constants_py, + meson.current_source_dir() / 'id128-constants.h', + meson.project_source_root() / 'docs/id128.rst', + meson.current_source_dir() / 'id128-defines.h', + include_dir / 'systemd/sd-messages.h', + ]) +endif