meson: port python bindings to build natively via meson and meson-python

We get full build parallelism and fewer confusing ancient distutils
paths. The python wheels build fully standalone, including linking
libfdt as a static library.

For convenience, when running pip install a meson option is passed that
prevents building tools or installing headers/pkgconfig files.
meson-python would otherwise include them in the wheel itself, in case
they are needed, but this is essentially a bit useless so don't bother.

The old setuptools-based build is now redundant and goes away.

Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
Message-ID: <20250430152601.43554-3-eschwartz@gentoo.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
main
Eli Schwartz 2025-04-30 11:25:26 -04:00 committed by David Gibson
parent 7ebfcac852
commit ecb21febfd
9 changed files with 73 additions and 213 deletions

View File

@ -1,12 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)

global-exclude *
include README.md
include GPL
include BSD-2-Clause
include setup.py
include pylibfdt/libfdt.i
include libfdt/libfdt.h
include libfdt/fdt.h
include libfdt/libfdt_env.h
include VERSION.txt

View File

@ -31,7 +31,6 @@ BISON = bison
LEX = flex
SWIG = swig
PKG_CONFIG ?= pkg-config
PYTHON ?= python3

INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
@ -47,8 +46,6 @@ INCLUDEDIR = $(PREFIX)/include
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\|msys\).*/\1/')

NO_PYTHON ?= 0

NO_VALGRIND := $(shell $(PKG_CONFIG) --exists valgrind; echo $$?)
ifeq ($(NO_VALGRIND),1)
CPPFLAGS += -DNO_VALGRIND
@ -158,29 +155,6 @@ SCRIPTS = dtdiff

all: $(BIN) libfdt

# We need both Python and swig to build/install pylibfdt.
# This builds the given make ${target} if those deps are found.
check_python_deps = \
if $(PKG_CONFIG) --cflags $(PYTHON) >/dev/null 2>&1; then \
if which swig >/dev/null 2>&1; then \
can_build=yes; \
fi; \
fi; \
if [ "$${can_build}" = "yes" ]; then \
$(MAKE) $${target}; \
else \
echo "\#\# Skipping pylibfdt (install python dev and swig to build)"; \
fi ;

.PHONY: maybe_pylibfdt
maybe_pylibfdt: FORCE
target=pylibfdt; $(check_python_deps)

ifeq ($(NO_PYTHON),0)
all: maybe_pylibfdt
endif


ifneq ($(DEPTARGETS),)
ifneq ($(MAKECMDGOALS),libfdt)
-include $(DTC_OBJS:%.o=%.d)
@ -254,14 +228,6 @@ install-includes:

install: install-bin install-lib install-includes

.PHONY: maybe_install_pylibfdt
maybe_install_pylibfdt: FORCE
target=install_pylibfdt; $(check_python_deps)

ifeq ($(NO_PYTHON),0)
install: maybe_install_pylibfdt
endif

$(VERSION_FILE): Makefile FORCE
$(call filechk,version)

@ -287,16 +253,6 @@ dist:
gzip -9 > ../dtc-$(dtc_version).tar.gz


#
# Rules for pylibfdt
#
PYLIBFDT_dir = pylibfdt

include $(PYLIBFDT_dir)/Makefile.pylibfdt

.PHONY: pylibfdt
pylibfdt: $(PYLIBFDT_dir)/_libfdt.so

#
# Release signing and uploading
# This is for maintainer convenience, don't try this at home.
@ -330,9 +286,6 @@ TESTS_BIN += fdtput
TESTS_BIN += fdtget
TESTS_BIN += fdtdump
TESTS_BIN += fdtoverlay
ifeq ($(NO_PYTHON),0)
TESTS_PYLIBFDT += maybe_pylibfdt
endif

ifneq ($(MAKECMDGOALS),libfdt)
include tests/Makefile.tests
@ -344,7 +297,7 @@ endif
STD_CLEANFILES = *~ *.o *.$(SHAREDLIB_EXT) *.d *.a *.i *.s core a.out vgcore.* \
*.tab.[ch] *.lex.c *.output

clean: libfdt_clean pylibfdt_clean tests_clean
clean: libfdt_clean tests_clean
@$(VECHO) CLEAN
rm -f $(STD_CLEANFILES)
rm -f $(VERSION_FILE)

