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

How should C++ dependencies of C libraries be properly linked? #14234

Open
alyssais opened this issue Feb 10, 2025 · 7 comments
Open

How should C++ dependencies of C libraries be properly linked? #14234

alyssais opened this issue Feb 10, 2025 · 7 comments

Comments

@alyssais
Copy link
Contributor

alyssais commented Feb 10, 2025

When C and C++ objects are being linked together, a C++ compiler should be used. Meson handles this case for internal C and C++ objects, but if a C project links with a C++ dependency, Meson does not know to use a C++ linker. Because static libraries don't contain dependency information, linking a static C executable with a C++ dependency will fail with undefined symbol errors for the libstdc++ (etc.) symbols. If a C executable depends on a C library that depends on a C++ library, that executable also has to be linked with a C++ linker, and so on.

For example, in Nixpkgs, we link libtiff (C) with LERC (C++), and we support building as much of our entire package set as possible using only static linking. Getting this to work properly would be very invasive — fixing libtiff is fine, but every C program that even transitively depends on libtiff also needs to be made to use a C++ linker. For example, I'm currently looking at appstream (C) -> librsvg (C [and Rust but that's irrelevant]) -> gdk-pixbuf (C) -> libtiff (C) -> LERC (C++), and wondering if it really makes sense to be teaching appstream's build system to be using link_language: 'cpp' whenever it's linking an executable with librsvg. Working around this in the build system is especially complicated when, as in this case, the C++ dependency layers down the dependency chain is optional, so a C++ compiler shouldn't always be used to prevent needlessly requiring one when there's no C++ involved.

What would the right solution be here? To me "this library needs to be linked using a C++ linker" is information that is analagous to the dependency information for static libraries included in pkg-config files, but there's no way to express that in a pkg-config file. Technically you could add the C++ standard library as a Libs.private entry, but that doesn't feel right, since AFAICT it's supposed to be an implementation detail of the C++ compiler, and there might be other C++-specific behaviors it's necessary to use a C++ compiler to get besides linking the standard library. (This isn't even really a Meson-specific problem, but it is a problem that Meson has, and it so happens that most of the packages I'm looking at are using Meson, and this is the place I trust I can find people who care about solving this problem.)

@richfelker
Copy link

This seems like a standard pkg-config file topic. While at the source level for the library written in C++, it's an implementation detail of the C++ implementation what -l is needed to get the C++ standard library, the built binaries for the library are tied to the ABI of the C++ standard library they're built against, and that linkage should be reflected in the .pc file that gets installed.

@eli-schwartz
Copy link
Member

eli-schwartz commented Feb 10, 2025

Technically you could add the C++ standard library as a Libs.private entry, but that doesn't feel right, since AFAICT it's supposed to be an implementation detail of the C++ compiler, and there might be other C++-specific behaviors it's necessary to use a C++ compiler to get besides linking the standard library.

It's even more complicated, since while it's technically an implementation detail both clang and GCC have different "implementation details", and can also be told via a public interface (-stdlib=) to use the other compiler's stdlib, or even default that way at configure time.

I think the build system of the static library itself, is in the best position to know what compiler and what -stdlib it uses, so in theory every build system could and should simply emit that information into custom pkg-config variables and meson should try using those variables if they exist.

The other solution that springs to mind is the vcpkg approach: all packages built for a specific configuration and CXXFLAGS go in their own distinctively named sysroot. That's kind of awkward to juggle... and it doesn't solve knowing the link_language itself.

@alyssais
Copy link
Contributor Author

I think the build system of the static library itself, is in the best position to know what compiler and what -stdlib it uses, so in theory every build system could and should simply emit that information into custom pkg-config variables and meson should try using those variables if they exist.

Will Meson be able to find those variables even if it's a transitive dependency? (In my example, will appstream's Meson build be able to figure out from lerc.pc that it needs to do C++ linkage?

What information would custom pkg-config variables need to contain? Meson can pass a custom -stdlib, but it only knows one C++ compiler, so if there are other compiler implementation details apart from that, mixing compilers still wouldn't work.

What would be the path to getting these variables adopted? This isn't just a Meson problem — is there a right place to propose (de-facto) standard pkg-config variables?

@eli-schwartz
Copy link
Member

Will Meson be able to find those variables even if it's a transitive dependency? (In my example, will appstream's Meson build be able to figure out from lerc.pc that it needs to do C++ linkage?

Currently this information is not checked, as it's not being used for anything. The tooling provides the necessary primitives to walk the .pc dependency graph, via pkg-config --print-requires (and --print-requires-private), so it can be done although it will be more painful / require running a bunch of commands, hopefully only when doing static linking though!

What would be the path to getting these variables adopted? This isn't just a Meson problem — is there a right place to propose (de-facto) standard pkg-config variables?

I would open a ticket at https://github.com/pkgconf/pkgconf/issues to discuss it. This also provides a path forward to e.g. query the link_language for a dependency and have pkgconf walk its own graph, merging declarations, and respond once with a suitable answer.

That would mean degrading on freedesktop pkg-config, which fortunately nobody uses anymore. I suppose a sufficiently interested lover of retrocomputing could still use the painfully slow method mentioned above.

@eli-schwartz
Copy link
Member

The CPS schema also defines link_languages, defaulting to "c", although CPS is still effectively an ecosystem in public alpha.

@alyssais
Copy link
Contributor Author

Done

@dcbaker
Copy link
Member

dcbaker commented Feb 11, 2025

We also have some work toward being able to deal with the stdlib thing in CPS, although it's probably not sufficient in all cases, nor do I think any of the implementations handle it (thus the not fully baked).

I'm hoping to have cps-config baked enough to start generating cps with Meson and round tripping in the next month. And CMake can do that already for simple enough cases.

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

No branches or pull requests

4 participants