BUILD_SHARED_LIBS

As far as I know it’s best practice to not specify SHARED or STATIC for a library, but rely on BUILD_SHARED_LIBS instead.

I have to questions regarding this.

  1. Should one specify BUILD_SHARED_LIBS as an option? Up to know I never did. But some Libraries do this and when using FetchContent BUILD_SHARED_LIBS will be set to that default value as it is by default empty → This lead to nasty bugs as some libs where build static, then one lib had option(BUILD_SHARED_LIBS ON) and set it therefore to On. All a second run then all Libraries where build shared.
    → Is it wrong from the libraries to declare the option?
    → Should I declare in my project the option, so BUILD_SHARED_LIB always has a value? → It will then not be overwritten, right?

  2. Often Libraries delcare my-library and my-library-shared, so the just have two targets for both static and dynamic. This is often done so they can pass set some different values depending on whether it’s shared or not. But I think it’s also done to make packaging and building from source easier.
    → Is this bad practice? → It makes my life harder to keep track which libraries use BUILD_SHARED_LIBS and which have two targets
    → What are arguments against it? How can be argued against the “pro” arguments → I believe the different logic could just be done with if(BUILD_SHARED_LIBS), so that’s no argument. What about the “ease of use” argument?

Thanks,
Leon

It’s fine to declare it as an option if you want to change the default. As long as the minimum required CMake version is 3.13, a parent project can override it with a normal variable.

There is always a danger from FetchContent / add_subdirectory that the child project will set a cache variable that affects global state. It is always a requirement to audit the code before including it, so you know what it does.

I think having two separate targets for static and shared is an absolutely terrible idea and I wish people would stop doing it. So much do I feel this way, I wrote a blog post about it:

3 Likes

Thanks for your answer! Especially the detailed blog post, which I will use in the future to convience library authors :smiley:

@alex Execuse me. I have some questions to ask after reading your IMPRESSIVE blog article.

In the article, you said that it is better to use find_package()'s COMPONENTS mechanism to specify whether to use shared or static version of a library. Am I correct?

find_package(SomeLib REQUIRED shared)  # or `static`

Then, what about the following libraries: Qt, Poco,…etc? Since those well-developed libraries already use COMPONENTS mechanism to provide their Component Imported Targets. Take Qt for example:

find_package(Qt6 CONFIG REQUIRED
  Core Widget Gui Network)

target_link_libraries(myExe 
  PRIVATE
    Qt6::Core
    Qt6::Widget
    Qt6::Gui
    Qt6::Network)

What about this kind of situation? Do you mean that it is recommended for Qt to provide additional two Component Imported Targets, shared and static. For example:

  • If I want to use static version:

    find_package(Qt6 CONFIG REQUIRED
      static Core Widget Gui Network)
    
  • If I want to use shared version:

    find_package(Qt6 CONFIG REQUIRED
      shared Core Widget Gui Network)
    

Like this?

If the key point is to “leave the decision to find_package(), how about request CMake to add some new arguments for find_package() like: STATIC and SHARED? (Just like add_library() did.)