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

[doxygen] Statically compile libc/libstdc++ to remove host system dependency #18415

Conversation

samuel-emrys
Copy link
Contributor

@samuel-emrys samuel-emrys commented Jul 8, 2023

This is a successor PR to #17051.

The previous form of this PR modified the package_id to use full_version_mode to ensure that the dependency package id's don't impact doxygen's package id - only version changes in dependencies would impact doxygen's package id. This was necessary to allow doxygen built with gcc 10 to be identified as compatible with a gcc 12 profile. Without this, compiler.version=12 propagates through the entire dependency tree and the package id of the doxygen package built with gcc 10 does not match appropriately and is not resolved as compatible, since it is impacted by the varying package_id of its upstream dependencies.

I've removed this as this was the reason #17051 was rejected, but I do note that it does hamper the existing implementation of compatibility() substantially as the benefits are only really seen when the only package in doxygen's tree using a different compiler version is doxygen.

Relates to #17034


@samuel-emrys samuel-emrys changed the title Recipe/17034 doxygen libcpp compatibility [doxygen] Remove self.info.settings.compiler deletion from package_id Jul 8, 2023
@conan-center-bot

This comment has been minimized.

@conan-center-bot

This comment has been minimized.

Comment on lines 58 to 62
if (Version(conan_version).major >= 2):
# Models libc++ compatibility and identifies Debug builds as equivalent to Release builds
compatible_versions = [{"settings": [("compiler.version", v), ("build_type", "Release")]}
for v in self.settings.compiler.version.possible_values() if v <= Version(self.settings.compiler.version)]
return compatible_versions
Copy link
Contributor Author

@samuel-emrys samuel-emrys Jul 9, 2023

Choose a reason for hiding this comment

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

To be clear about the intent of this PR - this compatibility() implementation is not the main purpose of this PR. The main purpose of this PR is to delete the del self.info.settings.compiler statement in the package_id() method. The information erasure here hurts the ability for users to manage libcxx abi compatibility.

This compatibility() implementation is a proposal for consideration of how to improve the model of libcxx compatibility going forward. It is not perfect (in the sense that it doesn't capture the binary compatibility for all binaries that would be compatible), but it does improve upon the representation indended by deleting self.info.settings.compiler.

Unfortunately the effectiveness of this compatibility() definition is largely inhibited by restrictions around CCI recipes modifying the package_id mode. As it stands, this only provides a benefit if a user were to attempt to consume doxygen as follows, assuming that a binary had been created using the gcc10 profile already:

conan install --requires doxygen/1.9.4 -pr:h gcc10 -pr:b gcc10 -s "doxygen/*:compiler.version=gcc12"

This is because, if the user does something like this:

conan install --requires doxygen/1.9.4 -pr:h gcc12 -pr:b gcc12

Then the entire dependency tree utilises gcc12, and the package id identified for a compatible version of doxygen (built with gcc10) changes.

Given the limited utility of this compatibility() implementation, I'm happy to remove it if required. It does positively contribute to this recipe, but as above, only in a limited way. I wanted to present this for consideration to trigger some thought about when the compatibility() method provides value in CCI recipes/what a standard practice should be.

@@ -54,10 +54,13 @@ def requirements(self):
self.requires("xapian-core/1.4.19")
self.requires("zlib/1.2.13")

def package_id(self):
del self.info.settings.compiler
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the primary purpose of this PR. This kind of statement should be removed from all tool_requires packages. See option (3) proposed in #17034 for more detail

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand, if it should be removed, and if it's a primary purpose of this PR, then why it's not removed?
P.S. all the side changes could go into another PR. it's good practice to keep PRs atomic and focused on one thing at time. it's easier to review/approve changes if the do only one thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand, if it should be removed, and if it's a primary purpose of this PR, then why it's not removed?

Yeah, I can appreciate this is confusing. The evolution of the review is such that this wasn't identified as the best approach to resolving the glibcxx problems. I called this out here to distinguish it from the compatibility() implementation which was just a complementary proposal in addition to this. What this turned into is statically compiling glibcxx.

P.S. all the side changes could go into another PR. it's good practice to keep PRs atomic and focused on one thing at time. it's easier to review/approve changes if the do only one thing.

The side changes only became necessary in this PR to get the pipeline working again. Honestly though, given how long it takes to go through a review cycle I'm loathe to split it out as I want to get these changes in. If team review was more responsive id be more inclined to submit more atomic PRs

Copy link
Contributor

Choose a reason for hiding this comment

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

the best approach I could recommend is to create multiple PRs, each one for its own set of changes, e.g.:

  • PR 1 - solution 1 (remove del self.info.settings.compiler)
  • PR 2 - solution 2 (add compatibility() method)
  • PR 3 - solution 3 (link libstdc++ statically)
  • PR 4 - side changes (e.g. libiconv changes)

then it would be much more easier to read and review every solution. right now, I am sorry to say, but PR is a bit chaotic and hard to review, as PR title, comments in code and comments in review do not match each other, confusing reviewers on what's actually happening here (like comment here says "X" is the main thing in PR, but PR doesn't include "X", and instead includes some unrelated "Y" and "Z", which is mind-blowing).

