blob: 5740e135b57be56efa1d129b87f7b390de9cf2b9 [file] [log] [blame]
project(
'kmod',
'c',
version : '34',
license : ['LGPLv2.1', 'GPL-2.0-or-later'],
meson_version : '>=0.61.0',
default_options : [
'c_std=gnu11',
'b_pie=true',
'warning_level=2',
'prefix=/usr',
'sysconfdir=/etc',
]
)
cdata = configuration_data()
cdata.set_quoted('PACKAGE', meson.project_name())
cdata.set_quoted('VERSION', meson.project_version())
cdata.set10('ENABLE_LOGGING', get_option('logging'))
cdata.set10('ENABLE_DEBUG', get_option('debug-messages'))
cdata.set10('ENABLE_ELFDBG', false)
pkg = import('pkgconfig')
cc = meson.get_compiler('c')
# We rely on the glibc variant of basename, et al.
cdata.set10('_GNU_SOURCE', true)
################################################################################
# Function and structure checks
################################################################################
_funcs = [
'open64', 'stat64', 'fopen64', '__stat64_time64',
'secure_getenv',
]
foreach func : _funcs
cdata.set10('HAVE_@0@'.format(func.to_upper()), cc.has_function(func, args : '-D_GNU_SOURCE'))
endforeach
# Meson has some amount of support for finding builtins by passing the symbol
# name without the "__builtin_" prefix to cc.has_function(). In practice, it
# doesn't seem to detect everything we need.
_builtins = [
['__builtin_clz', '0', true],
['__builtin_types_compatible_p', 'int, int', true],
['__builtin_uadd_overflow', '0U, 0U, (void*)0', false],
['__builtin_uaddl_overflow', '0UL, 0UL, (void*)0', false],
['__builtin_uaddll_overflow', '0ULL, 0ULL, (void*)0', false],
['__builtin_umul_overflow', '0U, 0U, (void*)0', false],
['__builtin_umull_overflow', '0UL, 0UL, (void*)0', false],
['__builtin_umulll_overflow', '0ULL, 0ULL, (void*)0', false],
]
foreach tuple : _builtins
builtin = tuple[0]
args = tuple[1]
required = tuple[2]
# XXX: meson 1.5.0 has links(... required ) flag
have = cc.links('int main(void){@0@(@1@);return 0;}'.format(builtin, args))
if required and not have
error('required builtin function not found: @0@'.format(builtin))
endif
cdata.set10('HAVE_@0@'.format(builtin.to_upper()), have)
endforeach
# basename may be only available in libgen.h with the POSIX behavior,
# not desired here
_decls = [
['basename', 'string.h'],
['__xstat', 'sys/stat.h'],
]
foreach tuple : _decls
decl = tuple[0]
header = tuple[1]
have = cc.has_header_symbol(header, decl, args : '-D_GNU_SOURCE')
cdata.set10('HAVE_DECL_@0@'.format(decl.to_upper()), have)
endforeach
cdata.set10('HAVE_STATIC_ASSERT', cc.compiles('_Static_assert(1, "Test");', name : '_Static_assert'))
cdata.set10('HAVE_NORETURN', cc.compiles('#include <stdlib.h>; _Noreturn int foo(void) { exit(0); }', name : '_Noreturn'))
################################################################################
# Default CFLAGS and LDFLAGS
################################################################################
add_project_arguments(
cc.get_supported_arguments([
'-fdata-sections',
'-fdiagnostics-show-option',
'-ffunction-sections',
'-fno-common',
'-Wchar-subscripts',
'-Wendif-labels',
'-Wfloat-equal',
'-Wformat=2',
'-Wformat-nonliteral',
'-Wformat-security',
'-Winit-self',
'-Wlogical-op',
'-Wmissing-declarations',
'-Wmissing-include-dirs',
'-Wmissing-noreturn',
'-Wmissing-prototypes',
'-Wnested-externs',
'-Wno-attributes',
'-Wno-declaration-after-statement',
'-Wno-unused-parameter',
'-Wold-style-definition',
'-Wpointer-arith',
'-Wredundant-decls',
'-Wshadow',
'-Wsign-compare',
'-Wstrict-aliasing=3',
'-Wstrict-prototypes',
'-Wtype-limits',
'-Wundef',
'-Wuninitialized',
'-Wvla',
'-Wwrite-strings',
]),
language : 'c'
)
add_project_link_arguments(
cc.get_supported_link_arguments([
'-Wl,--gc-sections',
]),
language : 'c'
)
# Clang as of v18, relies on statically linking the sanitizers. This causes two
# distinct issues:
# - the shared library is underlinked, so the build fails
# - the modules (that we dlopen/ld_preload) are underlinked so the tests fail
#
# Force shared libasan (GCC defaults to shared and this toggle doesn't exist),
# which combined with the LD_PRELOAD in our wrapper makes everything happy.
if get_option('b_sanitize') != 'none' and cc.get_id() == 'clang'
add_project_arguments('-shared-libasan', language : 'c')
add_project_link_arguments('-shared-libasan', language : 'c')
endif
################################################################################
# Options
################################################################################
module_compressions = ''
module_signatures = ''
features = []
dep_map = {}
# keep in sync with meson_options.txt
dlopen_all = get_option('dlopen').contains('all')
#-------------------------------------------------------------------------------
# Directories
#-------------------------------------------------------------------------------
prefixdir = get_option('prefix')
sysconfdir = get_option('sysconfdir')
bindir = prefixdir / get_option('bindir')
sbindir = prefixdir / get_option('sbindir')
includedir = prefixdir / get_option('includedir')
libdir = prefixdir / get_option('libdir')
datadir = prefixdir / get_option('datadir')
distconfdir = get_option('distconfdir')
moduledir = get_option('moduledir')
bashcompletiondir = get_option('bashcompletiondir')
fishcompletiondir = get_option('fishcompletiondir')
zshcompletiondir = get_option('zshcompletiondir')
cdata.set_quoted('SYSCONFDIR', sysconfdir)
_customdirs = [
# The defaults are hard-coded due to historical reasons
['distconfdir', prefixdir / 'lib', 'DISTCONFDIR'],
['moduledir', '/lib/modules', 'MODULE_DIRECTORY'],
]
foreach tuple : _customdirs
dir_option = tuple[0]
def_path = tuple[1]
quoted = tuple[2]
customdir = get_variable(dir_option)
if customdir == ''
customdir = def_path
else
if not customdir.startswith('/')
error('User provided @0@, \'@1@\' is not an absolute path.'
.format(dir_option, customdir))
endif
# Strip all leading/trailing and re-add only the leading one.
customdir = '/' / customdir.strip('/')
endif
cdata.set_quoted(quoted, customdir)
set_variable(dir_option, customdir)
endforeach
foreach confdir : [sysconfdir, distconfdir]
install_emptydir(confdir / 'depmod.d')
install_emptydir(confdir / 'modprobe.d')
endforeach
_completiondirs = [
['bash', 'bash-completion', 'bash-completion' / 'completions', '@0@'],
['fish', 'fish', 'fish' / 'vendor_functions.d', '@0@.fish'],
['zsh', '', 'zsh' / 'site-functions', '_@0@'],
]
foreach tuple : _completiondirs
dir_option = tuple[0] + 'completiondir'
pkg_dep = tuple[1]
def_path = tuple[2]
ins_name = tuple[3]
completiondir = get_variable(dir_option)
if completiondir == ''
completion = dependency(pkg_dep, required : false)
if completion.found()
completion_prefix = completion.get_variable(pkgconfig : 'prefix')
if completion_prefix != prefixdir
warning('User provided prefix \'@0@\' differs from @1@ one \'@2@\'.'
.format(prefixdir, pkg_dep, completion_prefix))
warning('Not installing completion. To re-enable, manually set @0@.'
.format(dir_option))
completiondir = 'no'
else
completiondir = completion.get_variable(pkgconfig : 'completionsdir')
endif
else
completiondir = datadir / def_path
endif
endif
_completions = [
'insmod',
'lsmod',
'rmmod',
]
if completiondir != 'no'
foreach comp : _completions
install_data(
files('shell-completion' / tuple[0] / ins_name.format(comp)),
install_dir : completiondir,
)
endforeach
endif
set_variable(dir_option, completiondir)
endforeach
if bashcompletiondir != 'no'
install_data(
files('shell-completion/bash/kmod'),
install_dir : bashcompletiondir,
)
endif
#-------------------------------------------------------------------------------
# Compression support
#-------------------------------------------------------------------------------
_compression = [
['zstd', 'libzstd', '>= 1.4.4'],
['xz', 'liblzma', '>= 4.99'],
['zlib', 'zlib', '>= 0'],
]
foreach tuple : _compression
opt = tuple[0]
pkg_dep = tuple[1]
pkg_dep_version = tuple[2]
dlopen = dlopen_all or get_option('dlopen').contains(opt)
if not dlopen_all and dlopen and get_option(opt).disabled()
error('Incompatiable options: dlopen=@0@ for disabled @0@'.format(opt))
endif
dep = dependency(pkg_dep, version : pkg_dep_version, required : get_option(opt))
have = dep.found()
if have and dlopen
dep = dep.partial_dependency(compile_args : true, includes : true)
endif
cdata.set10('ENABLE_' + opt.to_upper(), have)
cdata.set10('ENABLE_' + opt.to_upper() + '_DLOPEN', have and dlopen)
if have
module_compressions += '@0@ '.format(opt)
endif
features += ['@0@@1@'.format(have ? '+' : '-', opt.to_upper())]
dep_map += {opt : dep}
endforeach
#-------------------------------------------------------------------------------
# Signed modules
#-------------------------------------------------------------------------------
opt = 'openssl'
dep = dependency('libcrypto', version : '>= 1.1.0', required : get_option(opt))
have = dep.found()
if have
module_signatures = 'PKCS7 legacy'
else
module_signatures = 'legacy'
endif
cdata.set10('ENABLE_' + opt.to_upper(), have)
features += ['@0@@1@'.format(have ? '+' : '-', opt.to_upper())]
dep_map += {opt : dep}
#-------------------------------------------------------------------------------
# Config output
#-------------------------------------------------------------------------------
cdata.set_quoted('KMOD_FEATURES', ' '.join(features))
config_h = configure_file(
output : 'config.h',
configuration : cdata
)
add_project_arguments('-include', 'config.h', language : 'c')
################################################################################
# libraries and binaries
################################################################################
libshared = static_library(
'shared',
files(
'shared/array.c',
'shared/array.h',
'shared/elf-note.h',
'shared/hash.c',
'shared/hash.h',
'shared/macro.h',
'shared/missing.h',
'shared/strbuf.c',
'shared/strbuf.h',
'shared/util.c',
'shared/util.h',
'shared/tmpfile-util.c',
'shared/tmpfile-util.h',
),
gnu_symbol_visibility : 'hidden',
install : false,
)
libkmod_files = files(
'libkmod/libkmod-builtin.c',
'libkmod/libkmod.c',
'libkmod/libkmod-config.c',
'libkmod/libkmod-elf.c',
'libkmod/libkmod-file.c',
'libkmod/libkmod.h',
'libkmod/libkmod-index.c',
'libkmod/libkmod-index.h',
'libkmod/libkmod-internal-file.h',
'libkmod/libkmod-internal.h',
'libkmod/libkmod-list.c',
'libkmod/libkmod-module.c',
'libkmod/libkmod-signature.c',
)
libkmod_deps = []
cdeps = []
if not cc.has_function('dlopen')
cdeps += cc.find_library('dl', required : true)
endif
if dep_map.get('zstd').found()
libkmod_files += files('libkmod/libkmod-file-zstd.c')
libkmod_deps += dep_map['zstd']
endif
if dep_map.get('xz').found()
libkmod_files += files('libkmod/libkmod-file-xz.c')
libkmod_deps += dep_map['xz']
endif
if dep_map.get('zlib').found()
libkmod_files += files('libkmod/libkmod-file-zlib.c')
libkmod_deps += dep_map['zlib']
endif
if dep_map.get('openssl').found()
libkmod_deps += dep_map['openssl']
endif
install_headers('libkmod/libkmod.h')
libkmod = shared_library(
'kmod',
libkmod_files,
dependencies : libkmod_deps + cdeps,
link_with : libshared,
link_args : ['-Wl,--version-script', meson.current_source_dir() /
'libkmod/libkmod.sym'],
link_depends : files('libkmod/libkmod.sym'),
gnu_symbol_visibility : 'hidden',
version : '2.5.1',
install : true,
)
pkg.generate(
name : 'libkmod',
description : 'Library to deal with kernel modules',
libraries : libkmod,
requires_private : libkmod_deps,
libraries_private : cdeps,
)
libkmod_internal = static_library(
'kmod-internal',
objects : libkmod.extract_all_objects(recursive : true),
dependencies : libkmod_deps + cdeps,
install : false,
)
kmod_sources = files(
'tools/depmod.c',
'tools/insmod.c',
'tools/kmod.c',
'tools/kmod.h',
'tools/log.c',
'tools/log.h',
'tools/lsmod.c',
'tools/modinfo.c',
'tools/modprobe.c',
'tools/opt.c',
'tools/opt.h',
'tools/rmmod.c',
'tools/static-nodes.c',
)
kmod = executable(
'kmod',
kmod_sources,
link_with : [libshared, libkmod_internal],
gnu_symbol_visibility : 'hidden',
build_by_default : get_option('tools'),
install : get_option('tools'),
)
_kmod_variables = [
'sysconfdir=' + sysconfdir,
'distconfdir=' + distconfdir,
'module_directory=' + moduledir,
]
# Don't (space) escape variables with space-separated lists, for consistency
# with the autotools build.
_kmod_unescaped_variables = [
'module_signatures=' + module_signatures,
]
# XXX: Support for empty variables was added in meson v1.4.0.
# pkgconf behaves identically on missing and empty variable.
if module_compressions != ''
_kmod_unescaped_variables += ['module_compressions=' + module_compressions]
endif
pkg.generate(
name : 'kmod',
description : 'Tools to deal with kernel modules',
install_dir : datadir / 'pkgconfig',
unescaped_variables : _kmod_unescaped_variables,
variables : _kmod_variables,
)
_tools = [
'depmod',
'insmod',
'lsmod',
'modinfo',
'modprobe',
'rmmod',
]
if get_option('tools')
mkdir_p = 'mkdir -p "$DESTDIR@0@"'
meson.add_install_script('sh', '-c', mkdir_p.format(sbindir))
ln_s = 'ln -sfr "$DESTDIR@0@/kmod" "$DESTDIR@1@"'
foreach tool : _tools
meson.add_install_script('sh', '-c', ln_s.format(bindir, sbindir / tool))
endforeach
endif
internal_kmod_symlinks = []
foreach tool : _tools
internal_kmod_symlinks += custom_target(
tool,
command : ['ln', '-sf', kmod, '@OUTPUT@'],
output : tool,
depends : kmod,
build_by_default : true,
)
endforeach
# ------------------------------------------------------------------------------
# TESTSUITE
# ------------------------------------------------------------------------------
if get_option('build-tests')
bash = find_program('bash')
sanitizer_env = find_program('scripts/sanitizer-env.sh')
setup_modules = find_program('scripts/setup-modules.sh')
setup_rootfs = find_program('scripts/setup-rootfs.sh')
top_include = include_directories('.')
subdir('testsuite')
endif
# ------------------------------------------------------------------
# documentation
# ------------------------------------------------------------------
if get_option('manpages')
build_scdoc = find_program('scripts/build-scdoc.sh')
subdir('man')
endif
if get_option('docs')
test_gtkdoc = find_program('scripts/test-gtkdoc.sh')
subdir('libkmod/docs')
endif
# ------------------------------------------------------------------
# summary
# ------------------------------------------------------------------
summary({
'prefix' : prefixdir,
'sysconfdir' : sysconfdir,
'bindir' : bindir,
'sbindir' : sbindir,
'includedir' : includedir,
'libdir' : libdir,
'datadir' : datadir,
}, section : 'Directories')
summary({
'distconfdir' : distconfdir,
'moduledir' : moduledir,
}, section : 'Kmod specific')
summary({
'bashcompletiondir' : bashcompletiondir,
'fishcompletiondir' : fishcompletiondir,
'zshcompletiondir' : zshcompletiondir,
}, section : 'Shell completions')
summary({
'tools' : get_option('tools'),
'logging' : get_option('logging'),
'debug-messages' : get_option('debug-messages'),
'build-tests' : get_option('build-tests'),
'manpages' : get_option('manpages'),
'docs' : get_option('docs'),
'dlopen' : get_option('dlopen'),
}, section : 'Options')
summary({
'features' : ' '.join(features)
}, section : '')
summary({
'Compiler' : cc.get_id() + ' (' + cc.version() + ')',
'Linker' : cc.get_linker_id(),
}, section : 'Build tools')