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

[Discussion] how to model GLIBC version for conan packages #3972

Open
3 tasks
SSE4 opened this issue Nov 21, 2018 · 40 comments
Open
3 tasks

[Discussion] how to model GLIBC version for conan packages #3972

SSE4 opened this issue Nov 21, 2018 · 40 comments

Comments

@SSE4
Copy link
Contributor

SSE4 commented Nov 21, 2018

To help us debug your issue please explain:

  • I've read the CONTRIBUTING guide.
  • I've specified the Conan version, operating system version and any tool that can be relevant.
  • I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.

/cc @rdeterre @bc-lee @elizagamedev

this is more generic version of #3963
related issues:
bincrafters/community#296
bincrafters/community#267

historically, conan didn't model glibc version, relying only on compiler version for binary compatibility checks.
this causes some issues in the past, as binaries compiled with the same compiler weren't compatible across various linux distributions.
for instance, if we compiled libraries or executables via GCC 4.8 on Ubuntu 12.04, these executables and libraries fail to run/link on CentOS 7 machines. same situation with other popular Linux distributions, like RHEL, Fedora, etc.

@danimtb
Copy link
Member

danimtb commented Nov 26, 2018

We should discuss this issue once we face the cross-building model one as this might affect the current settings and have further implications in Linux packages.

@scddev
Copy link
Contributor

scddev commented Feb 18, 2019

I am facing a similar issue regarding libc when building packages for glibc and musl (e.g. Alpine). Both are Linux, x86_64 but linked against a different libc.

We modified settings.yml like this:

compiler:
  ...
  gcc:
    ...
    libc: [None, glibc, musl]
  clang:
    ..
    libc: [None, glibc, musl]

This does not cover the fact of different versions of glibc or musl but covers at least our use case.

@vector-of-bool
Copy link
Contributor

To note: There is more than just the glibc version that defines the system ABI on Linux. Things like dynamic linking SONAMEs to system libraries present another dimension of whether a given ELF can be loaded correctly on a given system. Issues with differing system libraries were my trouble with Qt, even if there were compatible glibc versions.

I have a local fork of Conan at $job and I experimented with the introduction of two settings: os_family and os_distribution. The former is more general, with values like RedHat, Debian, SUSE and Windows, while the latter is more specific, with things like CentOS, RHEL, Ubuntu, and OpenSUSE. Another setting, os_release would cover the major revisions of a particular OS family/distribution, with values like 7 (for RHEL 7 and CentOS 7), 16.04, or 19.04.

A tuple of (os_family, os_release) is almost always enough to distinguish the basic ABI of the system, since the libc version and important system SONAMEs are stable within a given release of a distribution. In my tests, I was able to successfully generate Qt packages using this scheme that reliably rebuilt between a Ubuntu and CentOS system without them accidentally sharing binaries.

Of course, not all packages are so sensitive to these system attributes, and may want to "opt-in" to become more granular.

One case this design does not consider is that a CentOS 6 package is probably compatible with CentOS 7 (as long as the proper SONAMEs match), but I personally don't consider that a compelling enough feature to warrant additional complexity.

@rdeterre
Copy link

rdeterre commented Jul 4, 2019

As a workaround for this issue, is there a conan install option we could use to prevent a machine from using prebuilt binaries from the remote server, and rebuild packages from source if it does not have a locally built package?

Simply using --build solves the issue, except that all the dependencies get rebuilt every time even when perfectly valid prebuilt binaries are already available locally. This is problematic for big dependencies like Boost.

@Minimonium
Copy link
Contributor

I mean, always building the stuff from public servers maybe not that nice, since then why do we store it all then.
Indeed, it would be great to have a distribution + its version combination to define the whole distribution environment, not just glibc.
But also we could consider providing an alternative approach of the glibc + standard library and its version combination for "compatible" binaries if your package doesn't use other system libraries. Maybe not that useful for public binaries, but it's good to have such default settings out of the box for private workshops to not reinvent the wheel.

@kwinsch
Copy link

kwinsch commented Jul 24, 2019

Taking the suggestions from https://docs.conan.io/en/latest/extending/custom_settings.html as a base and incorporating the ideas from @scddev , the following custom settings are working for me:

os:
...
  Linux:
    distro: [None, RHEL6, RHEL7, Ubuntu18.04]
...
compiler:
  ...
  gcc:
    ...
    libc: [None, glibc, musl]