@samuel-emrys
Copy link
Contributor Author

@jcar87 could you take a look at this please? Relates to our discussion in #17034

@conan-center-bot

This comment has been minimized.

@jcar87
Copy link
Contributor

jcar87 commented Jul 11, 2023

Hi @samuel-emrys - thank you for raising this PR and for your detailed investigation and explanation.

From what I can see, there are two issues that are highly related but not exactly the same:

  1. The removal of the compiler setting in the package_id() method, can cause Conan to retrieve a binary package that is not compatible with the current machine, if that binary was compiled against a version of libstdc++ that is newer than what the system supports. That version of libstdc++ is tracked by the version of the compiler, so removing the compiler will not result in a missing binary, but might result in a runtime error on some systems.

  2. The fact that doxygen (an executable or set of executables, for all intents and purposes) has dependencies on libraries, and that the Conan Center defaults (both the recipes and CI) mean that these will be consumed in their static variants (shared=False). The way static libraries work in Conan 2.0 mean that they will affect the package ID of the consumer - with the intention that if the dependencies change in any way, this causes a rebuild of the package that consumes it. In essence, this works against the removal of the setting altogether. While the intention is that a package built with say, gcc 7 can be consumed with a profile running gcc 11, this is not the case, as the embedded package IDs of the dependencies will be different in those scenarios - so this is basically as if the package_id() of doxygen wasn't removing the compiler setting anyway.

Here there are multiple solutions to consider. For the (1) problem, ignoring (2) altogether - preventing the deletion of self.info.settings.compiler from package_id() as proposed in this PR can be a good solution as that would at least cause a missing binary when using older compilers, one that can be built with --build=missing, and that would work locally. Another solution would be for us (in Conan Center) to publish a new package revision that is built with an older gcc - this would work if the package in question didn't have dependencies and thus (2) didn't come into play.

For (1), what we have done in other recipes, most recently in ninja, is use the static C++ runtime - you can have a look at this PR: #17620 - This is also what is done (implicitly) for CMake, which now downloads the binaries provided by KitWare, which also link the static C++ runtime.

For (2), we could probably relax the condition, and prevent the full package reference from ending up in the package info of doxygen, for example, by adding something along these lines in the package_id():

        host_deps = [dep for _, dep in self.dependencies.host.items()]
        if all(dep.package_type in ["static-library", "header-library"] for dep in host_deps):
            self.info.requires.minor_mode()

I see this is inline with what you've previously proposed - having reviewed the situation and having a better understanding within the team, this may be an acceptable change.

I think the combination of static C++ runtime and using the minor_mode() for the dependencies in the package_id() method would ensure a broader compatibility of the binary produced by Conan Center CI:

conan install --tool-requires=doxygen/1.9.4 --profile:build gcc11

^ on Linux/x86_64 this would download the binaries from the Conan Center remote, and they would run, assuming the gcc11 runtime is in the system

For any other version of gcc in the profile, e.g.:

conan install --tool-requires=doxygen/1.9.4 --profile:build gcc12

This would fail with missing binaries, as you point out above. With the changes I'm proposing:

conan install --tool-requires=doxygen/1.9.4 --profile:build gccX

it would always retrieve same package ID from conan Center, regardless of version of gcc, and it would always run on versions that have even older gcc, provided the glibc version is at least the one used by Conan Center (we still target an older glibc in Conan Center, so this should be fine).

@jcar87 jcar87 self-assigned this Jul 11, 2023
@samuel-emrys
Copy link
Contributor Author

  1. The removal of the compiler setting in the package_id() method, can cause Conan to retrieve a binary package that is not compatible with the current machine, if that binary was compiled against a version of libstdc++ that is newer than what the system supports. That version of libstdc++ is tracked by the version of the compiler, so removing the compiler will not result in a missing binary, but might result in a runtime error on some systems.

