# CPack: distributing both shared and static libraries

**URL:** https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225
**Category:** Usage
**Created:** [May 16, 2020, 5:21am UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225 "2020-05-16T05:21:55Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![alex](https://discourse.cmake.org/user_avatar/discourse.cmake.org/alex/32/125_2.png) [@alex](https://discourse.cmake.org/u/alex)
#### Post date: [May 16, 2020, 5:21am UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/1 "2020-05-16T05:21:55Z")

</div>

Before switching to CMake we were able to distribute tarballs containing our library as both a static library and a shared library (at least on Linux and macOS). What is the preferred way to do this with CMake 3.16+?

---

<div class="post-metadata">

### Author: ![craig.scott](https://discourse.cmake.org/user_avatar/discourse.cmake.org/craig.scott/32/20_2.png) [@craig.scott](https://discourse.cmake.org/u/craig.scott)
#### Post date: [May 16, 2020, 10:37pm UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/2 "2020-05-16T22:37:00Z")

</div>

(Check _Section 26.3: Multi Configuration Packages_ of my book). If you are using CMake 3.16 or later, definitely look at using the `cpack -C` command line option. CMake 3.16 or later allows you to pass multiple configurations to this option. You are responsible for ensuring that each configuration has already been built when you run `cpack`. This option only really makes sense if you’re using a multi-config generator, but since there’s now the `Ninja Multi-Config` generator (since CMake 3.17), this is possible on all major platforms. It can be done with earlier CMake versions and/or single-config generators, but it is much more involved and much less intuitive.

---

<div class="post-metadata">

### Author: ![alex](https://discourse.cmake.org/user_avatar/discourse.cmake.org/alex/32/125_2.png) [@alex](https://discourse.cmake.org/u/alex)
#### Post date: [May 16, 2020, 11:04pm UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/3 "2020-05-16T23:04:53Z")

</div>

So this would entail creating custom configurations? Like “RelWithDebInfoStatic”, for example? And would our users be able to pick which to use when they run `find_package` somehow?

---

<div class="post-metadata">

### Author: ![craig.scott](https://discourse.cmake.org/user_avatar/discourse.cmake.org/craig.scott/32/20_2.png) [@craig.scott](https://discourse.cmake.org/u/craig.scott)
#### Post date: [May 17, 2020, 6:02am UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/4 "2020-05-17T06:02:03Z")

</div>

No it shouldn’t add any additional requirements unless you want builds that provide both static and shared versions of a target. That isn’t going to work out so good anyway because one target cannot have two different representations (static and shared). You would need to have two different targets in that case. One approach I’ve seen people suggest is to create `blahStatic` and `blahShared` targets, then define a `blah` interface target that links to one of those based on the value of `BUILD_SHARED_LIBS` or some similar option. Consumers should then only link against `blah` and the `blahStatic` and `blahShared` libraries are somewhat of an internal implementation detail. I say “somewhat” because it would still probably impact consumers in terms of things they might need to add to their own packages, depending on how they are incorporating your library.

---

<div class="post-metadata">

### Author: ![alex](https://discourse.cmake.org/user_avatar/discourse.cmake.org/alex/32/125_2.png) [@alex](https://discourse.cmake.org/u/alex)
#### Post date: [May 17, 2020, 6:15am UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/5 "2020-05-17T06:15:00Z")

</div>

Well, I would like to provide both, but mutually exclusively is fine. Maybe like a variable that’s set ahead of `find_package(Halide)` (in my case) that has it load the appropriate set of exports.

---

<div class="post-metadata">

### Author: ![Allen](https://discourse.cmake.org/user_avatar/discourse.cmake.org/allen/32/4767_2.png) [@Allen](https://discourse.cmake.org/u/Allen)
#### Post date: [May 18, 2020, 5:40pm UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/6 "2020-05-18T17:40:05Z")

</div>

You might want to see how [HDF5](https://www.hdfgroup.org/) did it, not perfect solution but usuable.

Allen

---

<div class="post-metadata">

### Author: ![alex](https://discourse.cmake.org/user_avatar/discourse.cmake.org/alex/32/125_2.png) [@alex](https://discourse.cmake.org/u/alex)
#### Post date: [May 19, 2020, 6:15pm UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/7 "2020-05-19T18:15:44Z")

</div>

Allen - it looks like HDF5 uses separate packages for static and shared? I was specifically asking how to avoid that. Am I missing something?

---

<div class="post-metadata">

### Author: ![Allen](https://discourse.cmake.org/user_avatar/discourse.cmake.org/allen/32/4767_2.png) [@Allen](https://discourse.cmake.org/u/Allen)
#### Post date: [May 19, 2020, 6:45pm UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/8 "2020-05-19T18:45:23Z")

</div>

No, it packages both into a single install. Only the builds are separated.  
In the case of tools, there are static () and shared (-shared).  
Libraries are static (lib.lib) and shared (.lib)

Not perfect, but workable. And the find\_package can request either or both.

Allen

---

<div class="post-metadata">

### Author: ![alex](https://discourse.cmake.org/user_avatar/discourse.cmake.org/alex/32/125_2.png) [@alex](https://discourse.cmake.org/u/alex)
#### Post date: [July 21, 2020, 10:34pm UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/9 "2020-07-21T22:34:48Z")

</div>

I still haven’t found a satisfactory solution to this, but this is what I’m planning and I’m interested to hear if anyone has any other perspectives.

@Allen – I looked into what HDF5 does, and I mostly like the structure.  
@craig.scott – I’m still unsure how to use `cpack -C` to combine, say, two Debug builds, one with `BUILD_SHARED_LIBS` set to `YES` and the other to `NO`.

So here are my collected notes:

1. When packaging for a Linux distribution / homebrew / something else, it is not acceptable to have _separate_ packages for shared and static. They have to be packaged together. No one does `sudo apt install libpng-dev-shared libpng-dev-static`. It’s just `libpng-dev`.
2. There are two ways to have separate shared/static targets:  
a. **Via a common object library.** This is a no-go, not only because object libraries never behave how you expect (they require export/install afaik) but because it requires compiling all the objects with `-fPIC`. This adds overhead that defeats the purpose of static libraries for many consumers.  
b. **Sources listed twice.** This fixes the PIC issue and is correct.
3. (2b) has the following drawbacks:  
a. Both targets are always built, even if only one is required.  
b. The `MyLib::MyLib` alias must be consistently used within the `MyLib` project and must conditionally point to one or the other target, depending on the value of `BUILD_SHARED_LIBS`.
4. The user experience should be consistent between `find_package` via vcpkg or another package manager, and `add_subdirectory` / `FetchContent`.
5. (3a) and (4) imply that it is better to have a single target and multiple build directories.
6. (4) requires that `MyLib::MyLib`'s type depends on `BUILD_SHARED_LIBS`.
7. Controlling `find_package` via `BUILD_SHARED_LIBS` is awkward (have to set and reset) and unexpected. There are a few workarounds:  
a. **(Ab)use the components system** – The user may (must?) specify either “shared” or “static” as a component, which directly determines the type.  
b. **Read a “namespaced” (cache?) variable** – The value of `MyLib_SHARED_LIBS` determines the type. This has the advantage of being command-line configurable. The right way to do this might be `option` + `mark_as_advanced`.  
c. **All of the above**. Component \> `MyLib_SHARED_LIBS` \> `BUILD_SHARED_LIBS`. If none are present, a default is chosen or an error is issued.
8. Regardless, the active configuration (ie. `Debug` or `Release`) should be respected. This means that a complete package combines four builds.
9. Regarding (8), `cpack -C "Debug;Release"` only takes me so far. Unless I’m missing something, it only helps produce a multi-config package for a _single_ `BUILD_SHARED_LIBS` setting. If I want it to additionally package both static/shared, I have to compromise on something above.

The workflow I would _like_ is to build Debug and Release via Ninja Multi-Config twice: once into a “shared” build tree and again into a “static” build tree, and then combine all four of them somehow.

---

<div class="post-metadata">

### Author: ![alex](https://discourse.cmake.org/user_avatar/discourse.cmake.org/alex/32/125_2.png) [@alex](https://discourse.cmake.org/u/alex)
#### Post date: [July 23, 2020, 11:49pm UTC](https://discourse.cmake.org/t/cpack-distributing-both-shared-and-static-libraries/1225/10 "2020-07-23T23:49:00Z")

</div>

It turns out it’s not so scary to merge a few build trees with CPack. Just create a working directory for several build trees that looks like:

```auto
build/
|- shared-Debug/
|- shared-Release/
|- static-Debug/
`- static-Release/

```

Then configure and build your project in each one, changing only `CMAKE_BUILD_TYPE` and `BUILD_SHARED_LIBS`. Write a script titled `package.cmake`, which contains:

```auto
include("shared-Release/CPackConfig.cmake")

set(CPACK_INSTALL_CMAKE_PROJECTS
    static-Debug <PROJ> ALL /
    shared-Debug <PROJ> ALL /
    static-Release <PROJ> ALL /
    shared-Release <PROJ> ALL /
)

```

Then a simple `cpack --config /path/to/package.cmake` from the `build` directory suffices. Note that this requires a single-config generator like Ninja. Make sure that you generate different targets export files for each of static and shared, then write the appropriate loading logic in your package configuration script. There, you can choose to use components, a variable, a fallback mechanism, or whatever to load the appropriate export script.