...

Whe also have some custom made distros for embedded device which we all have a distro name for. Unlike the glibc mess, working with musl as a libc, does usually not require the definition of the distro.

@kwinsch
Copy link

kwinsch commented Aug 15, 2019

For the toolchain side, we also had to expand 'os_build', so that the from source generated toolchain works on all build nodes. The toolchain is added as a dependency in the profile via a '[build_requires]'. The following expansion of the 'settings.yml" seems to works for our embedded, cross compilation setup.

os_build: 
    Windows:
    WindowsStore:
    Linux:
        distro: [None, "alpine", "ubuntu1204", "ubuntu1804"]
    Macos:
    FreeBSD:
    SunOS:
    AIX:
...
os:
    ...
    Linux:
        distro: [None, "buildroot_foo", "buildroot_bar"]
        platform: [None, "foo", "bar"]
...
compiler:
  ...
  gcc:
    ...
    libc: [None, glibc, musl]
...

@datalogics-robb
Copy link
Contributor

I'm in favor of the solution using libc and its version.
Distribution is more specific, but is orthogonal to the things that make a binary incompatible. If a package is created linking to system shared libraries, those libraries might not have been installed even though the same version of the same distribution is used. This same issue could also be resolved by using static "linking", which isn't feasible to do with glibc.

@puetzk
Copy link

puetzk commented May 29, 2020

We (with several linux targets, some public distros and some yocto-based cross-toolchains) do the following in settings.yml

os:
    Linux:
        distribution:
            None:
            # based on /etc/os-release $ID and $VERSION_ID
            ubuntu:
                version: [ "16.04", "16.10", "18.04", "18.10" ]
            wrlinux:
                version: [ "7.0", "8.0", "8.0.0.28", "8.0.0.30" ]
            fsl-imx:
                version: [ "4.14.98-2.0.0_ga" ]

Allowing distribution: None covers the existing scheme if the profile doesn't set it, but otherwise we use the https://www.freedesktop.org/software/systemd/man/os-release.html standard to get labeling (not for the first time making me wish there was a .py version of profiles like there is conanfile.txt and conanfile.py, so it could just read os-release directly).

Yocto supports this sort of labeling via the bitbake recipe https://git.yoctoproject.org/cgit.cgi/poky/plain/meta/recipes-core/os-release/

@madebr
Copy link
Contributor

madebr commented Jun 27, 2020

What about this:

libc:
  glibc: ["2.18", "2.19", "2.20", ..., "2.31"]
  musl: ["0.9", "1.0", "1.1", "1.2"]
compiler:
  gcc:
    libc: &libc (None for undefined or default)
  llvm:
    libc: &libc
  Visual Studio:
    [no libc]

For auto generation of the profile, the libc version can be detected by compiling+executing a simple file using gnu_get_libc_version, or extracting the version defintions from objdump -p /lib64/libc.so.*.

The bigger problem is: how will the hashing/package id work?

Packages compiled with different glibc versions, will generate a different package id.
But how should conan behave if a consumer has a newer glibc library? Should it require a package of the same glibc version?
Should it look for all available packages having a glic version lower as its own?
ALso, I"m afraid this range matching is incompatible with the hashing principle of conan.

@Minimonium
Copy link
Contributor

That's the same issue as with compiler.cppstd. We need a way of constraining compatible settings from upstream.

@mpusz
Copy link

mpusz commented Jun 29, 2020

Another point to highlight the importance of this feature is conan-io/conan-docker-tools#200. Even though there is gcc-9.3 available in Ubuntu 20.04 (used already for gcc-10 image) and it fixes some bugs in gcc-9.2 that prevent my library to compile on the older compiler, we can't upgrade the docker image because of glibc issue.

@datalogics-robb
Copy link
Contributor

I've professionally encountered problems with GLIBC compatibility. We had a customer running a Linux from scratch whose exact version had a bug that only triggered with our service with libdispatch trying to fork a child process. It was only one exact version that had the bug, the rest...

IMHO - we're discussing a "model" for glibc compatibility for Linux - bugs are not the sort of thing I would include in a model of how glibc compatibility works. Bugs will invariably violate any rules for compatibility, and render any model invalid. I would recommend creating a model that ignores a special case like a bug in a particular version of a library as it will cause more problems than it will solve. A more reasonable fix for an issue like this might be to have a recipe require a version of glibc later than the problematic one.

