mirror of
https://github.com/nlohmann/json.git
synced 2026-10-04 13:40:33 +00:00
Check in CI that Meson and pkg-config offer the CMake options
tools/check_build_options/check_build_options.py takes the compile definitions of the CMake target as the reference and checks that the CMake pkg-config block, meson_options.txt (name and default), meson.build, and the Meson section of the package manager docs have each option with the same definition and condition. It runs as make check_build_options in the ci_meson_install job, so a new CMake option cannot be forgotten in the Meson build again. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -39,6 +39,8 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Get latest CMake and ninja
|
- name: Get latest CMake and ninja
|
||||||
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
|
- name: Check that Meson and pkg-config offer the CMake options
|
||||||
|
run: make check_build_options
|
||||||
- name: Install Meson
|
- name: Install Meson
|
||||||
run: pip install meson
|
run: pip install meson
|
||||||
- name: Install with Meson
|
- name: Install with Meson
|
||||||
|
|||||||
@@ -267,7 +267,9 @@ ninja -C builddir
|
|||||||
```
|
```
|
||||||
|
|
||||||
`meson_options.txt` defines the options, which mirror the CMake options that change the library's target (for example,
|
`meson_options.txt` defines the options, which mirror the CMake options that change the library's target (for example,
|
||||||
`-DDiagnostics=true`). Meson requires this file next to `meson.build`, so it is also part of `include.zip`.
|
`-DDiagnostics=true`). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options`
|
||||||
|
([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files
|
||||||
|
stay in sync with the CMake options.
|
||||||
|
|
||||||
When installing, `meson.build` installs the headers, a pkg-config file, and the CMake package config files, so that
|
When installing, `meson.build` installs the headers, a pkg-config file, and the CMake package config files, so that
|
||||||
`find_package(nlohmann_json)` works. As Meson cannot generate `nlohmann_jsonTargets.cmake` itself, it is created from
|
`find_package(nlohmann_json)` works. As Meson cannot generate `nlohmann_jsonTargets.cmake` itself, it is created from
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check
|
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check check_build_options
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# configuration
|
# configuration
|
||||||
@@ -124,6 +124,10 @@ macro_builder_check:
|
|||||||
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
||||||
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
||||||
|
|
||||||
|
# check that the Meson build and the pkg-config files offer the options of the CMake target
|
||||||
|
check_build_options:
|
||||||
|
python3 tools/check_build_options/check_build_options.py .
|
||||||
|
|
||||||
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
||||||
check-amalgamation:
|
check-amalgamation:
|
||||||
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# check_build_options
|
||||||
|
|
||||||
|
Checks that the Meson build and the pkg-config files offer the same options as the CMake target, so that a new CMake
|
||||||
|
option is not forgotten in one of them.
|
||||||
|
|
||||||
|
The compile definitions of the CMake target (`target_compile_definitions` in [`CMakeLists.txt`](../../CMakeLists.txt))
|
||||||
|
are the reference. When you add an option there, also add it to
|
||||||
|
|
||||||
|
- the pkg-config block in `CMakeLists.txt` (`NLOHMANN_JSON_PKGCONFIG_CFLAGS`),
|
||||||
|
- [`meson_options.txt`](../../meson_options.txt), named without the `JSON_` prefix and with the same default,
|
||||||
|
- [`meson.build`](../../meson.build) (`json_defines`), and
|
||||||
|
- the list of Meson options in
|
||||||
|
[`docs/mkdocs/docs/integration/package_managers.md`](../../docs/mkdocs/docs/integration/package_managers.md).
|
||||||
|
|
||||||
|
Run the check with
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make check_build_options
|
||||||
|
```
|
||||||
|
|
||||||
|
It needs only Python 3 and runs in the `ci_meson_install` CI job.
|
||||||
Binary file not shown.
+144
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Check that the Meson build and the pkg-config files offer the CMake options.
|
||||||
|
|
||||||
|
The compile definitions of the CMake target (target_compile_definitions in
|
||||||
|
CMakeLists.txt) are the reference. For every option used there, the script
|
||||||
|
checks that
|
||||||
|
|
||||||
|
- the CMake pkg-config file adds the same definition under the same condition,
|
||||||
|
- meson_options.txt has a boolean option of the same name without the "JSON_"
|
||||||
|
prefix and with the same default,
|
||||||
|
- meson.build adds the same definition under the same condition, and
|
||||||
|
- the Meson section of the package manager documentation lists the option.
|
||||||
|
|
||||||
|
Meson's MultipleHeaders option selects the include directory and adds no
|
||||||
|
definition; it is the only Meson option without a definition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
|
||||||
|
DOCS = os.path.join('docs', 'mkdocs', 'docs', 'integration', 'package_managers.md')
|
||||||
|
|
||||||
|
# Meson options that add no compile definition, with their default
|
||||||
|
MESON_ONLY = {'MultipleHeaders': 'false'}
|
||||||
|
|
||||||
|
|
||||||
|
def read(root, path):
|
||||||
|
with open(os.path.join(root, path), encoding='utf-8') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_target_definitions(cmake):
|
||||||
|
"""Return {option: (definition, add_if_on)} from target_compile_definitions."""
|
||||||
|
block = re.search(r'target_compile_definitions\(\s*\$\{NLOHMANN_JSON_TARGET_NAME\}\s*INTERFACE(.*?)\n\)', cmake, re.S)
|
||||||
|
if not block:
|
||||||
|
sys.exit('CMakeLists.txt: target_compile_definitions of the target not found')
|
||||||
|
result = {}
|
||||||
|
for line in block.group(1).split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
m = re.fullmatch(r'\$<\$<NOT:\$<BOOL:\$\{JSON_(\w+)\}>>:(\w+=\w+)>', line)
|
||||||
|
if m:
|
||||||
|
result[m.group(1)] = (m.group(2), False)
|
||||||
|
continue
|
||||||
|
m = re.fullmatch(r'\$<\$<BOOL:\$\{JSON_(\w+)\}>:(\w+=\w+)>', line)
|
||||||
|
if m:
|
||||||
|
result[m.group(1)] = (m.group(2), True)
|
||||||
|
continue
|
||||||
|
sys.exit(f'CMakeLists.txt: unexpected line in target_compile_definitions: {line}')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_defaults(cmake):
|
||||||
|
"""Return {option: 'true'/'false'} for the option() calls of the form JSON_<name>."""
|
||||||
|
return {m.group(1): 'true' if m.group(2) == 'ON' else 'false'
|
||||||
|
for m in re.finditer(r'^option\(JSON_(\w+)\s+"[^"]*"\s+(ON|OFF)\)', cmake, re.M)}
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_pkgconfig_definitions(cmake):
|
||||||
|
"""Return {option: (definition, add_if_on)} from the pkg-config block."""
|
||||||
|
return {m.group(2): (m.group(3), m.group(1) is None)
|
||||||
|
for m in re.finditer(r'if \((NOT )?JSON_(\w+)\)\s*\n\s*string\(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -D(\w+=\w+)"\)', cmake)}
|
||||||
|
|
||||||
|
|
||||||
|
def meson_options(options):
|
||||||
|
"""Return {option: default} for the boolean options in meson_options.txt."""
|
||||||
|
result = {}
|
||||||
|
for block in re.findall(r'option\((.*?)\)', options, re.S):
|
||||||
|
name = re.search(r"'(\w+)'", block).group(1)
|
||||||
|
kind = re.search(r"type\s*:\s*'(\w+)'", block)
|
||||||
|
value = re.search(r'value\s*:\s*(\w+)', block)
|
||||||
|
result[name] = value.group(1) if kind and kind.group(1) == 'boolean' and value else None
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def meson_definitions(meson):
|
||||||
|
"""Return {option: (definition, add_if_on)} from meson.build."""
|
||||||
|
return {m.group(2): (m.group(3), m.group(1) is None)
|
||||||
|
for m in re.finditer(r"if (not )?get_option\('(\w+)'\)\s*\n\s*json_defines \+= '(\w+=\w+)'", meson)}
|
||||||
|
|
||||||
|
|
||||||
|
def describe(definition):
|
||||||
|
name, add_if_on = definition
|
||||||
|
return f'{name} if {"enabled" if add_if_on else "disabled"}'
|
||||||
|
|
||||||
|
|
||||||
|
def compare(errors, where, expected, actual):
|
||||||
|
for option, definition in expected.items():
|
||||||
|
if option not in actual:
|
||||||
|
errors.append(f'{where}: no definition for option {option} (expected {describe(definition)})')
|
||||||
|
elif actual[option] != definition:
|
||||||
|
errors.append(f'{where}: option {option} adds {describe(actual[option])}, expected {describe(definition)}')
|
||||||
|
for option in actual.keys() - expected.keys():
|
||||||
|
errors.append(f'{where}: definition for option {option}, which the CMake target does not have')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__.split('\n')[0])
|
||||||
|
parser.add_argument('root', nargs='?', default=REPO_ROOT, help='repository root (default: %(default)s)')
|
||||||
|
root = parser.parse_args().root
|
||||||
|
|
||||||
|
cmake = read(root, 'CMakeLists.txt')
|
||||||
|
reference = cmake_target_definitions(cmake)
|
||||||
|
defaults = cmake_defaults(cmake)
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
compare(errors, 'CMakeLists.txt (pkg-config)', reference, cmake_pkgconfig_definitions(cmake))
|
||||||
|
compare(errors, 'meson.build', reference, meson_definitions(read(root, 'meson.build')))
|
||||||
|
|
||||||
|
options = meson_options(read(root, 'meson_options.txt'))
|
||||||
|
for option in reference:
|
||||||
|
if option not in defaults:
|
||||||
|
errors.append(f'CMakeLists.txt: no option(JSON_{option} ... ON|OFF)')
|
||||||
|
elif option not in options:
|
||||||
|
errors.append(f'meson_options.txt: option {option} missing')
|
||||||
|
elif options[option] != defaults[option]:
|
||||||
|
errors.append(f'meson_options.txt: option {option} must be boolean with value {defaults[option]} as in CMake')
|
||||||
|
for option, default in MESON_ONLY.items():
|
||||||
|
if options.get(option) != default:
|
||||||
|
errors.append(f'meson_options.txt: option {option} must be boolean with value {default}')
|
||||||
|
for option in options.keys() - reference.keys() - MESON_ONLY.keys():
|
||||||
|
errors.append(f'meson_options.txt: option {option} has no counterpart in the CMake target')
|
||||||
|
|
||||||
|
docs = read(root, DOCS)
|
||||||
|
for option in sorted(set(options) & (reference.keys() | MESON_ONLY.keys())):
|
||||||
|
if f'`{option}`' not in docs:
|
||||||
|
errors.append(f'{DOCS}: Meson option {option} not listed')
|
||||||
|
|
||||||
|
for error in errors:
|
||||||
|
print(error, file=sys.stderr)
|
||||||
|
if errors:
|
||||||
|
print('The Meson build and the pkg-config files must offer the options of the CMake target; see '
|
||||||
|
'tools/check_build_options/README.md.', file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
print(f'OK: {len(reference)} options with compile definitions agree between CMake, pkg-config, and Meson.')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user