diff --git a/docs/markdown/Machine-files.md b/docs/markdown/Machine-files.md index ecdb8b47a..9e4b0c2a5 100644 --- a/docs/markdown/Machine-files.md +++ b/docs/markdown/Machine-files.md @@ -237,6 +237,8 @@ section. subprojects. This setting has no effect if the `exe_wrapper` was not specified. The default value is `true`. (*new in 0.56.0*) - `java_home` is an absolute path pointing to the root of a Java installation. +- `bindgen_clang_arguments` an array of extra arguments to pass to clang when + calling bindgen ### CMake variables diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index 512c4ec96..30a63455b 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -86,10 +86,17 @@ r1 = rust.bindgen( ) ``` - *Since 1.1.0* Meson will synchronize assertions for Rust and C/C++ when the `b_ndebug` option is set (via `-DNDEBUG` for C/C++, and `-C debug-assertions=on` for Rust), and will pass `-DNDEBUG` as an extra argument to clang. This allows for reliable wrapping of `-DNDEBUG` controlled behavior with `#[cfg(debug_asserions)]` and or `cfg!()`. Before 1.1.0, assertions for Rust were never turned on by Meson. + +*Since 1.2.0* Additional arguments to pass to clang may be specified in a +*machine file in the properties section: + +```ini +[properties] +bindgen_clang_arguments = ['--target', 'x86_64-linux-gnu'] +``` diff --git a/docs/markdown/snippets/rust_extra_clang_bindgen_arguments.md b/docs/markdown/snippets/rust_extra_clang_bindgen_arguments.md new file mode 100644 index 000000000..71268d43a --- /dev/null +++ b/docs/markdown/snippets/rust_extra_clang_bindgen_arguments.md @@ -0,0 +1,8 @@ +## A machine file may be used to pass extra arguments to clang in a bindgen call + +Because of the way that bindgen proxies arguments to clang the only choice to +add extra arguments currently is to wrap bindgen in a script, since the +arguments must come after a `--`. This is inelegant, and not very portable. Now +a `bindgen_clang_arguments` field may be placed in the machine file for the host +machine, and these arguments will be added to every bindgen call for clang. This +is intended to be useful for things like injecting `--target` arguments. diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py index 1bf6ab9f7..11821fb77 100644 --- a/mesonbuild/envconfig.py +++ b/mesonbuild/envconfig.py @@ -237,6 +237,12 @@ class Properties: value = T.cast('T.Optional[str]', self.properties.get('java_home')) return Path(value) if value else None + def get_bindgen_clang_args(self) -> T.List[str]: + value = mesonlib.listify(self.properties.get('bindgen_clang_arguments', [])) + if not all(isinstance(v, str) for v in value): + raise EnvironmentException('bindgen_clang_arguments must be a string or an array of strings') + return T.cast('T.List[str]', value) + def __eq__(self, other: object) -> bool: if isinstance(other, type(self)): return self.properties == other.properties diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 41536303f..faca9c06a 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -201,7 +201,10 @@ class RustModule(ExtensionModule): else: depends.append(d) - clang_args: T.List[str] = [] + # Copy to avoid subsequent calls mutating the original + # TODO: if we want this to be per-machine we'll need a native kwarg + clang_args = state.environment.properties.host.get_bindgen_clang_args().copy() + for i in state.process_include_dirs(kwargs['include_directories']): # bindgen always uses clang, so it's safe to hardcode -I here clang_args.extend([f'-I{x}' for x in i.to_string_list( diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index 5a9c01d04..3807c8806 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import subprocess import tempfile import textwrap @@ -67,7 +69,7 @@ class NativeFileTests(BasePlatformTests): self.current_config = 0 self.current_wrapper = 0 - def helper_create_native_file(self, values): + def helper_create_native_file(self, values: T.Dict[str, T.Dict[str, T.Union[str, int, float, bool, T.Sequence[T.Union[str, int, float, bool]]]]]) -> str: """Create a config file as a temporary file. values should be a nested dictionary structure of {section: {key: @@ -81,10 +83,10 @@ class NativeFileTests(BasePlatformTests): for k, v in entries.items(): if isinstance(v, (bool, int, float)): f.write(f"{k}={v}\n") - elif isinstance(v, list): - f.write("{}=[{}]\n".format(k, ', '.join([f"'{w}'" for w in v]))) - else: + elif isinstance(v, str): f.write(f"{k}='{v}'\n") + else: + f.write("{}=[{}]\n".format(k, ', '.join([f"'{w}'" for w in v]))) return filename def helper_create_binary_wrapper(self, binary, dir_=None, extra_args=None, **kwargs): @@ -622,6 +624,29 @@ class NativeFileTests(BasePlatformTests): else: self.fail('Did not find bindir in build options?') + @skip_if_not_language('rust') + def test_bindgen_clang_arguments(self) -> None: + if self.backend is not Backend.ninja: + raise SkipTest('Rust is only supported with Ninja') + + testcase = os.path.join(self.rust_test_dir, '12 bindgen') + config = self.helper_create_native_file({ + 'properties': {'bindgen_clang_arguments': 'sentinal'} + }) + + self.init(testcase, extra_args=['--native-file', config]) + targets: T.List[T.Dict[str, T.Any]] = self.introspect('--targets') + for t in targets: + if t['id'].startswith('rustmod-bindgen'): + args: T.List[str] = t['target_sources'][0]['compiler'] + self.assertIn('sentinal', args, msg="Did not find machine file value") + cargs_start = args.index('--') + sent_arg = args.index('sentinal') + self.assertLess(cargs_start, sent_arg, msg='sentinal argument does not come after "--"') + break + else: + self.fail('Did not find a bindgen target') + class CrossFileTests(BasePlatformTests):