Yes, exactly.

  1. The fact that doxygen (an executable or set of executables, for all intents and purposes) has dependencies on libraries, and that the Conan Center defaults (both the recipes and CI) mean that these will be consumed in their static variants (shared=False). The way static libraries work in Conan 2.0 mean that they will affect the package ID of the consumer - with the intention that if the dependencies change in any way, this causes a rebuild of the package that consumes it. In essence, this works against the removal of the setting altogether. While the intention is that a package built with say, gcc 7 can be consumed with a profile running gcc 11, this is not the case, as the embedded package IDs of the dependencies will be different in those scenarios - so this is basically as if the package_id() of doxygen wasn't removing the compiler setting anyway.

Yes, this might be true as well. I think your reasoning here makes sense, but it's not what I've observed. The observation I made impacted the ability for the compatibility() method I've defined to work the way it should, but I can see how it might also impact the compiler erasure.

To address your proposed solution:

Static C++ runtime + relax CCI requirements on package_id mode + retain del self.info.settings.compiler.

Pros:

  • This would solve the problem I'm talking about

Cons:

  • Enforces static C++ runtime for all executables. This wouldn't be necessary if you adopt compatibility() instead
  • Throws away information in the package_id, which could make it more difficult to apply a more complete ABI compatibility definition globally later
  • Removes user choice about how to build the package because the tradeoffs force the user to build the package with your decisions. I guess this is my big objection to this approach.

The solution I'm proposing:

Relax CCI requirements on package_id mode + remove del self.info.settings.compiler + define a compatibility() function to describe ABI compatibility

Pros:

  • A much closer to truth representation of ABI compatibility
  • Retains all package metadata
  • No requirement to use static c++ runtime
  • Widely useful because it provides the user with choice about how they might manage binaries in the absence of a pre-built from CCI (they could choose to statically compile c++ runtime in, or use a dynamic c++ runtime without issues)

Cons:

  • How would this interact with a global definition in compatibility.py? Does the local conanfile implementation take precedence? What if I want to later add a more granular ABI compatibility definition? Do I then need to modify every recipe that defines a local compatibility() method? I'm not sure what the answers to these questions are.
  • This is a useful but limited ABI compatibility representation, which means that despite being the more flexible method doesn't provide as good a representation as the erasure method you're proposing. It won't identify that doxygen as built with clang and libstdc++ is compatible with a profile specifying gcc and libstdc++, but not compatible with a clang/libc++ profile, as an example.

Notes on package_id mode

From the documentation:

Modes / Variables name version user channel package_id RREV PREV
minor_mode() Yes Yes (e.g., 1.2.Z+b102) No No No No No
full_version_mode() Yes Yes (e.g., 1.2.3+b102) No No No No No

The difference between these two appears to be mostly whether you're assuming that the library uses semver or not. I wasn't able to identify a versioning strategy for doxygen, so would full_version_mode() be more appropriate here?

Anyway, happy to proceed either way - both solve my issue, but my preference would be for what's in this PR. It would also be good to establish a practice across CCI for this kind of thing so that there's broad awareness of the strategy.

@jcar87
Copy link
Contributor

jcar87 commented Jul 11, 2023

I think your reasoning here makes sense, but it's not what I've observed.

This is interesting! Any information you could provide for us to check, would be greatly appreciated.

Enforces static C++ runtime for all executables.
I think this would be an opt-in for us as maintainers, rather than a statement that this is to be enforced for all executables. It is a way of saying: as maintainer, I know this is consumed purely as an executable and that the command line interface (or other interfaces) of that executable are relevant where the version of the compiler use to build this specific tool is less relevant.

Throws away information in the package_id, which could make it more difficult to apply a more complete ABI compatibility definition globally later

The first part is correct - it throws away information in the package_id, but I'd say this is intentional as explained above, based on the knowledge that for this particular case, these are less relevant: our goal here is to broaden the scenarios in which a single package id can satisfy the requirement, such that consumers don't have to build it from source when they use newer versions of compilers. This saves time, and a new version of a compiler may not add any value to the executable. For something consumed as an executable application, the version of the compiler si no longer as relevant as for a library (where the version of the compiler may play a part in ABI compatibility)

The second statement I think it wouldn't be as accurate. On the one hand, the compatibility of the runtimes (glibc, libstdc++) is external to the recipe, and with the compatibility.py feature in Conan 2.0, we're trying to move away from specifying these in the recipes themselves. On the other hand, you could probably consider the glibc, libstdc++ runtime specific to be part of the os setting - at least on Linux. A good equivalent on macOS would be the os.version, which captures the runtime compatibility. On Linux you may want information such as glibc version (os.glibc for example?) and os.glibcxx version. Linux is more complicated since the these are intrinsically related to the compiler version - especially when the compiler is gcc you are also expressing which minimum version of glibcxx you are using. If this setting were to exist, there a compatibility.py could be implemented in such a way that it tries all older versions known to be compatible with the current one. There hasn't been a breaking change in a long time, but if there was, compatibility.py would be the one place to express this. Outside of the recipe.

