/
githubmirror
/
nvme-cli
Обзор
Документация
Войти
/
githubmirror
/
nvme-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
meson.build
867 строк
25 KB
Daniel Wagner
tests/e2e: make test configuration flexible
10 авг 2026, 17:30
10 авг 2026, 17:30
10c5590
Код
Авторство
О чём код?
# SPDX-License-Identifier: GPL-2.0-or-later ################################################################################ # The "version" must follow this syntax: # Official releases: # major.minor[.patch] e.g. 3.0, 3.0.1 # Release Candidates: # major.minor[.patch]-rc.* e.g. 3.0-rc.1, 3.0.1-rc.1 # # Development versions: # alpha: major.minor[.patch]-a.* e.g. 3.0-a.1, 3.0.1-a.1 # beta: major.minor[.patch]-b.* e.g. 3.0-b.1, 3.0.1-b.1 project( 'nvme-cli', ['c'], meson_version: '>= 0.62.0', license: [ 'GPL-2.0-only', # nvme-cli 'LGPL-2.1-or-later', # libnvme ], version: '3.0-b.5', default_options: [ 'c_std=gnu99', 'buildtype=debugoptimized', 'warning_level=1', 'sysconfdir=etc', 'wrap_mode=nofallback', ], ) fs = import('fs') cc = meson.get_compiler('c') cxx_available = add_languages('cpp', required: false, native: false) host_system = host_machine.system() ################################################################################ # Determine which features we need to build: The nvme executable, the libnvme # library, and/or the python bindings. These are controlled by the following # meson options (see meson_options.txt) # # -Dnvme=[auto|enabled|disabled] Default: enabled # -Dlibnvme=[auto|enabled|disabled] Default: enabled # -Dpython=[auto|enabled|disabled] Default: auto # # -Dnvme=auto is equivalent to -Dnvme=enabled # -Dlibnvme=auto is equivalent to -Dlibnvme=enabled # # -Dpython=auto means build the python bindings if all the required # dependencies are present. Also, -Dpython=enabled forces -Dlibnvme=enabled. want_nvme = get_option('nvme').disabled() == false want_libnvme = get_option('libnvme').disabled() == false want_fabrics = get_option('fabrics').disabled() == false and host_system == 'linux' want_mi = get_option('mi').disabled() == false and host_system == 'linux' want_top = get_option('top').disabled() == false and host_system == 'linux' want_discoverd = get_option('nvme-discoverd').disabled() == false and want_fabrics and host_system == 'linux' want_nvmf_autoconnect = get_option('nvmf-autoconnect').disabled() == false want_deprecated_cmds = get_option('deprecated-cmds').disabled() == false want_json_c = get_option('json-c').disabled() == false want_libkmod = get_option('libkmod').disabled() == false and host_system == 'linux' want_tests = get_option('tests') want_examples = get_option('examples') and host_system == 'linux' want_docs = get_option('docs') want_docs_build = get_option('docs-build') is_static = get_option('default_library') == 'static' feature_python = get_option('python') if not want_fabrics or feature_python.disabled() py3_dep = dependency('', required: false) # Needed for muon want_python = false else if feature_python.enabled() want_libnvme = true endif python3 = import('python').find_installation( 'python3', required: feature_python.enabled() ) if python3.found() py3_dep = python3.dependency(required: feature_python.enabled()) swig = find_program('swig', required: feature_python.enabled()) header_found = cc.has_header( 'Python.h', dependencies: py3_dep, required: feature_python.enabled() ) else py3_dep = dependency('', required: false) swig = disabler() header_found = false endif want_python = ( want_libnvme and py3_dep.found() and swig.found() and header_found ) endif # Bump this only when making an ABI-incompatible change. libnvme_so_version = '1.0.0' # For PyPI builds, use only the major version (e.g., '3' instead of '3.0.0'). # This avoids the need for symbolic links which pip/wheel doesn't preserve. if get_option('pypi') libnvme_so_version = libnvme_so_version.split('.')[0] endif libnvme_so_major = libnvme_so_version.split('.')[0] libnvme_api_name = 'libnvme3' libnvme_link_name = 'nvme3' ################################################################################ prefixdir = get_option('prefix') datadir = join_paths(prefixdir, get_option('datadir')) mandir = join_paths(prefixdir, get_option('mandir')) sbindir = join_paths(prefixdir, get_option('sbindir')) sysconfdir = join_paths(prefixdir, get_option('sysconfdir')) udevrulesdir = join_paths(prefixdir, get_option('udevrulesdir')) dracutrulesdir = join_paths(prefixdir, get_option('dracutrulesdir')) systemddir = join_paths(prefixdir, get_option('systemddir')) nmdispatchdir = join_paths(prefixdir, get_option('nmdispatchdir')) # /run's location is fixed by the FHS -- unlike the dirs above, it must # never scale with --prefix. rundir = get_option('rundir') std_prefix = prefixdir == '/usr' or prefixdir == '/usr/local' ############################################################################### conf = configuration_data() requires = '' version_tag = get_option('version-tag') if version_tag != '' git_version = version_tag else git_version = meson.project_version() git = find_program('git', required: false) if git.found() and fs.exists(meson.current_source_dir() / '.git') r = run_command( git, '-C', meson.current_source_dir(), 'describe', '--abbrev=7', '--dirty=+', check: false, ) if r.returncode() == 0 git_version = r.stdout().strip().replace('^v', '') endif endif endif conf.set('GIT_VERSION', '"@0@"'.format(git_version)) conf.set('SYSCONFDIR', '"@0@"'.format(sysconfdir)) conf.set('RUNDIR', '"@0@"'.format(rundir)) conf.set('CONFIG_FABRICS', want_fabrics, description: 'Is fabrics enabled') conf.set('CONFIG_MI', want_mi, description: 'Is MI enabled') conf.set('CONFIG_TOP', want_top, description: 'Is nvme top enabled') conf.set('CONFIG_DEPRECATED_CMDS', want_deprecated_cmds, description: 'Deprecated commands enabled') if host_system == 'windows' kernel32_dep = cc.find_library('kernel32', required: true) bcrypt_dep = cc.find_library('bcrypt', required: true) setupapi_dep = cc.find_library('setupapi', required: true) cfgmgr32_dep = cc.find_library('cfgmgr32', required: true) winpthread_dep = cc.find_library('winpthread', required: true) endif conf.set10( 'NVME_HAVE_BCRYPT', cc.find_library('bcrypt', required: false).found(), description: 'Is bcrypt availabe' ) # Check for libjson-c availability if not want_json_c json_c_dep = dependency('', required: false) else json_c_dep = dependency( 'json-c', required: get_option('json-c'), version: '>=0.13', fallback : ['json-c', 'json_c_dep'], ) if json_c_dep.version().version_compare('>=0.14') conf.set('CONFIG_JSONC_14', true, description: 'Is json-c at least 0.14?') requires = 'Requires: json-c >= 0.14' else requires = 'Requires: json-c >= 0.13' endif endif conf.set('CONFIG_JSONC', json_c_dep.found(), description: 'Is json-c available?') conf.set('NVME_VERSION', '"@0@"'.format(meson.project_version())) conf.set('LIBNVME_VERSION', '"@0@"'.format(meson.project_version())) # local (cross-compilable) implementations of ccan configure steps conf.set10( 'NVME_HAVE_BUILTIN_TYPES_COMPATIBLE_P', cc.compiles( '''int main(void) { return __builtin_types_compatible_p(int, long); } ''', name: '__builtin_type_compatible_p' ), description: 'Is __builtin_types_compatible_p available?' ) conf.set10( 'HAVE_TYPEOF', cc.compiles( '''int main(void) { int a = 1; typeof(a) b; b = a; } ''', name: 'typeof' ), description: 'Is typeof available?' ) conf.set10( 'NVME_HAVE_BYTESWAP_H', cc.compiles( '''#include <byteswap.h>''', name: 'byteswap.h' ), description: 'Is byteswap.h include-able?' ) conf.set10( 'NVME_HAVE_BSWAP_64', cc.links( '''#include <byteswap.h> int main(void) { return bswap_64(0); } ''', name: 'bswap64' ), description: 'Is bswap_64 available?' ) conf.set10( 'HAVE_LITTLE_ENDIAN', host_machine.endian() == 'little', description: 'Building for little-endian' ) conf.set10( 'HAVE_BIG_ENDIAN', host_machine.endian() == 'big', description: 'Building for big-endian' ) conf.set10( 'NVME_HAVE_ISBLANK', cc.links( '''#include <ctype.h> int main(int argc, char **argv) { return isblank(argv[0][0]); } ''', name: 'isblank' ), description: 'Is isblank() available?' ) conf.set10( 'NVME_HAVE_SYS_RANDOM', cc.compiles( '''#include <sys/random.h>''', name: 'sys/random.h' ), description: 'Is sys/random.h(getrandom) include-able?' ) conf.set10( 'HAVE_ATTRIBUTE_UNUSED', cc.get_id() == 'clang', description: 'Is compiler warning about unused static line function?' ) conf.set10( 'NVME_HAVE_SED_OPAL', cc.compiles( '''#include <linux/sed-opal.h>''', name: 'linux/sed-opal.h' ), description: 'Is linux/sed-opal.h include-able?' ) conf.set10( 'NVME_HAVE_KEY_TYPE', cc.compiles( ''' #include <linux/sed-opal.h> int main(void) { struct opal_key key; key.key_type = OPAL_INCLUDED; } ''', name: 'key_type' ), description: 'Does struct opal_key have a key_type field?' ) conf.set( 'NVME_HAVE_TM_GMTOFF', cc.compiles( ''' #include <time.h> int main(void) { struct tm tm; tm.tm_gmtoff = 1; } ''', name: 'tm_gmtoff' ), description: 'Does struct tm have a tm_gmtoff field?' ) conf.set('HAVE_STRSEP', cc.compiles( ''' #include <string.h> int main(void) { char *s = NULL; char *p = strsep(&s, ","); return p != NULL; } ''', name: 'strsep' ), description: 'Is strsep available?' ) if cc.has_function_attribute('fallthrough') conf.set('fallthrough', '__attribute__((fallthrough))') else conf.set('fallthrough', 'do {} while (0) /* fallthrough */') endif if not want_fabrics or get_option('liburing').disabled() liburing_dep = dependency('', required: false) else liburing_dep = dependency('liburing', version: '>=2.2', required: get_option('liburing')) endif conf.set('CONFIG_LIBURING', liburing_dep.found(), description: 'Is liburing available?') if not want_libkmod libkmod_dep = dependency('', required: false) else libkmod_dep = dependency('libkmod', required: get_option('libkmod')) endif conf.set('NVME_HAVE_LIBKMOD', libkmod_dep.found(), description: 'Is libkmod available?') if host_system != 'linux' or get_option('openssl').disabled() openssl_dep = dependency('', required: false) else openssl_dep = dependency( 'openssl', version: '>=3.0.0', required: get_option('openssl'), fallback : ['openssl', 'libssl_dep'], ) endif if openssl_dep.found() # Test for LibreSSL v3.x with incomplete OpenSSL v3 APIs if openssl_dep.type_name() != 'internal' is_libressl = cc.has_header_symbol( 'openssl/opensslv.h', 'LIBRESSL_VERSION_NUMBER', dependencies: openssl_dep, ) has_header = cc.has_header( 'openssl/core_names.h', dependencies: openssl_dep, ) if is_libressl and not has_header openssl_dep = dependency('', required: false) endif endif endif conf.set('CONFIG_OPENSSL', openssl_dep.found(), description: 'Is OpenSSL/LibreSSL available?') if not want_fabrics or get_option('keyutils').disabled() keyutils_dep = dependency('', required: false) else keyutils_dep = dependency('libkeyutils', required : get_option('keyutils')) endif conf.set('CONFIG_KEYUTILS', keyutils_dep.found(), description: 'Is libkeyutils available?') if not want_discoverd libsystemd_dep = dependency('', required: false) else # v253 for Type=notify-reload (nvme-discoverd.service). libsystemd_dep = dependency('libsystemd', version: '>=253', required: get_option('nvme-discoverd')) endif want_discoverd = want_discoverd and libsystemd_dep.found() conf.set('CONFIG_DISCOVERD', want_discoverd, description: 'Is nvme-discoverd enabled') if not want_mi or get_option('libdbus').disabled() libdbus_dep = dependency('', required: false) else # Check for libdbus availability. Optional, only required for MCTP dbus scan libdbus_dep = dependency( 'dbus-1', required: get_option('libdbus'), fallback: ['dbus', 'libdbus_dep'], default_options: [ 'default_library=static', 'embedded_tests=false', 'message_bus=false', 'modular_tests=disabled', 'tools=false', ], ) endif conf.set('CONFIG_DBUS', libdbus_dep.found(), description: 'Enable dbus support?') conf.set10( 'HAVE_STATEMENT_EXPR', cc.compiles( '''int main(int argc, char **argv) { return ({ int x = argc; x == 1; }); } ''', name: 'statement-expr' ), description: 'Can we use a statement as an expression?' ) conf.set10( 'NVME_HAVE_LINUX_MCTP_H', cc.compiles( '''#include <linux/mctp.h>''', name: 'linux/mctp.h' ), description: 'Is linux/mctp.h include-able?' ) have_netdb = false if not is_static have_netdb = cc.links( '''#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> int main(int argc, char **argv) { struct addrinfo hints, *result; return getaddrinfo(argv[1], argv[2], &hints, &result); } ''', name: 'netdb', ) endif conf.set( 'NVME_HAVE_NETDB', have_netdb, description: 'Is network address and service translation available' ) threads_dep = dependency('threads', required: true) dl_dep = dependency('dl', required: false) conf.set( 'NVME_HAVE_LIBC_DLSYM', cc.has_function('dlsym', dependencies: dl_dep), description: 'Is dlsym function present', ) conf.set('NVME_HAVE_SENDFILE', cc.has_function('sendfile')) conf.set('NVME_HAVE_MMAP', cc.has_function('mmap')) conf.set10( 'NVME_HAVE_REALLOCARRAY', cc.has_function( 'reallocarray', prefix : '#include <stdlib.h>', args : '-D_GNU_SOURCE'), description: 'Is reallocarray available', ) conf.set10( 'NVME_HAVE_SIGACTION', cc.has_function( 'sigaction', prefix : '#include <signal.h>'), description: 'Is sigaction available', ) conf.set10( 'HAVE_STORAGE_REINITIALIZE_MEDIA', host_system == 'windows' and cc.compiles( ''' #include <winioctl.h> int main(void) { struct STORAGE_REINITIALIZE_MEDIA media; return (int)sizeof(media); } ''', name: 'STORAGE_REINITIALIZE_MEDIA' ), description: 'Does winioctl.h define struct STORAGE_REINITIALIZE_MEDIA' ) project_config_file = 'nvme-config.h' nvme_config_h = configure_file(output: project_config_file, configuration: conf) config_dep = declare_dependency( sources: nvme_config_h, include_directories: '.', ) # The -include option below requires the full path when # nvme-cli is build as subproject of another meson project. if meson.version().version_compare('>=1.4.0') nvme_config_h_path = nvme_config_h.full_path() else nvme_config_h_path = meson.current_build_dir() / project_config_file endif add_project_arguments( [ '-fomit-frame-pointer', '-D_GNU_SOURCE', '-include', nvme_config_h_path, ], language : 'c', ) # This handles a specific packaging scenario where libnvme is built and # installed separately from nvme-cli. In this case, libnvme is installed # under a non-standard prefix, and nvme-cli must be built against the # previously installed libnvme headers. # # To support this layout, we explicitly add the libnvme include path from # the non-standard prefix. This cannot be done using include_directories(), # which only supports relative paths, while the libnvme include path is # absolute. if not want_libnvme and not std_prefix libnvme_hdr_location = prefixdir / get_option('includedir') / libnvme_api_name message( 'Using non-standard libnvme headers location: ', libnvme_hdr_location ) # Allow compiler to find headers in non-standard location add_project_arguments('-I' + libnvme_hdr_location, language : 'c') endif ################################################################################ libtype = get_option('default_library') if libtype == 'static' requires = '' endif if host_system == 'windows' add_project_arguments( [ '-DNOMINMAX', # don't include Windows min/max definitions '-D_POSIX_THREAD_SAFE_FUNCTIONS', # enables gmtime_r support ], language : 'c', ) endif substs = configuration_data() substs.set('VERSION', meson.project_version()) substs.set('NVMECLI_LICENSE', meson.project_license()[0]) substs.set('LIBNVME_LICENSE', meson.project_license()[1]) substs.set('UDEVRULESDIR', udevrulesdir) substs.set('DRACUTRULESDIR', dracutrulesdir) substs.set('REQUIRES', requires) substs.set('DATADIR', datadir) substs.set('MANDIR', mandir) substs.set('RUNDIR', rundir) substs.set('SBINDIR', sbindir) substs.set('SYSCONFDIR', sysconfdir) substs.set('SYSTEMDDIR', systemddir) substs.set('SYSTEMCTL', get_option('systemctl')) substs.set('PREFIX', prefixdir) substs.set('URL', 'https://github.com/linux-nvme/nvme-cli/') substs.set('LIBNVME_API_NAME', libnvme_api_name) substs.set('LIBNVME_SO_VERSION', libnvme_so_major) ################################################################################ doc_tgts = [] subdir('ccan') # declares: ccan_dep subdir('shared') # declares: shared_dep subdir('libnvme') # declares: libnvme_dep if want_nvme subdir('plugins') # declares: plugin_sources subdir('src') # declares: sources sources += plugin_sources link_args_list = [] link_deps = [ config_dep, ccan_dep, libnvme_dep, shared_dep, json_c_dep, libkmod_dep, ] if host_system == 'windows' link_deps += [ kernel32_dep, bcrypt_dep, ] if is_static # Keep these MinGW runtime dependencies in-process to avoid # requiring extra DLLs in PATH for static Windows builds. link_args_list += ['-Wl,-Bstatic'] if json_c_dep.found() link_args_list += ['-ljson-c'] endif link_args_list += ['-lwinpthread', '-Wl,-Bdynamic'] else link_deps += [winpthread_dep] endif else link_args_list = ['-ldl'] endif nvme_exe = executable( 'nvme', sources, include_directories: 'src', dependencies: link_deps, link_args: link_args_list, install: true, install_dir: sbindir, ) if host_system == 'windows' and want_libnvme and not is_static # Copy libnvme DLL next to nvme.exe so it can be found at # runtime without modifying PATH. cp = find_program('cp') custom_target('copy-libnvme-dll', input: libnvme, output: fs.name(libnvme.full_path()), command: [cp, '@INPUT@', '@OUTPUT@'], depends: [libnvme, nvme_exe], build_by_default: true, ) endif if get_option('e2e-tests') # A standalone copy of the e2e runner, placed next to the 'nvme' # binary in the build root so it can be invoked directly -- e.g. # './.build/nvme-cli-e2e --controller=... ...' -- entirely # independent of 'meson test'. cp = find_program('cp') custom_target('nvme-cli-e2e', input: files('tests/nvme-cli-e2e'), output: 'nvme-cli-e2e', command: [cp, '@INPUT@', '@OUTPUT@'], build_by_default: true, install: false, ) endif subdir('tests') if want_discoverd subdir('discoverd') endif configure_file( input: 'nvme.spec.in', output: 'nvme.spec', configuration: substs, ) configure_file( input: 'etc/nvme-fabrics.conf.sample.in', output: 'nvme-fabrics.conf.sample', configuration: substs, install: true, install_dir: join_paths(sysconfdir, 'nvme'), ) if want_nvmf_autoconnect dracut_files = [ '70-nvmf-autoconnect.conf', ] foreach file : dracut_files configure_file( input: 'nvmf-autoconnect/dracut-conf/' + file + '.in', output: file, configuration: substs, install: true, install_dir: dracutrulesdir, ) endforeach systemd_files = [ 'nvmefc-boot-connections.service', 'nvmf-autoconnect.service', 'nvmf-connect-nbft.service', 'nvmf-connect.target', 'nvmf-connect@.service', ] foreach file : systemd_files configure_file( input: 'nvmf-autoconnect/systemd/' + file + '.in', output: file, configuration: substs, install: true, install_dir: systemddir, ) endforeach endif # These rules provision keys and tune vendor devices; kept regardless of # which NVMe-oF autoconnect mechanism (if any) is in use. udev_files = [ '65-persistent-net-nbft.rules', '70-nvmf-keys.rules', '71-nvmf-hpe.rules', '71-nvmf-netapp.rules', '71-nvmf-vastdata.rules', ] if want_nvmf_autoconnect udev_files += '70-nvmf-autoconnect.rules' endif foreach file : udev_files configure_file( input: 'nvmf-autoconnect/udev-rules/' + file + '.in', output: file, configuration: substs, install: true, install_dir: udevrulesdir ) endforeach if want_nvmf_autoconnect nm_dispatcher_files = [ '80-nvmf-connect-nbft.sh' ] foreach file : nm_dispatcher_files configure_file( input: 'nvmf-autoconnect/nm-dispatcher/' + file + '.in', output: file, configuration: substs, install: true, install_mode: 'rwxr-xr-x', install_dir: nmdispatchdir ) endforeach endif install_data( 'completions/bash-nvme-completion.sh', rename: 'nvme', install_dir: datadir / 'bash-completion/completions', ) install_data( 'completions/_nvme', install_dir: datadir / 'zsh/site-functions', ) subdir('Documentation') endif if host_system == 'linux' valgrind_cmd = [ 'valgrind', '--leak-check=full', '--error-exitcode=1', '--suppressions=' + meson.current_source_dir() / 'valgrind.supp', ] python3_supp = '/usr/lib/valgrind/python3.supp' if fs.is_file(python3_supp) valgrind_cmd += ['--suppressions=' + python3_supp] endif add_test_setup('valgrind', exe_wrapper: valgrind_cmd, timeout_multiplier: 5, ) add_test_setup('asanubsan', timeout_multiplier: 2, ) endif if doc_tgts.length() == 0 doc_tgts += custom_target( 'docs-dummy', output: 'docs.stamp', command: ['true'], build_by_default: false, ) endif alias_target('docs', doc_tgts) ################################################################################ path_dict = { 'prefixdir': prefixdir, 'sysconfdir': sysconfdir, 'sbindir': sbindir, 'datadir': datadir, 'mandir': mandir, 'udevrulesdir': udevrulesdir, 'dracutrulesdir': dracutrulesdir, 'rundir': rundir, 'systemddir': systemddir, 'nmdispatchdir': nmdispatchdir, 'build location': meson.current_build_dir(), } summary(path_dict, section: 'Paths') dep_dict = { 'json-c': json_c_dep.found(), 'OpenSSL': openssl_dep.found(), 'keyutils': keyutils_dep.found(), 'libdbus': libdbus_dep.found(), 'python3': py3_dep.found(), 'liburing': liburing_dep.found(), 'libkmod': libkmod_dep.found(), 'libsystemd': libsystemd_dep.found(), } if host_system == 'windows' dep_dict += { 'kernel32': kernel32_dep.found(), 'bcrypt': bcrypt_dep.found(), 'setupapi': setupapi_dep.found(), 'cfgmgr32': cfgmgr32_dep.found(), } endif summary(dep_dict, section: 'Dependencies', bool_yn: true) wanted_dict = { 'nvme': want_nvme, 'libnvme': want_libnvme, 'fabrics': want_fabrics, 'nvme-discoverd': want_discoverd, 'nvmf-autoconnect': want_nvmf_autoconnect, 'deprecated-cmds': want_deprecated_cmds, 'python bindings ': want_python, 'build docs': want_docs_build, } summary(wanted_dict, section: 'Features to build', bool_yn: true) conf_dict = { 'git version': conf.get('GIT_VERSION'), } summary(conf_dict, section: 'Configuration', bool_yn: true)