# static and dynamic libraries, single compile

**URL:** <https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855>\
**Category:** Code\
**Created:** [February 28, 2021, 5:07am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855 "2021-02-28T05:07:22Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jay\_K](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/8491ac/32.png) [@Jay\_K](https://discourse.cmake.org/u/Jay_K)\
**Post date:** [February 28, 2021, 5:07am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/1 "2021-02-28T05:07:22Z")

</div>

I am **very** new to cmake.

I am **not** new to build systems.  
I have maintained signficant ones.

I have a very custom build system.  
Very declarative, and embeds a little custom scripting language as an escape hatch.

Nice but design, but that I would rather stop maintaining. At least the underlying  
platform support. I envision keeping the declarative stuff  
and generating cmake files from it. Or maybe translating  
once to idiomatic maintainable cmake.

However it has at least one very nice simple useful  
feature that I have seen rarely or never elsewhere,  
and I would like to know how to achieve it in cmake (or automake/libtool,  
for better or worse, I want to delegate the work to others).

We compile everything just once, with -fPIC.  
I find the approach of compiling things twice quite absurd, really.

For any “library” by default we make a static library and  
link a shared library. We name the static libraries like foo.a.sa  
or foo.lib.sa. (I forget what we do on HP-UX, where .sa means shared archive;  
we have very broad platform support also).

If a library however is marked as “build\_standalone”, we only build  
the .sa form.

When we link an executable, by default, we probe around for the libraries  
it requests. If there is dynamic we use that, else if there is static we use that.

If an executable is marked “standalone”, we look only for static,  
which always succeeds.

The standalone libraries have another use, I only just realized.  
The system is its own language. We have to write “bindings”, like rewriting  
the X headers in our language, and such. Then we have like:

source(xwhateverbindings)  
if not HasX11  
build\_standalone()  
end  
library(“x11bindings”)

The result of this, the same, compile, make static archive, and do not  
attempt shared link. In this fashion, we can still compile our bindings  
to check them somewhat, w/o error due to lack of underlying X11.  
i.e. because we’d have unresolved symbols, depending (I know shared  
libraries can sometimes have unresolved symbols).

The users of the library, granted:  
if HasX11  
use\_library(“x11bindings”)  
program(“guiapp”)  
end

so no checking on executable source we cannot link.

Does my explanation make sense?

Is this possible in cmake? How?  
If no way, maybe a reasonable feature request?

I do find the cmake documenation a bit obtuse, I’m sorry to say.  
It speaks of higher level concepts.

Thank you very much,

- Jay

---

<div class="post-metadata">

**Author:** ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)\
**Post date:** [February 28, 2021, 7:36am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/2 "2021-02-28T07:36:26Z")

</div>

I do not understand right.

`Modern cmake` use the concept of build `targets`.  
One `target` may depend on other `targets`.  
…

see [It’s Time To Do CMake Right | Pablo Arias](https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/)

---

<div class="post-metadata">

**Author:** ![Jay\_K](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/8491ac/32.png) [@Jay\_K](https://discourse.cmake.org/u/Jay_K)\
**Post date:** [February 28, 2021, 1:58pm UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/3 "2021-02-28T13:58:40Z")

</div>

That is informative and shines a very positive light on CMake, thank you.

I had some typos, I apologize, but my explanation wasn’t far off.

We do _like_ this:

# By default, make library foo with source foo.c bar.c

---

<div class="post-metadata">

**Author:** ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)\
**Post date:** [February 28, 2021, 3:34pm UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/4 "2021-02-28T15:34:48Z")

</div>

OK, a simple modern `CMakeLists.txt`:

```auto
cmake_minimum_required(VERSION 3.14...3.20)

# ---- Project ----

# Note: update this to your new project's name and version
project(
  Observe
  VERSION 3.0
  LANGUAGES CXX
)

# ---- Include guards ----

if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
  message(
    FATAL_ERROR
      "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there."
  )
endif()

# --- Import tools ----

include(cmake/tools.cmake)

# ---- Add dependencies via CPM ----
# see https://github.com/TheLartians/CPM.cmake for more info

include(cmake/CPM.cmake)

# PackageProject.cmake will be used to make our target installable
CPMAddPackage(
  NAME PackageProject.cmake
  GITHUB_REPOSITORY TheLartians/PackageProject.cmake
  VERSION 1.5.1
)

# ---- Add source files ----
# Note: globbing sources is considered bad practice as CMake's generators may not detect new files
# automatically. Keep that in mind when changing files, or explicitly mention them here.
file(GLOB_RECURSE _headers CONFIGURE_DEPENDS
     "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h"
)

# ---- Create library ----

add_library(Observe INTERFACE)
target_compile_features(Observe INTERFACE cxx_std_17)

# beeing a cross-platform target, we enforce enforce standards conformance on MSVC
target_compile_options(
  Observe INTERFACE "$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/permissive>"
)

target_include_directories(
  Observe
  INTERFACE "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
            "$<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>"
)

# for run-clang-tidy
add_executable(example example/example.cpp)
target_link_libraries(example Observe)

# ---- Create an installable target ----
# this allows users to install and find the library via `find_package()`.

packageProject(
  NAME ${PROJECT_NAME}
  VERSION ${PROJECT_VERSION}
  BINARY_DIR ${PROJECT_BINARY_DIR}
  INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include
  INCLUDE_DESTINATION include/${PROJECT_NAME}-${PROJECT_VERSION}
  DEPENDENCIES ""
)

```

This creates the following after `make install`:

```bash
tree stage/
stage/
├── include
│ └── Observe-3.0
│ └── observe
│ ├── event.h
│ ├── observer.h
│ └── value.h
└── lib
    └── cmake
        └── Observe-3.0
            ├── ObserveConfig.cmake
            ├── ObserveConfigVersion.cmake
            └── ObserveTargets.cmake

6 directories, 6 files

```

clone the project from here [GitHub - ClausKlein/Observe: 📣 Hey listen! A simple general-purpose event-listener system for C++17.](https://github.com/ClausKlein/Observe)

---

<div class="post-metadata">

**Author:** ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)\
**Post date:** [February 28, 2021, 3:53pm UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/5 "2021-02-28T15:53:47Z")

</div>

A sample how to build and install `shared libraries` you may try this project:

> **[ClausKlein/ModernCppStarter](https://github.com/ClausKlein/ModernCppStarter)**
>
> 🚀 Kick-start your C++! A template for modern C++ projects using CMake, CI, code coverage, clang-format, reproducible dependency management and much more.

---

<div class="post-metadata">

**Author:** ![Jay\_K](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/8491ac/32.png) [@Jay\_K](https://discourse.cmake.org/u/Jay_K)\
**Post date:** [February 28, 2021, 6:03pm UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/6 "2021-02-28T18:03:27Z")

</div>

That doesn’t answer my question.

But ok I looked into things some. I hadn’t looked in years I guess.

Apparently this is a faq: build static and dynamic libraries.  
It isn’t fully automated, but compile once is easy enough:

add\_library(l1-standalone STATIC a.cpp b.cpp)  
add\_library(l1-shared SHARED $\<TARGET\_OBJECTS:l1-standalone\>)  
set\_property(TARGET l1-standalone PROPERTY POSITION\_INDEPENDENT\_CODE 1)

And then if I chose the names right, building selectively  
just standalone and letting probing happen, should work.

i.e. default:  
add\_library(l1-standalone STATIC a.cpp b.cpp)  
add\_library(l1 SHARED $\<TARGET\_OBJECTS:l1-standalone\>)  
set\_property(TARGET l1-standalone PROPERTY POSITION\_INDEPENDENT\_CODE 1)

standalone:  
add\_library(l1 STATIC a.cpp b.cpp)  
set\_property(TARGET l1 PROPERTY POSITION\_INDEPENDENT\_CODE 1)

But this doesn’t offer though is “standalone” executable  
that favor standalone libraries. Perhaps we can drop that feature.  
rpath=$origin/…/lib offers “similar” behavior. Though we also use standalone  
so we can update libraries used by the actual build system implementation,  
i.e. to avoid overwriting in-use files. Maybe this isn’t really needed.  
And we use it to run uninstalled executables. A bit of an inefficient hack, but it is useful.  
Like for executables used during the build (we ignore the cross build problem there, granted).  
Maybe cmake creates wrapper scripts that set LD\_LIBRARY\_PATH or such for that?  
Maybe I’ll just try and see.

I had forgotten, other aspects of our build system that will need  
translation or alteration or abandonment.

We create multi-arch package stores, like:

/install/pkg1/amd64-linux/libpkg1.a  
/install/pkg1/amd64-linux/libpkg1.so  
/install/pkg1/arm64-darwin/libpkg1.a  
/install/pkg1/arm64-darwin/libpkg1.dylib  
/install/pkg2/amd64-linux/libpkg2.a  
/install/pkg2/amd64-linux/libpkg2.so  
/install/pkg2/arm64-darwin/libpkg2.a  
/install/pkg2/arm64-darwin/libpkg2.dylib

and then sym or hardlinks into bin and lib directories.

Kinda like Debian’s /lib/ but predating it by a long time.

We’ll have to think about what we need and what is worth customization.

Thank you,

- Jay

---

<div class="post-metadata">

**Author:** ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)\
**Post date:** [February 28, 2021, 8:57pm UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/7 "2021-02-28T20:57:42Z")

</div>

> [@Jay\_K](#):
>
> I do find the cmake documenation a bit obtuse, I’m sorry to say.  
> It speaks of higher level concepts.

I think you should not say, that the cmake document is `obtuse`!

Think about of your questions, that is `obtuse`.

I seems time to forget what you dit in the past.

Make it new, clear, and simple!

**There is more than one way to do it.**

I can’t help you, sorry.

---

<div class="post-metadata">

**Author:** ![erk](https://discourse.cmake.org/user_avatar/discourse.cmake.org/erk/32/114_2.png) [@erk](https://discourse.cmake.org/u/erk)\
**Post date:** [March 1, 2021, 7:40am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/8 "2021-03-01T07:40:20Z")

</div>

> [@Jay\_K](#):
>
> Apparently this is a faq: build static and dynamic libraries.  
> It isn’t fully automated, but compile once is easy enough:
> 
> add\_library(l1-standalone STATIC a.cpp b.cpp)  
> add\_library(l1-shared SHARED $\<TARGET\_OBJECTS:l1-standalone\>)  
> set\_property(TARGET l1-standalone PROPERTY POSITION\_INDEPENDENT\_CODE 1)

From my understanding I think you’d rather use OBJECT library:  
[https://cmake.org/cmake/help/latest/command/add\_library.html#object-libraries](https://cmake.org/cmake/help/latest/command/add_library.html#object-libraries)

```cmake
add_library(objlib OBJECT a.cpp b.cpp)
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
add_library(l1-static STATIC)
target_link_libraries(l1-static objlib)
add_library(l1-shared SHARED)
target_link_libraries(l1-shared objlib)
add_executable(standalone-exec)
target_link_libraries(standalone-exec objlib)

```

cf:  
[https://cmake.org/cmake/help/latest/command/target\_link\_libraries.html#linking-object-libraries](https://cmake.org/cmake/help/latest/command/target_link_libraries.html#linking-object-libraries)

---

<div class="post-metadata">

**Author:** ![erk](https://discourse.cmake.org/user_avatar/discourse.cmake.org/erk/32/114_2.png) [@erk](https://discourse.cmake.org/u/erk)\
**Post date:** [March 1, 2021, 7:43am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/9 "2021-03-01T07:43:47Z")

</div>

> [@Jay\_K](#):
>
> I had forgotten, other aspects of our build system that will need  
> translation or alteration or abandonment.
> 
> We create multi-arch package stores, like:

I think you should separate the subject of your question. I understand that your current build system has various feature but:

1. as @ClausKlein said you should try the cmake-way of doing thing first not trying to translate _line-by-line_
2. ask relatively standalone question for each feature you seek for your build system so that it’s easier to answer without having to cope with the whole complexity.

---

<div class="post-metadata">

**Author:** ![Jay\_K](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/8491ac/32.png) [@Jay\_K](https://discourse.cmake.org/u/Jay_K)\
**Post date:** [March 1, 2021, 9:51am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/10 "2021-03-01T09:51:18Z")

</div>

I saw object libraries, but on Windows I prefer .libs over .objs, so that the linker  
will resolve and perhaps retain fewer symbols.

On Unix I guess slightly preference for object libs to avoid the cost to write the lib.

Very very similar though.

- Jay

---

<div class="post-metadata">

**Author:** ![erk](https://discourse.cmake.org/user_avatar/discourse.cmake.org/erk/32/114_2.png) [@erk](https://discourse.cmake.org/u/erk)\
**Post date:** [March 1, 2021, 10:16am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/11 "2021-03-01T10:16:23Z")

</div>

> [@Jay\_K](#):
>
> I saw object libraries, but on Windows I prefer .libs over .objs, so that the linker  
> will resolve and perhaps retain fewer symbols.

You should not assume what CMake will use for OBJECT libraries on various platform. The point of CMake is to abstract away as far as possible from platform specifics. With CMake you usually give **functional requirements** on your build.

If you want to explain what CMake should do on Windows vs Linux vs MacOS etc… then you’ll end up with a CMake build system with a lot of if(WIN32) etc… which seems awkward when you seek cross-platform build.

---

<div class="post-metadata">

**Author:** ![Jay\_K](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/8491ac/32.png) [@Jay\_K](https://discourse.cmake.org/u/Jay_K)\
**Post date:** [March 1, 2021, 11:41am UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/12 "2021-03-01T11:41:16Z")

</div>

I would just avoid object libraries everywhere, not per-platform,  
because of their varying semantic, at least on Windows (possibly everywhere?)

And sure, but what is the high level difference then?  
What are the intended observed behaviors of an object library vs. a static library?  
They sound like exact low level ideas, and do in fact map directly to the next level.

The caveat I am avoiding others have documented:

[Benefits of CMake Object Libraries | Scientific Computing | SciVision](https://www.scivision.dev/cmake-object-libraries/#:~:text=Benefits%20of%20CMake%20Object%20Libraries%2013%20February,%202020,the%20relevant%20compile%20options,%20definitions%20and%20link%20flags).

"  
Caveats  
Using object libraries means that duplicate symbols can cause conflicts that need to be  
manually resolved. When linking non-object libraries, the linker typically just uses  
the first instance seen of a symbol.  
"

- Jay

---

<div class="post-metadata">

**Author:** ![hsattler](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/h/59ef9b/32.png) [@hsattler](https://discourse.cmake.org/u/hsattler)\
**Post date:** [March 1, 2021, 7:47pm UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/13 "2021-03-01T19:47:09Z")

</div>

How would you do this on Windows to compile only once for shared and static libraries? Is that even possible due to the need for dllexport?

---

<div class="post-metadata">

**Author:** ![Jay\_K](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/j/8491ac/32.png) [@Jay\_K](https://discourse.cmake.org/u/Jay_K)\
**Post date:** [March 1, 2021, 8:25pm UTC](https://discourse.cmake.org/t/static-and-dynamic-libraries-single-compile/2855/14 "2021-03-01T20:25:18Z")

</div>

There is no need for declspec(dllexport).  
Plenty of systems do not use it.  
Use a .def file instead.

Declspec(dllimport) is also not needed.  
(Unless you import data, or compare function pointers for equality, both are rare.)

- Jay