For what it's worth, I think the logic you are proposing by implementing the compatibility() and retaining the compiler info is a very valid way of approaching this - but if there was an interest in modelling the glibc/libsdtdc++ versions, I would consider an approach where this is a subsetting in os, as it is ultimately the OS that determines this compatibility. This is in line with previous advice conan-io/conan#2749 (comment) and https://docs.conan.io/1/extending/custom_settings.html?highlight=distros#adding-new-sub-settings.

Crucially, I think that this means that users can customize this to their environments without changes to the recipes - that is, a Conan 2.0 can customize both the os setting for Linux, as well as compatibility.py - and then this would work without any further changes in most recipes. For example, one could create a package for an executable, and erase the compiler, but retain os.glibc_version and os.glibcxx_version (hypothetically), and rely on compatibility.py when consuming this on a different system. In that instance, however, the embedding of the full package ID of the dependencies is still a problem - as it would force a specific compiler version as well. Hence I think that relaxing that was the right solution all along, as you initially suggested.

@jcar87
Copy link
Contributor

jcar87 commented Jul 11, 2023

One additional consideration, and an argument against a compatibility() method as proposed in this PR, is Clang on Linux.

When we use clang on Linux with GNU's libstdc++, e.g:

os=Linux
compiler=clang
compiler.libcxx=libstdc++11

Clang will, by default, use the most recent gcc toolchain it finds on the system, as per the docs: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gcc-toolchain (adding -v to the compiler invocation will show which gcc toolchain was selected)

This can create an interesting situation. I can use, say, clang-13 on a Linux distro with gcc-12, and the same clang-13 on a Linux distro that has gcc-8. Both would map to the same package ID, but one of them will be built against GLIBCXX_3.4.25, and the other against GLIBCXX_3.4.30 - and this will determine which systems it can run on. In this case, compatibility() doesn't help - for starters, we are using a single compiler with a single version. So the proposed logic works well when the compiler is gcc, because it so happens that the gcc version used also captures the version of the runtime library. Hence what I think if were to fully reflect this in the settings, I think there's an aspect that belongs in the compiler+version, and another aspect that belongs in the OS. There are also other aspects not mentioned here, but for libraries, the version of binutils also influences which relocations are used - it is possible to generate libraries with newer binutils that a linker from an older binutils cannot work with - even if all other aspects are considered compatible. This is unique to Linux - and there comes a point where the complexities may not be worth the effort of modelling them. The facilities are there for Conan users who wish to do so - but for Conan Center we are choosing the path of either building these applications with the oldest possible version of gcc on a distro with the oldest possible version of glibc (within reason), or to statically link stdlibc++ to avoid this dependency. Both of these are longstanding approaches to this very problem.

@conan-center-bot

This comment has been minimized.

1 similar comment
@conan-center-bot

This comment has been minimized.

@conan-center-bot

This comment has been minimized.

@conan-center-bot

This comment has been minimized.

@conan-center-bot

This comment has been minimized.

@samuel-emrys samuel-emrys changed the title [doxygen] Remove self.info.settings.compiler deletion from package_id [doxygen] Statically compile libc to remove host system dependency Aug 26, 2023
@samuel-emrys samuel-emrys changed the title [doxygen] Statically compile libc to remove host system dependency [doxygen] Statically compile libc/libstdc++ to remove host system dependency Aug 26, 2023
@conan-center-bot

This comment has been minimized.

@conan-center-bot

This comment has been minimized.

@conan-center-bot

This comment has been minimized.

Conan 1.x doesn't have the necessary SettingsItem.get_definition() to
query settings.yml, so guard it to enable a conan 2.0
implementation
…mpatibility

* Use possible_values() to retrieve the total set of possible compiler
  versions rather than the deprecated/private get_definition for
  compatibility with conan > 2.0.5
* Remove package_id modification for CCI compatibility.
@samuel-emrys samuel-emrys force-pushed the recipe/17034-doxygen-libcpp-compatibility branch from 4c61f7e to a7ca12a Compare December 30, 2023 15:04
@conan-center-bot
Copy link
Collaborator

Conan v1 pipeline ❌

