mesonbuild / meson

The Meson Build System
http://mesonbuild.com
Apache License 2.0
5.66k stars 1.64k forks source link

Don't want platform libraries to be included when linking statically to a dependency #2765

Open ocrete opened 6 years ago

ocrete commented 6 years ago

The "-static-libtool-libs" argument to libtool tells it only statically link libraries that have a ".la" files, but to dynamically link other libraries.

The use-case is the following:

The device contains the .so files for libraries that are also in its SDK. I want to create the smallest possible binary for my device using GStreamer. So I want the files coming from the "app SDK" to be statically linked into my application, but the files from the "platform SDK" to be static.

Currently, the way I do it is by making sure there are no ".la" files in the "device SDK", but that the "app SDK" always has a .la file.. Then I use the "-static-libtool-libs" option to libtool and it just works.

I suggest doing the same with .pc files. I think the trick is to take the output of "pkg-config --libs --static" and of "pkg-config --libs", and if a library is in both, add it to the list of static libraries. If a library is only in "pkgconfig --libs --static" then add it to the general list.. When all the libraries have been processed, remove all "static" libs from the general list. Then we'll have 2 lists, the "static" list.. which must be statically linked, and the "general list" which must be dynamically linked...

xclaesse commented 6 years ago

This has something similar to PR #2711 conceptually. The key is that pkgconfig dependencies could be used both as static and shared library. So meson has to make a choice and currently always link dynamically.

It makes sense to have a project wide option to tell which one to choose when both are available. I think this issue should use the same config than the one I introduced for both_library() case.

xclaesse commented 6 years ago

Actually PkgConfigDependency already has a static argument and the code to invoke pkg-config --static. So it is just a matter to add a global setting to set self.static to true by default on all ExternalDependency objects. Right?

ocrete commented 6 years ago

Except that linking statically means putting all the deps explicitly on the GCC command line. But using the output of pkg-config --static won't work as some of those may be static. also the order must be respected when linking statically.

ocrete commented 6 years ago

Another option for this is to instead do it path based, so I can say "libraries in ~/cerbero/build/ are statically linked, but those in ~/devicesdk/ are dynamically linked"

nirbheek commented 6 years ago

If you set static: true on a pkg-config dependency(), Meson will do some magic to find a static library corresponding to the paths outputted by pkg-config --libs and pass the full path to it to gcc. I implement this a couple of weeks ago, and this will work with 0.44.0. If it doesn't work, please file a bug.

The same should also work for cc.find_library().

Do you need anything else?

ocrete commented 6 years ago

Nope, that doesn't work, because you take the whole output of "pkg-config --libs --static" and try to build all of those as static. But this is not what I want, I just want some of them to be.

Here is an example

My meson.build is

project('test','c')
mydep = dependency('libpng', static: true)
executable('test2',['test.c'], dependencies: mydep)

Then I run meson as

PKG_CONFIG_PATH=~/cerbero/build/dist/linux_x86_64/lib/pkgconfig meson build

But that fails because it tries to find a static library for libm which is in the Libs.private.. Which is what I want to avoid. It does:

Meson encountered an error in file meson.build, line 3, column 0:
Static library not found for 'm'
nirbheek commented 6 years ago

For platform libraries, we should actually add a blacklist. For instance, no one wants -ldl -lm -lc -lpthread to be statically linked unless they are building with a custom toolchain and with no libc.

Would have to think how this would be implemented.

ocrete commented 6 years ago

I'd rather not having something magical for platform libraries, as maybe we want to use other libraries that are already in the platform such as glib or openssl (to make a smaller binary)

xclaesse commented 6 years ago

I think what we need is an exclude list like ['/usr', '/lib']. So if the fullpath of libfoo.a has one of them as prefix, link it dynamically instead.

rhd commented 6 years ago

The same should also work for cc.find_library().

This doesn't support static: true - even though I wish it did!

xclaesse commented 6 years ago

I'm facing a similar case for glib CI: https://gitlab.gnome.org/GNOME/glib/merge_requests/360

I would like in the CI to install glib into a prefix, then build a simple app that static link on the installed glib build, to verify the installed .pc is correct and static links works (spoiler alert, current they don't).

My test project looks like this:

project('test-static-link', 'c')

app = executable('test-static-link', 'app.c',
  dependencies : dependency('gio-2.0', static : true)
)
test('test-static-link', app)

The problem is the "static : true" kwarg is a all-or-nothing switch. Setting it to true means it will try to static link all glib dependencies (e.g. libffi) but fedora (used as base for our CI docker) doesn't package static libraries, so meson is printing WARNING message that it cannot find .a file for them. I would like to be able to tell "static link glib/gobject/gio that are installed into a custom prefix, and dynamic link their dependencies that comes from the distro".

I'm not convinced anymore PR #2816 is the best way to achieve this, I think there are easier and less invasive ways. I think we just need to make static kwarg accept other types than a a simple boolean. For example dependency('foo', static : ['/home/user/my-prefix/lib']) that would mean "static link only if the .a is found in that directory".

We could then do something like (would require nicer API to avoid double dependency lookup):

tmp = dependency('foo')
libdir = tmp.get_pkgconfig_variable('libdir')
dep = dependency('foo', static : [libdir])