Skip to content

Commit

Permalink
rust: add new 'object' type crate
Browse files Browse the repository at this point in the history
Allows linking Rust sources in C/C++ programs as static object files, which
means the Rust standard libraries/crates are not statically linked in the program,
saving a huge amount of installation space.
The caller is responsible for providing linkage to the standard library and/or any
needed external crate, and for providing allocators stubs if needed.
  • Loading branch information
bluca committed Dec 25, 2022
1 parent 2fd5bd9 commit dc18827
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 4 deletions.
23 changes: 23 additions & 0 deletions docs/markdown/snippets/rust_object_crates.md
Original file line number Diff line number Diff line change
@@ -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)
```
8 changes: 7 additions & 1 deletion docs/yaml/functions/_build_target_base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,17 @@ 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
default, "cdylib" means a C ABI library, "dylib" means a Rust ABI, and
"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.
10 changes: 9 additions & 1 deletion mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 6 additions & 2 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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]:
"""
Expand Down
8 changes: 8 additions & 0 deletions test cases/rust/20 object link/librust.rs
Original file line number Diff line number Diff line change
@@ -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
}
22 changes: 22 additions & 0 deletions test cases/rust/20 object link/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
project('staticprog', 'c')

rustc = find_program('rustc')
add_languages('rust')

# Requires a discoverable std library, most likely via pkg-config,
# but there is no common standard so make it optional for now.
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)
endif
8 changes: 8 additions & 0 deletions test cases/rust/20 object link/prog.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <stdio.h>

int print_foo(void);

int main(void) {
print_foo();
return 0;
}

0 comments on commit dc18827

Please sign in to comment.