Failure in build 9 (a7ca12a55fc408494468265b976438b1c520c8d9):

  • doxygen/1.9.4:
    Didn't run or was cancelled before finishing

  • doxygen/1.9.1:
    Didn't run or was cancelled before finishing

  • doxygen/1.9.2:
    Didn't run or was cancelled before finishing

  • doxygen/1.8.18:
    Didn't run or was cancelled before finishing

  • doxygen/1.8.17:
    Didn't run or was cancelled before finishing

  • doxygen/1.8.20:
    CI failed to create some packages (All logs)

    Logs for packageID 9429036dabacaa8f8ba28926d76ad7371147514e:
    [settings]
    arch=x86_64
    build_type=Debug
    compiler=clang
    compiler.libcxx=libstdc++
    compiler.version=12
    os=Linux
    
    [...]
    Downloading conanmanifest.txt
    Downloading conaninfo.txt
    Downloading conan_package.tgz
    util-linux-libuuid/2.39: Package installed 9387155579217a8eec37b28e121e419c4350656c
    util-linux-libuuid/2.39: Downloaded package revision 3317f2d5e435e5acbdb95daccc2ad740
    zlib/1.3: Retrieving package 9387155579217a8eec37b28e121e419c4350656c from remote 'conan-center' 
    Downloading conanmanifest.txt
    Downloading conaninfo.txt
    Downloading conan_package.tgz
    zlib/1.3: Package installed 9387155579217a8eec37b28e121e419c4350656c
    zlib/1.3: Downloaded package revision 28311445c0aaf1aeee301faa0561baeb
    xapian-core/1.4.19: Retrieving package 82a3566c3c5d5b70ad1a48fd367f1cba0a987996 from remote 'conan-center' 
    Downloading conanmanifest.txt
    Downloading conaninfo.txt
    Downloading conan_package.tgz
    xapian-core/1.4.19: Package installed 82a3566c3c5d5b70ad1a48fd367f1cba0a987996
    xapian-core/1.4.19: Downloaded package revision 46c69f6eeec862457fc94a09d1ee8158
    doxygen/1.8.20: Retrieving package 9429036dabacaa8f8ba28926d76ad7371147514e from remote 'c3i_PR-18415' 
    Downloading conanmanifest.txt
    Downloading conaninfo.txt
    Downloading conan_package.tgz
    doxygen/1.8.20: Package installed 9429036dabacaa8f8ba28926d76ad7371147514e
    doxygen/1.8.20: Downloaded package revision 14c629af4ca4e66fb9a2886d56e463c5
    doxygen/1.8.20 (test package): Applying build-requirement: doxygen/1.8.20
    doxygen/1.8.20 (test package): Applying build-requirement: xapian-core/1.4.19
    doxygen/1.8.20 (test package): Applying build-requirement: libiconv/1.17
    doxygen/1.8.20 (test package): Applying build-requirement: zlib/1.3
    doxygen/1.8.20 (test package): Applying build-requirement: util-linux-libuuid/2.39
    doxygen/1.8.20 (test package): Generator txt created conanbuildinfo.txt
    doxygen/1.8.20 (test package): Generator 'VirtualBuildEnv' calling 'generate()'
    doxygen/1.8.20 (test package): Aggregating env generators
    doxygen/1.8.20 (test package): Generated conaninfo.txt
    doxygen/1.8.20 (test package): Generated graphinfo
    Using lockfile: '/home/conan/w/prod-v1/bsr/cci-c191f976/recipes/doxygen/all/test_package/build/c1639ecc46419afdfebd8c59670e230324381512/conan.lock'
    Using cached profile from lockfile
    [HOOK - conan-center.py] pre_build(): [FPIC MANAGEMENT (KB-H007)] 'fPIC' option not found
    [HOOK - conan-center.py] pre_build(): [FPIC MANAGEMENT (KB-H007)] OK
    doxygen/1.8.20 (test package): Calling build()
    doxygen/1.8.20 (test package): Running test()
    doxygen/1.8.20 (test package): Doxygen Version:
    
    ----Running------
    > . "/home/conan/w/prod-v1/bsr/cci-c191f976/recipes/doxygen/all/test_package/build/c1639ecc46419afdfebd8c59670e230324381512/conanbuild.sh" && doxygen --version
    -----------------
    doxygen: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by doxygen)
    WARN: xapian-core/1.4.19: requirement zlib/[>=1.2.11 <2] overridden by doxygen/1.8.20 to zlib/1.3 
    doxygen/1.8.20 (test package): WARN: This conanfile has no build step
    ERROR: doxygen/1.8.20 (test package): Error in test() method, line 16
    	self.run("doxygen --version")
    	ConanException: Error 1 while executing doxygen --version
    

Note: To save resources, CI tries to finish as soon as an error is found. For this reason you might find that not all the references have been launched or not all the configurations for a given reference. Also, take into account that we cannot guarantee the order of execution as it depends on CI workload and workers availability.

@samuel-emrys
Copy link
Contributor Author

Notes for myself for future debugging:

  • The existing compatibility() definition doesn't make Debug and Release profiles equal. To illustate, the following are not equivalent:
conan install --requires doxygen/1.8.20 -s:h build_type=Release
conan install --requires doxygen/1.8.20 -s:h build_type=Debug

These are not equivalent because the build type applies to the entire dependency tree, and the dependencies influence the package id of doxygen. With the existing compatibility method, the following are equivalent:

conan install --requires doxygen/1.8.20 -s:h build_type=Release
conan install --requires doxygen/1.8.20 -s:h build_type=Release -s:h "doxygen/*:build_type=Debug"

This means that we expect to see 1x Release build and 1x Debug build in the c3i jobs as they currently stand.

  • The Debug build for clang 12 is failing with the following error:
doxygen: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by doxygen)

This indicates glibc is not being compiled statically. Looking at the log, it's matching the prebuilt doxygen/1.8.20:9429036dabacaa8f8ba28926d76ad7371147514e, which is the Debug profile build with gcc 9, using libstdc++11. I've confirmed that conanio/clang12-ubuntu16.04 image contains glibc 2.23, as does conanio/gcc9-ubuntu16.04. Is it possible that the gcc9 image being used isn'y ubuntu16.04? even the ubuntu18.04 image uses GLIBC_2.27, so i'm not sure where the GLIBC_2.29 requirement would come from..

  • Revising the gcc link options documentation still seems to indicate that all that is required to link statically against glibc is -static-libgcc -static-libstdc++, so I'm not sure I understand why we have unresolved glibc symbols given that they should be contained within the doxygen binary. We could also add -static here, but I'm not sure that's the best choice to make since this seems to mean that the compiler/linker should preference any static libraries where available, which might interfere with the ability to have any dynamic dependencies in the dependency tree should the user want to do that.
  • The missing GLIBC symbols seem to be coming from libm? does that mean this should also be statically linked?
  • Inspecting the release executable built in the conanio/clang12-ubuntu16.04 image with ldd yields the following:
conan@2b5b3358e38c:~/conan-center-index/recipes/doxygen/all$ ldd -v /home/conan/.conan2/p/b/doxyg57fad1a642f0c/p/bin/doxygen
        linux-vdso.so.1 =>  (0x00007fff235ef000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd600d0b000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fd600a02000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd600638000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd600f28000)

        Version information:
        /home/conan/.conan2/p/b/doxyg57fad1a642f0c/p/bin/doxygen:
                libpthread.so.0 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libpthread.so.0
                libpthread.so.0 (GLIBC_2.3.2) => /lib/x86_64-linux-gnu/libpthread.so.0
                libm.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libm.so.6
                libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_2.3) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_2.7) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_2.17) => /lib/x86_64-linux-gnu/libc.so.6
                ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2
        /lib/x86_64-linux-gnu/libpthread.so.0:
                ld-linux-x86-64.so.2 (GLIBC_2.2.5) => /lib64/ld-linux-x86-64.so.2
                ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
                libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_2.3.2) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_PRIVATE) => /lib/x86_64-linux-gnu/libc.so.6
        /lib/x86_64-linux-gnu/libm.so.6:
                ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2
                libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
                libc.so.6 (GLIBC_PRIVATE) => /lib/x86_64-linux-gnu/libc.so.6
        /lib/x86_64-linux-gnu/libc.so.6:
                ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2
                ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2

which also indicates that libcis being linked dynamically rather than statically - further evidence that -static-libgcc and -static-libstdc++ are insufficient to statically link glibc, though also supports the fact that libgcc and libstdc++ are sucessfully being linked statically.

  • Adding -static leads to a bunch of link errors with libpthread:
Scanning dependencies of target doxysearch.cgi
[ 30%] Building CXX object addon/doxysearch/CMakeFiles/doxysearch.cgi.dir/doxysearch.cpp.o
[ 30%] Building CXX object qtools/CMakeFiles/qtools.dir/qfileinfo.cpp.o
[ 30%] [FLEX][fortrancode] Building scanner with flex 2.6.4
[ 31%] Building CXX object src/CMakeFiles/doxycfg.dir/__/generated_src/configoptions.cpp.o
[ 32%] Building CXX object qtools/CMakeFiles/qtools.dir/qgarray.cpp.o
[ 32%] Building CXX object qtools/CMakeFiles/qtools.dir/qgcache.cpp.o
[ 32%] Building CXX object src/CMakeFiles/doxycfg.dir/__/generated_src/configvalues.cpp.o
[ 33%] [FLEX][fortranscanner] Building scanner with flex 2.6.4
[ 33%] Building CXX object qtools/CMakeFiles/qtools.dir/qgdict.cpp.o
[ 33%] Linking CXX executable ../../bin/doxysearch.cgi
ld: error: undefined symbol: pthread_once
>>> referenced by timer_create.o:(__timer_create_new) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced by locale.o:(std::locale::facet::_S_get_c_locale()) in archive /usr/local/bin/../lib/gcc/x86_64-linux-gnu/10.3.0/../../../../lib64/libstdc++.a
>>> referenced by locale_init.o:(std::locale::_S_initialize()) in archive /usr/local/bin/../lib/gcc/x86_64-linux-gnu/10.3.0/../../../../lib64/libstdc++.a
>>> referenced 1 more times