View File

@ -31,7 +31,7 @@ libfdt = library(
version: meson.project_version(),
link_args: link_args,
link_depends: 'version.lds',
install: true,
install: get_option('default_library') != 'static' or not wheel_only,
)

libfdt_inc = include_directories('.')
@ -41,20 +41,22 @@ libfdt_dep = declare_dependency(
link_with: libfdt,
)

install_headers(
files(
'fdt.h',
'libfdt.h',
'libfdt_env.h',
if not wheel_only
install_headers(
files(
'fdt.h',
'libfdt.h',
'libfdt_env.h',
)
)
)

pkgconfig = import('pkgconfig')
pkgconfig = import('pkgconfig')

pkgconfig.generate(
libraries: libfdt,
version: meson.project_version(),
filebase: 'libfdt',
name: 'libfdt',
description: 'Flat Device Tree manipulation',
)
pkgconfig.generate(
libraries: libfdt,
version: meson.project_version(),
filebase: 'libfdt',
name: 'libfdt',
description: 'Flat Device Tree manipulation',
)
endif

View File

@ -43,6 +43,7 @@ py = import('python')
py = py.find_installation(required: get_option('python'))
swig = find_program('swig', required: get_option('python'))
pylibfdt_enabled = not meson.is_cross_build() and py.found() and swig.found() ? true : false
wheel_only = get_option('wheel-only')

version_gen_h = vcs_tag(
command: ['git', 'describe', '--dirty=+'],
@ -59,7 +60,7 @@ util_dep = declare_dependency(
dependencies: libfdt_dep
)

if get_option('tools')
if get_option('tools') and not wheel_only
flex = find_program('flex', required: true)
bison = find_program('bison', required: true)


View File

@ -10,3 +10,5 @@ option('python', type: 'feature', value: 'auto',
description: 'Build pylibfdt Python library')
option('tests', type: 'boolean', value: true,
description: 'Build tests')
option('wheel-only', type: 'boolean', value: false,
description: 'building from meson-python')

View File

@ -1,30 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
# Makefile.pylibfdt
#

PYLIBFDT_srcs = $(PYLIBFDT_dir)/libfdt.i
PYMODULE = $(PYLIBFDT_dir)/_libfdt.so
PYLIBFDT_CLEANFILES_L = libfdt_wrap.c libfdt.py *.pyc *.so
PYLIBFDT_CLEANFILES = $(PYLIBFDT_CLEANFILES_L:%=$(PYLIBFDT_dir)/%)
PYLIBFDT_CLEANDIRS_L = __pycache__ libfdt.egg-info
PYLIBFDT_CLEANDIRS = build $(PYLIBFDT_CLEANDIRS_L:%=$(PYLIBFDT_dir)/%)

SETUP = ./setup.py

ifndef V
SETUPFLAGS += --quiet
endif

$(PYMODULE): WARNINGS = # suppress warnings from generated code
$(PYMODULE): $(PYLIBFDT_srcs) $(LIBFDT_archive) $(SETUP)
@$(VECHO) PYMOD $@
CFLAGS="$(CFLAGS) -Wno-error" $(PYTHON) $(SETUP) $(SETUPFLAGS) build_ext

install_pylibfdt: $(PYMODULE)
@$(VECHO) INSTALL-PYLIB
$(PYTHON) $(SETUP) $(SETUPFLAGS) install --prefix=$(PREFIX)

pylibfdt_clean:
@$(VECHO) CLEAN "(pylibfdt)"
rm -f $(PYLIBFDT_CLEANFILES)
rm -rf $(PYLIBFDT_CLEANDIRS)

View File

@ -1,13 +1,21 @@
setup_py = find_program('../setup.py')
setup_py = [setup_py, '--quiet', '--top-builddir', meson.project_build_root()]

pylibfdt = custom_target(
'pylibfdt',
libfdt_c = custom_target(
'swig',
input: 'libfdt.i',
depends: libfdt,
output: '_libfdt.so',
command: [setup_py, 'build_ext'],
build_by_default: true,
output: ['libfdt.c', 'libfdt.py'],
install: true,
install_dir: [false, py.get_install_dir(pure: false)],
command: [swig, '-python', '-I'+meson.current_source_dir() / '../libfdt', '-o', '@OUTPUT0@', '@INPUT@']
)

meson.add_install_script(setup_py, 'install', '--prefix=' + get_option('prefix'), '--root=$DESTDIR')
nowarn_gen = cc.get_supported_arguments(
'-Wno-cast-qual',
'-Wno-missing-prototypes',
'-Wno-redundant-decls',
)
pylibfdt = py.extension_module(
'_libfdt',
libfdt_c,
c_args: ['-DPY_SSIZE_T_CLEAN'] + nowarn_gen,
dependencies: [libfdt_dep, py.dependency()],
install: true,
)

33
pyproject.toml Normal file
View File

@ -0,0 +1,33 @@
[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']

[project]
name = 'libfdt'
authors = [
{name = 'Simon Glass', email = 'sjg@chromium.org'},
]
classifiers = [
'Programming Language :: Python :: 3',
'License :: OSI Approved :: BSD License',
'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)',
'Operating System :: OS Independent',
]
description = 'Python binding for libfdt'
readme = 'README.md'
requires-python = '>=3.8'
dynamic = ['version']

[project.urls]
'homepage' = 'https://git.kernel.org/pub/scm/utils/dtc/dtc.git'

# These arguments are applied only when building a redistributable binary wheel
# for uploading to PyPI. We don't want to install libraries (or headers /
# pkgconfig files / executables) that clash with system C installs, so we
# disable everything other than the python bindings themselves, and build the
# python C-API extension using static linkage to avoid juggling "libdir" /
# LD_LIBRARY_PATH / RPATH around. When building both the C library and the
# python bindings for a distro, `meson setup` will still default to shared
# libraries.
[tool.meson-python.args]
setup = ['-Ddefault_library=static', '-Dwheel-only=true']

View File

@ -1,97 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)

"""
setup.py file for SWIG libfdt
Copyright (C) 2017 Google, Inc.
Written by Simon Glass <sjg@chromium.org>
"""

import os
import sys

from setuptools import setup, Extension
from setuptools.command.build_py import build_py as _build_py


def scan_for_info(srcdir):
"""Scan for the version and long_description fields

Args:
srcdir (str): Source-directory path

Returns: tuple
str: Full description (contents of README.md)
str: Version string
"""
with open(os.path.join(srcdir, "VERSION.txt"), "r", encoding='utf-8') as fh:
version = fh.readline().strip()

with open(os.path.join(srcdir, "README.md"), "r", encoding='utf-8') as fh:
long_description = fh.read()

return version, long_description


def get_top_builddir(srcdir):
"""Figure out the top-level directory containing the source code

Args:
srcdir (str): Source-directory path

Returns:
str: Directory to build in
"""
if '--top-builddir' in sys.argv:
index = sys.argv.index('--top-builddir')
sys.argv.pop(index)
return sys.argv.pop(index)
return srcdir


class BuildPy(_build_py):
"""Small class to run the build_ext command"""
def run(self):
self.run_command("build_ext")
return super().run()


srcdir = os.path.dirname(__file__)
version, long_description = scan_for_info(srcdir)

libfdt_module = Extension(
'_libfdt',
sources=[os.path.join(srcdir, 'pylibfdt/libfdt.i')],
define_macros=[('PY_SSIZE_T_CLEAN', None)],
include_dirs=[os.path.join(srcdir, 'libfdt')],
libraries=['fdt'],
library_dirs=[os.path.join(get_top_builddir(srcdir), 'libfdt')],
swig_opts=['-I' + os.path.join(srcdir, 'libfdt')],
)


setup(
name='libfdt',
version=version,
cmdclass = {'build_py' : BuildPy},
author='Simon Glass',
author_email='sjg@chromium.org',
description='Python binding for libfdt',
ext_modules=[libfdt_module],
package_dir={'': os.path.join(srcdir, 'pylibfdt')},
py_modules=['libfdt'],
python_requires=">=3.8",

long_description=long_description,
long_description_content_type="text/plain",
url="https://git.kernel.org/pub/scm/utils/dtc/dtc.git",
license="BSD",
license_files=["GPL", "BSD-2-Clause"],

classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: BSD License",
"License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)",
"Operating System :: OS Independent",
],
)