diff --git a/docs/markdown/snippets/rust_object_crates.md b/docs/markdown/snippets/rust_object_crates.md new file mode 100644 index 000000000000..4196644972b8 --- /dev/null +++ b/docs/markdown/snippets/rust_object_crates.md @@ -0,0 +1,23 @@ +## Rust object crates + +In order to link Rust objects into C/C++ libraries/programs without static +linking all crates/libraries used by the objects, the new 'object' type crate +can be used. It will produce object files instead of libraries from the Rust +sources. The caller is responsible for providing the linking parameters for +any crate/library needed by the Rust objects. + +```meson +libstd_rust = meson.get_compiler('c').find_library('std-abcdefgh') + +librust = static_library( + 'rust', + 'librust.rs', + rust_crate_type : 'object', + dependencies: libstd-rust +) + +user = executable( + 'user, + 'user.c', + link_with : librust) +``` \ No newline at end of file diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index 46eedc1a997b..220590f37f05 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -291,7 +291,8 @@ kwargs: If it is a [[static_library]] it defaults to "lib", and may be "lib", "staticlib", or "rlib". If "lib" then Rustc will pick a default, "staticlib" - means a C ABI library, "rlib" means a Rust ABI. + means a C ABI library, "rlib" means a Rust ABI, "object" means only object + files will be emitted. If it is a [[shared_library]] it defaults to "lib", and may be "lib", "dylib", "cdylib", or "proc-macro". If "lib" then Rustc will pick a @@ -299,3 +300,8 @@ kwargs: "proc-macro" is a special rust proceedural macro crate. "proc-macro" is new in 0.62.0. + + "object" is new in 1.1.0 and allows to link Rust object files in C/C++ + programs without static linking Rust crates/libraries. The caller must + provide dynamic linking parameters for all crates/libraries used, including + the Rust standard library. diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index c583024d27f6..69d243fa6fa7 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1871,6 +1871,14 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: cratetype = 'rlib' else: raise InvalidArguments('Unknown target type for rustc.') + + # Support for building and linking only object files, without static linking crates + # Note that we don't emit the shared library here (avoid --emit link) as we only want objects + if cratetype == 'object': + cratetype = 'cdylib' + args.extend(['--emit', 'obj', '-C', 'prefer-dynamic']) + else: + args.extend(['--emit', 'link']) args.extend(['--crate-type', cratetype]) # If we're dynamically linking, add those arguments @@ -1884,7 +1892,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None: # Rustc replaces - with _. spaces are not allowed, so we replace them with underscores args += ['--crate-name', target.name.replace('-', '_').replace(' ', '_')] depfile = os.path.join(target.subdir, target.name + '.d') - args += ['--emit', f'dep-info={depfile}', '--emit', 'link'] + args += ['--emit', f'dep-info={depfile}'] args += target.get_extra_args('rust') output = rustc.get_output_args(os.path.join(target.subdir, target.get_filename())) args += output diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 60cd0cfcc736..7a830bca8188 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1931,8 +1931,8 @@ def post_init(self) -> None: mlog.debug('Defaulting Rust static library target crate type to rlib') self.rust_crate_type = 'rlib' # Don't let configuration proceed with a non-static crate type - elif self.rust_crate_type not in ['rlib', 'staticlib']: - raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for static libraries; must be "rlib" or "staticlib"') + elif self.rust_crate_type not in ['rlib', 'staticlib', 'object']: + raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for static libraries; must be "rlib", "staticlib" or "object"') # By default a static library is named libfoo.a even on Windows because # MSVC does not have a consistent convention for what static libraries # are called. The MSVC CRT uses libfoo.lib syntax but nothing else uses @@ -1949,6 +1949,8 @@ def post_init(self) -> None: self.suffix = 'rlib' elif self.rust_crate_type == 'staticlib': self.suffix = 'a' + elif self.rust_crate_type == 'object': + self.suffix = 'o' else: self.suffix = 'a' self.filename = self.prefix + self.name + '.' + self.suffix @@ -2247,6 +2249,8 @@ def process_kwargs(self, kwargs): raise InvalidArguments(f'Invalid rust_crate_type "{rust_crate_type}": must be a string.') if rust_crate_type == 'proc-macro': FeatureNew.single_use('Rust crate type "proc-macro"', '0.62.0', self.subproject) + if rust_crate_type == 'object': + FeatureNew.single_use('Rust crate type "object"', '1.1.0', self.subproject) def get_import_filename(self) -> T.Optional[str]: """ diff --git a/test cases/failing/54 wrong static crate type/test.json b/test cases/failing/54 wrong static crate type/test.json index 83ae5e1b60bb..0a8dae22182e 100644 --- a/test cases/failing/54 wrong static crate type/test.json +++ b/test cases/failing/54 wrong static crate type/test.json @@ -1,7 +1,7 @@ { "stdout": [ { - "line": "test cases/failing/54 wrong static crate type/meson.build:7:0: ERROR: Crate type \"cdylib\" invalid for static libraries; must be \"rlib\" or \"staticlib\"" + "line": "test cases/failing/54 wrong static crate type/meson.build:7:0: ERROR: Crate type \"cdylib\" invalid for static libraries; must be \"rlib\", \"staticlib\" or \"object\"" } ] } diff --git a/test cases/rust/20 object link/librust.rs b/test cases/rust/20 object link/librust.rs new file mode 100644 index 000000000000..a15bee85204f --- /dev/null +++ b/test cases/rust/20 object link/librust.rs @@ -0,0 +1,8 @@ +use std::os::raw::c_int; + +#[no_mangle] +pub unsafe extern "C" fn print_foo() -> c_int { + let foo = "rust compiler is working"; + println!("{}", foo); + 0 +} diff --git a/test cases/rust/20 object link/meson.build b/test cases/rust/20 object link/meson.build new file mode 100644 index 000000000000..226fc0ef9c64 --- /dev/null +++ b/test cases/rust/20 object link/meson.build @@ -0,0 +1,24 @@ +project('staticprog', 'c') + +rustc = find_program('rustc') +add_languages('rust') + +libstd_rust = dependency('std-rust', required: false) + +librust = static_library( + 'rust', + 'librust.rs', + dependencies: libstd_rust, + rust_crate_type: 'object') + +if libstd_rust.found() + e = executable('c-program', 'prog.c', + link_with: librust, + install : true + ) + test('rusttest', e) +else + # Requires a discoverable std library, most likely via pkg-config, + # but there is no common standard so make it optional for now. + error('MESON_SKIP_TEST: libstd-rust dependency not found') +endif diff --git a/test cases/rust/20 object link/prog.c b/test cases/rust/20 object link/prog.c new file mode 100644 index 000000000000..a34fb9c64b15 --- /dev/null +++ b/test cases/rust/20 object link/prog.c @@ -0,0 +1,8 @@ +#include + +int print_foo(void); + +int main(void) { + print_foo(); + return 0; +}