ld: error: undefined symbol: pthread_attr_init
>>> referenced by timer_create.o:(__timer_create_new) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced by timer_routines.o:(__start_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a

ld: error: undefined symbol: pthread_attr_setdetachstate
>>> referenced by timer_create.o:(__timer_create_new) in archive /usr/lib/x86_64-linux-gnu/librt.a

ld: error: undefined symbol: pthread_mutex_lock
>>> referenced by timer_create.o:(__timer_create_new) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced by timer_delete.o:(__timer_delete_new) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced by timer_routines.o:(timer_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced 15 more times

ld: error: undefined symbol: pthread_mutex_unlock
>>> referenced by timer_create.o:(__timer_create_new) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced by timer_delete.o:(__timer_delete_new) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced by timer_routines.o:(timer_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced 19 more times

ld: error: undefined symbol: pthread_exit
>>> referenced by timer_routines.o:(timer_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a

ld: error: undefined symbol: pthread_create
>>> referenced by timer_routines.o:(timer_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a
>>> referenced by timer_routines.o:(__start_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a

ld: error: undefined symbol: __pthread_get_minstack
>>> referenced by timer_routines.o:(__start_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a

ld: error: undefined symbol: pthread_attr_setstacksize
>>> referenced by timer_routines.o:(__start_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a

ld: error: undefined symbol: pthread_attr_destroy
>>> referenced by timer_routines.o:(__start_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a

ld: error: undefined symbol: pthread_atfork
>>> referenced by timer_routines.o:(__start_helper_thread) in archive /usr/lib/x86_64-linux-gnu/librt.a
clang-12: error: linker command failed with exit code 1 (use -v to see invocation)
addon/doxysearch/CMakeFiles/doxysearch.cgi.dir/build.make:86: recipe for target 'bin/doxysearch.cgi' failed
make[2]: *** [bin/doxysearch.cgi] Error 1
CMakeFiles/Makefile2:735: recipe for target 'addon/doxysearch/CMakeFiles/doxysearch.cgi.dir/all' failed
make[1]: *** [addon/doxysearch/CMakeFiles/doxysearch.cgi.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
  • There's some discussion here about the pitfalls of attempting to link glibc into the program statically, which touches on compilcations when using iconv, which doxygen makes explicit use of.
  • Adding -Wl,-Bstatic -libc -Wl,-Bdynamic to the link flags yields the following error:
doxygen/1.8.20: RUN: cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE="/home/conan/.conan2/p/b/doxyg8e916df145564/b/build/Release/generators/conan_toolchain.cmake" -DCMAKE_INSTALL_PREFIX="/home/conan/.conan2/p/b/doxyg8e916df145564/p" -DCMAKE_EXE_LINKER_FLAGS="-static-libstdc++ -static-libgcc -Wl,-Bstatic -libc -Wl,-Bdynamic" -DCMAKE_POLICY_DEFAULT_CMP0091="NEW" -DCMAKE_BUILD_TYPE="Release" "/home/conan/.conan2/p/b/doxyg8e916df145564/b/src"
-- Using Conan toolchain: /home/conan/.conan2/p/b/doxyg8e916df145564/b/build/Release/generators/conan_toolchain.cmake
-- Conan toolchain: C++ Standard 14 with extensions ON
-- The C compiler identification is Clang 12.0.0
-- The CXX compiler identification is Clang 12.0.0
-- Check for working C compiler: /usr/local/bin/clang
-- Check for working C compiler: /usr/local/bin/clang -- broken
CMake Error at /usr/share/cmake-3.15/Modules/CMakeTestCCompiler.cmake:60 (message):
  The C compiler

    "/usr/local/bin/clang"

  is not able to compile a simple test program.

  It fails with the following output:

    Change Dir: /home/conan/.conan2/p/b/doxyg8e916df145564/b/build/Release/CMakeFiles/CMakeTmp

    Run Build Command(s):/usr/bin/make cmTC_d2204/fast && /usr/bin/make -f CMakeFiles/cmTC_d2204.dir/build.make CMakeFiles/cmTC_d2204.dir/build
    make[1]: Entering directory '/home/conan/.conan2/p/b/doxyg8e916df145564/b/build/Release/CMakeFiles/CMakeTmp'
    Building C object CMakeFiles/cmTC_d2204.dir/testCCompiler.c.o
    /usr/local/bin/clang   -m64    -o CMakeFiles/cmTC_d2204.dir/testCCompiler.c.o   -c /home/conan/.conan2/p/b/doxyg8e916df145564/b/build/Release/CMakeFiles/CMakeTmp/testCCompiler.c
    Linking C executable cmTC_d2204
    /usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_d2204.dir/link.txt --verbose=1
    /usr/local/bin/clang -m64   -static-libstdc++ -static-libgcc -Wl,-Bstatic -libc -Wl,-Bdynamic  -rdynamic CMakeFiles/cmTC_d2204.dir/testCCompiler.c.o  -o cmTC_d2204
    clang-12: warning: argument unused during compilation: '-static-libstdc++' [-Wunused-command-line-argument]
    ld: error: unable to find library -libc
    clang-12: error: linker command failed with exit code 1 (use -v to see invocation)
    CMakeFiles/cmTC_d2204.dir/build.make:86: recipe for target 'cmTC_d2204' failed
    make[1]: *** [cmTC_d2204] Error 1
    make[1]: Leaving directory '/home/conan/.conan2/p/b/doxyg8e916df145564/b/build/Release/CMakeFiles/CMakeTmp'
    Makefile:121: recipe for target 'cmTC_d2204/fast' failed
    make: *** [cmTC_d2204/fast] Error 2





  CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
  CMakeLists.txt:15 (project)

This indicates that libc.a can't be found, despite it existing in /usr/lib/x86_64-linux-gnu/libc.a on the conanio/clang12-ubuntu16.04 image. This is potentially the same issue associated with passing the -static option, as libpthread.a is also in the same location: /usr/lib/x86_64-linux-gnu/libpthread.a

@samuel-emrys
Copy link
Contributor Author

@jcar87 @uilianries @RubenRBS if any of you have any guidance you can provide on how you've statically compiled glibc in in the past, that would be appreciated. I had a quick look through the CCI recipes and nothing that I'm not already doing jumped out at me.

@samuel-emrys
Copy link
Contributor Author

The other element to this is whether we should even try to statically link libc in this case - as above, there are dangers in doing so, and the original issue of GLIBCXX compatibility is solved by statically linking libstdc++. Given the approach that conan-center-index takes to manage glibc compatibility, I'm confused as to why this is an issue at all - shouldn't all of these tests be built with ubuntu 18.04?

Ubuntu 18.04 has a glibc version of 2.27, so where is the glibc 2.29 requirement coming from?

@samuel-emrys
Copy link
Contributor Author

@jcar87 @uilianries @RubenRBS any help would be appreciated. I fear this is an infra issue and I have no insight into infra config

@AbrilRBS
Copy link
Member

AbrilRBS commented Jan 12, 2024

@samuel-emrys thanks for the ping! I have scheduled a review with Uilian on monday so we can both look into this, thanks a lot for your patience :)

@samuel-emrys
Copy link
Contributor Author

To be clear about the issue here, given I realise my comment above (to track my debugging) was a bit meandering:

  • This PR attempts to statically compile libgcc and libstdc++ because there are GLIBCXX incompatibilities when using doxygen (built with gcc11 on cci) on earlier images (i.e. gcc10). I seem to have successfully navigated this issue.
  • Now that we have a statically compiled libstdc++, there are libc errors because it was seemingly compiled against GLIBC_2.29.
  • I'm not sure where GLIBC_2.29 has come from, because most of the conan images use much earlier versions. If the earlier versions had been used we wouldn't be seeing this problem.

Is the solution to make sure that doxygen is rebuilt with an earlier GLIBC version? How can we ensure that that will be the case?

Copy link
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale label Mar 29, 2024
@samuel-emrys
Copy link
Contributor Author

@RubenRBS @uilianries any ideas here? How did your discussion go?

@github-actions github-actions bot removed the stale label Apr 4, 2024
Copy link
Contributor

github-actions bot commented May 5, 2024

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale label May 5, 2024
@samuel-emrys
Copy link
Contributor Author

@RubenRBS reminder to take a look at this when you get a chance

@samuel-emrys
Copy link
Contributor Author

@RubenRBS @jcar87 @uilianries any guidance on how to progress this would be appreciated.

Copy link
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale label Jul 18, 2024
Copy link
Contributor

This pull request has been automatically closed because it has not had recent activity. Thank you for your contributions.

@github-actions github-actions bot closed this Aug 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants