Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support build and link dynamic library #1444

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

westhide
Copy link

Copy link
Collaborator

@NobodyXu NobodyXu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

There are two places where I would like to see some changes

src/lib.rs Outdated
Comment on lines 1428 to 1439
if self.link_shared_flag {
let objects = objects.iter().map(|o| o.dst.clone()).collect::<Vec<_>>();

let mut command = self.try_get_compiler()?.to_command();
let cmd = command
.args(["-shared", "-o"])
.arg(dst.join(dynlib_name))
.args(&objects);
run(cmd, &self.cargo_output)?;
}

self.assemble(lib_name, &dst.join(static_name), &objects)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to run assemble if it's already dynamically linked?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider support dll on windows?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to run assemble if it's already dynamically linked?

Sure, fixed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider support dll on windows?

Can you elaborate on that please?

Copy link
Author

@westhide westhide Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows ddl is complicated, i have not idea how to add a dynamic library test case for it. It's weird as cl.exe require a main function to build a dll library.

With this source file, it will cause a Entry Point Not Found error

#include <windows.h>
void msvc() {}

Test pass if add a main function

#include <windows.h>
void msvc() {}
void main() {}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explanation, I'm ok with it not tested, if you have time to add another test for it I would greatly appreciate

@westhide westhide force-pushed the main branch 7 times, most recently from f9dead6 to f9e4a70 Compare March 31, 2025 16:15
Copy link
Collaborator

@NobodyXu NobodyXu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks LGTM!

cc @madsmtm can you review this PR please?

/// Configure whether cc should build dynamic library and link with `rustc-link-lib=dylib`
///
/// This option defaults to `false`.
pub fn link_shared_flag(&mut self, link_shared_flag: bool) -> &mut Build {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When reading this as a user, it is not really clear how this differs from shared_flag.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stated another way: When would you ever want shared_flag(true) but not link_shared_flag(true)? And vice-versa?

Comment on lines +1242 to +1248
/// Configures the output directory where shared library will be located.
///
/// This option defaults to `out_dir`.
pub fn shared_lib_out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Build {
self.shared_lib_out_dir = Some(out_dir.as_ref().into());
self
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm against adding this, I think the user should copy it from out_dir if they want the final shared library to be placed elsewhere.

@madsmtm
Copy link
Collaborator

madsmtm commented Apr 1, 2025

In general, I'm a bit inclined to think that -shared is an anti-feature of cc-rs, since:

@westhide
Copy link
Author

westhide commented Apr 4, 2025

In general, I'm a bit inclined to think that -shared is an anti-feature of cc-rs, since:

  • You never want this in a build.rs script, since then you need to distribute the shared library.
  • Outside of build.rs it may be valuable, but it also introduces a big cost, since now cc-rs has to know about linker flags as well (see e.g. Fix wasm32-unknown-unknown by passing -c #1424). I'd be more in favour of deprecating shared_flag, and instead recommending a linker-rs crate or similar.

Should I close this PR and use another crate to support dynamic linking? The purpose of submitting this commit was to implement a C/C++ UDF with Rust's compute engine. It's fine for me to just call a bash command in build.rs.

@madsmtm
Copy link
Collaborator

madsmtm commented Apr 4, 2025

The purpose of submitting this commit was to implement a C/C++ UDF with Rust's compute engine. It's fine for me to just call a bash command in build.rs.

Hmm, I guess I don't understand why that has to be dynamically linked?

Should I close this PR and use another crate to support dynamic linking?

We haven't actually decided to deprecate .shared_flag yet, so I'm still unsure what our recommendation should be.

@NobodyXu
Copy link
Collaborator

NobodyXu commented Apr 4, 2025

I think this is mostly an API issue, since it doesn't add a lot of code we just want the right API

@westhide
Copy link
Author

westhide commented Apr 4, 2025

When reading this as a user, it is not really clear how this differs from shared_flag.

I had the same confusion before I forked cc-rs and read the code. For most users' expectations, shared_flag should behave like dynamic linking. However, currently, shared_flag only adds the -shared flag while still linking statically. Since I didn’t want to introduce a breaking change, I added the link_shared_flag function to properly support dynamic linking.

Hmm, I guess I don't understand why that has to be dynamically linked?

In our use case, we have C/C++ UDF code that changes frequently. Static linking isn’t ideal because users would need to download and update the entire binary every time the UDF code changes, which can be time-consuming.
With dynamic linking, as long as we keep the Rust FFI interface stable, users only need to:

  • Update the latest UDF .so file,
  • Reset the LD_LIBRARY_PATH environment variable.

This approach significantly reduces update overhead.

@madsmtm
Copy link
Collaborator

madsmtm commented Apr 4, 2025

Since I didn’t want to introduce a breaking change, I added the link_shared_flag function to properly support dynamic linking.

I don't think this should really be seen as a breaking change, since .shared_flag(true) is broken today anyhow.

Alternatively, we deprecate both .shared_flag and .static_flag, and introduce .shared or .dynamic.

In our use case, we have C/C++ UDF code that changes frequently. Static linking isn’t ideal because users would need to download and update the entire binary every time the UDF code changes, which can be time-consuming. With dynamic linking, as long as we keep the Rust FFI interface stable, users only need to:

  • Update the latest UDF .so file,

  • Reset the LD_LIBRARY_PATH environment variable.

This approach significantly reduces update overhead.

Thanks for the context. If we go forwards with this PR, would you mind adding something to this effect to the method documentation, as a use-case example?

.target(&target)
.host(&target)
.target(target)
.host(target)
.file("foo.c")
.compile("foo");

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation on compile should also be updated to reflect the new renaming rules.

.target(&target)
.host(&target)
.target(target)
.host(target)
.file("foo.c")
.compile("foo");

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we actually want to support shared libraries, we should consider a .linker and .get_linker similar to .archiver and .get_archiver, as well as maybe allowing overriding the linker with an LD env var (?)

Though if we allow using a raw linker (e.g. invoke rust-lld binary instead of cc), we're getting into hairy territory of needing to know how to invoke the linker if it's not a compiler driver.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Which again is why I'm really not in favour of it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

shared_flag and static_flag don't work
3 participants