@blackliner
Copy link
Contributor

blackliner commented Feb 26, 2023

We are also facing the issue of Ubuntu 20.04 binaries of dependencies being pulled for 18.04 builds (because package IDs are the same on Ubuntu 18.04 and 20.04), and erroring out with:

~/.conan/data/opencv/4.5.2/_/_/package/b2b0041ab1b097316770fce0d8e5c4930aed6469/lib/libopencv_imgcodecs.so: undefined reference to `pow@GLIBC_2.29'
~/.conan/data/opencv/4.5.2/_/_/package/b2b0041ab1b097316770fce0d8e5c4930aed6469/lib/libopencv_imgproc.so: undefined reference to `exp@GLIBC_2.29'
~/.conan/data/opencv/4.5.2/_/_/package/b2b0041ab1b097316770fce0d8e5c4930aed6469/lib/libopencv_imgproc.so: undefined reference to `log@GLIBC_2.29

Do we have a workaround that works today?

@samuel-emrys
Copy link
Contributor

@blackliner The solution that I most often see recommended is to implement the following in your own settings.yml:

libc:
  glibc: ["2.18", "2.19", "2.20", ..., "2.31"]
  musl: ["0.9", "1.0", "1.1", "1.2"]
compiler:
  gcc:
    libc: &libc (None for undefined or default)
  llvm:
    libc: &libc
  Visual Studio:
    [no libc]

With conan 2.0, you could also try to define the compatibility relationship between different versions of glibc as well, using compatibility.py

@maksim-petukhov
Copy link

maksim-petukhov commented Mar 7, 2023

It's absolutely fine to use binaries compiled for centos 7 (glibc 2.17) in ubuntu 16.04 (glibc 2.23), because glibc is backwards compatible. So I wouldn't go that way:

os:
    Linux:
        distribution:
            None:
            # based on /etc/os-release $ID and $VERSION_ID
            ubuntu:
                version: [ "16.04", "16.10", "18.04", "18.10" ]
            wrlinux:
                version: [ "7.0", "8.0", "8.0.0.28", "8.0.0.30" ]
            fsl-imx:
                version: [ "4.14.98-2.0.0_ga" ]

This solution is not a valid yaml and it exposes libc key:

libc:
  glibc: ["2.18", "2.19", "2.20", ..., "2.31"]
  musl: ["0.9", "1.0", "1.1", "1.2"]
compiler:
  gcc:
    libc: &libc (None for undefined or default)
  llvm:
    libc: &libc
  Visual Studio:
    [no libc]

I modified it like so:

compiler:
    gcc:
        ...
        libc: &libc
            None: ~
            glibc: 
                version: ["2.17", ...]
    msvc:
        ...
        # no libc
    clang:
        ...
        libc:
            <<: *libc

None is for compatibility (null in Conan 2.0).
There is a little problem with packages for tools like ninja, tar etc. They have this in their conanfile:

    def package_id(self):
        del self.info.settings.compiler

It is a problem because an executable built for glibc 2.35 won't start with glibc 2.17. In order to fix this we should del everything in settings.compiler except libc if it is present.

@jusito
Copy link

jusito commented Mar 7, 2023

because glibc is backwards compatible.

Are you sure with it because as far as I know its not:
https://abi-laboratory.pro/?view=timeline&l=glibc
I don't have deep insights into this topic, just read about it and found this overview some time ago.

@maksim-petukhov
Copy link

because glibc is backwards compatible.

Are you sure with it because as far as I know its not: https://abi-laboratory.pro/?view=timeline&l=glibc I don't have deep insights into this topic, just read about it and found this overview some time ago.

@jusito let me just quote official webpage:

The GNU C Library is designed to be a backwards compatible, portable, and ...

There you can find some details on how they handle it

@datalogics-robb
Copy link
Contributor

datalogics-robb commented Mar 7, 2023 via email

@prince-chrismc
Copy link
Contributor

Please go watch Diego’s or my conference talk, when we do the opinionated ecosystem part

I affectionately think of you guys and gals.

"Death by a thousand bites"

@SpaceIm
Copy link
Contributor

SpaceIm commented Sep 17, 2023

IMHO I think it would be a surprise if the glibc version were part of the compiler settings though, as it’s a property of the OS/distro.

I agree.

Currently my custom conan v2 settings.yml is modified with:

os:
    Linux:
        libc:
            null:
            glibc:
                version: ["2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7",
                          "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", "2.14",
                          "2.15", "2.16", "2.17", "2.18", "2.19", "2.20", "2.21",
                          "2.22", "2.23", "2.24", "2.25", "2.26", "2.27", "2.28",
                          "2.29", "2.30", "2.31", "2.32", "2.33", "2.34", "2.35",
                          "2.36", "2.37", "2.38"]
            musl:
                version: ["0.9", "1.0", "1.1", "1.2"]

And in profiles targeting Linux I just have to add something like:

os.libc=glibc
os.libc.version=2.35

@jwillikers
Copy link
Contributor

I use the same form as @SpaceIm. It's necessary to put libc as an OS subsetting since application packages will often erase settings.compiler. I deduce the libc version based off of the Linux distribution in profiles.

@rob-baily
Copy link

What about this:

libc:
  glibc: ["2.18", "2.19", "2.20", ..., "2.31"]
  musl: ["0.9", "1.0", "1.1", "1.2"]
compiler:
  gcc:
    libc: &libc (None for undefined or default)
  llvm:
    libc: &libc
  Visual Studio:
    [no libc]

For auto generation of the profile, the libc version can be detected by compiling+executing a simple file using gnu_get_libc_version, or extracting the version defintions from objdump -p /lib64/libc.so.*.

The bigger problem is: how will the hashing/package id work?

Packages compiled with different glibc versions, will generate a different package id. But how should conan behave if a consumer has a newer glibc library? Should it require a package of the same glibc version? Should it look for all available packages having a glic version lower as its own? ALso, I"m afraid this range matching is incompatible with the hashing principle of conan.

Isn't range matching supported per https://docs.conan.io/2/tutorial/versioning/version_ranges.html? If the requirement needs to be part of the recipe because adding requirement based on the system then maybe instead of specifying hard coded versions it would be something like <${system_glibc_version} where ${system_glibc_version} is the version detected on the system.

@memsharded
Copy link
Member

Isn't range matching supported per https://docs.conan.io/2/tutorial/versioning/version_ranges.html?

No, version-ranges apply only to package versions, but is completely unrelated to the binary package_id computation. Only when the full dependency graph is resolved, and every version and recipe-revision of every package in the graph is known, the computation of package_id happens. At that point the glibc version is when it can be taken into account. The main problem is that defining a general glibc compatibility can be very challenging, so this will most likely not added to built-in.

In any case, adding custom glibc compatibility in 2.0 is relatively straightforward:

  • Add glibc as subsetting to os to your settings_user.yml, with the versions you want to manage
  • Add the os.glibc value in your profile files
  • If you want to implement some glibc compatibility, add the logic in your compatibility.py implementation

All 3 things can be conveniently managed with conan config install

@jwillikers
Copy link
Contributor

@memsharded It would be super helpful to have built-in detection for the glibc version through the detect_api. Would it be possible to add a method for this like detect_api.detect_libc or detect_api.detect_glibc? It's very easy to determine this on Linux via the ldd --version command. Right now, my solution is to hard-code the glibc version for every Linux distribution and version of that distribution, which makes it a pain to have to update all the time and impossible to use on rolling release distributions.

@memsharded
Copy link
Member

@jwillikers yes that would be possible. Would you like to contribute it yourself? the detect_api is relatively isolated, it shouldn't be difficult.

@jwillikers
Copy link
Contributor

@jwillikers yes that would be possible. Would you like to contribute it yourself? the detect_api is relatively isolated, it shouldn't be difficult.

Definitely. This will really help us clean up our Conan profiles, so I'll be able to. Hopefully I'll be able to get around to it in the near future.

jwillikers added a commit to jwillikers/conan that referenced this issue Feb 15, 2024
This PR adds a simple function to detect the version of glibc to the detect_api.
This functionality is only intended for Linux systems using glibc.
The function is detect_gnu_libc().
It simply runs ldd --version to detect the version of glibc.
It is also possible to detect the version of the libc library by running the libc.so file.
The location of this library varies much more than the path to ldd, so ldd --version is used instead.
The path to ldd is passed as an argument, /usr/bin/ldd by default.
This can modified to suite alternate sysroots.
It can even be used in cross-compilation scenarios where emulation is available to execute ldd.

The detect_gnu_libc function does a simple sanity check to verify that glibc is indeed being used.
This avoids confusion on systems using an alternative libc implementation, i.e. musl.

I also added a function to detect the version of the musl libc library as well.
This function is detect_musl_libc() and works the same way.

Addresses conan-io#3972, at least in part.
jwillikers added a commit to jwillikers/conan that referenced this issue Feb 15, 2024
This PR adds a simple function to detect the version of glibc to the detect_api.
This functionality is only intended for Linux systems using glibc.
The function is detect_gnu_libc().
It simply runs ldd --version to detect the version of glibc.
It is also possible to detect the version of the libc library by running the libc.so file.
The location of this library varies much more than the path to ldd, so ldd --version is used instead.
The path to ldd is passed as an argument, /usr/bin/ldd by default.
This can modified to suite alternate sysroots.
It can even be used in cross-compilation scenarios where emulation is available to execute ldd.

The detect_gnu_libc function does a simple sanity check to verify that glibc is indeed being used.
This avoids confusion on systems using an alternative libc implementation, i.e. musl.

I also added a function to detect the version of the musl libc library as well.
This function is detect_musl_libc() and works the same way.

Addresses conan-io#3972, at least in part.
jwillikers added a commit to jwillikers/conan that referenced this issue Feb 15, 2024
This PR adds a simple function to detect the version of glibc to the detect_api.
This functionality is only intended for Linux systems using glibc.
The function is detect_gnu_libc().
It simply runs ldd --version to detect the version of glibc.
It is also possible to detect the version of the libc library by running the libc.so file.
The location of this library varies much more than the path to ldd, so ldd --version is used instead.
The path to ldd is passed as an argument, /usr/bin/ldd by default.
This can modified to suite alternate sysroots.
It can even be used in cross-compilation scenarios where emulation is available to execute ldd.

The detect_gnu_libc function does a simple sanity check to verify that glibc is indeed being used.
This avoids confusion on systems using an alternative libc implementation, i.e. musl.

I also added a function to detect the version of the musl libc library as well.
This function is detect_musl_libc() and works the same way.

Addresses conan-io#3972, at least in part.
memsharded pushed a commit that referenced this issue Feb 19, 2024
* Add detect_gnu_libc() and detect_musl_libc() functions

This PR adds a simple function to detect the version of glibc to the detect_api.
This functionality is only intended for Linux systems using glibc.
The function is detect_gnu_libc().
It simply runs ldd --version to detect the version of glibc.
It is also possible to detect the version of the libc library by running the libc.so file.
The location of this library varies much more than the path to ldd, so ldd --version is used instead.
The path to ldd is passed as an argument, /usr/bin/ldd by default.
This can modified to suite alternate sysroots.
It can even be used in cross-compilation scenarios where emulation is available to execute ldd.

The detect_gnu_libc function does a simple sanity check to verify that glibc is indeed being used.
This avoids confusion on systems using an alternative libc implementation, i.e. musl.

I also added a function to detect the version of the musl libc library as well.
This function is detect_musl_libc() and works the same way.

Addresses #3972, at least in part.

* Break out parse functions and add detect_libc function

* Add tests and fix detection of musl libc

* Add functional tests for detect_gnu_libc and detect_musl_libc

These just check that the functions run without errors.
This avoids assumptions about what C library is being used.

* Unify on a single function

* Replace unittest with pytest

* Fix return for detect_libc function
@sagi-ottopia
Copy link
Contributor

Hi, I'm facing a similar issue, building a library for 2 different versions of Ubuntu (Focal, Jammy) and having glibc version issues.
I installed conan version 2.2.2 and I see that the new functions detect_libc is not being called, so the default profile/settings are not updated with glibc version.
What am I missing ? should I call it manually in my recipes ?
Thanks.

@memsharded
Copy link
Member

memsharded commented Apr 7, 2024

Hi @sagi-ottopia

The detection of libc is not automatic in the default profile detection. If you want to use it, you have to use the new detect_api in your profile with jinja syntax. Not in the recipes, but in the profile, like:

{% set libc, libc_version = detect_api.detect_libc() %}
[settings]
os=Linux
[conf]
user.confvar:libc={{libc}}
user.confvar:libc_version={{libc_version}}

Then, the settings model has to be customized, depending on how you want to model it, please check https://docs.conan.io/2/reference/config_files/settings.html#adding-new-sub-settings

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

No branches or pull requests