Home > Blockchain >  How to automatically compile linked libraries in CMake when building an executable?
How to automatically compile linked libraries in CMake when building an executable?

Time:12-29

I am given several projects (git repos) and each of them depends on a number of libraries (also git repos). Every project has just one target executable and links libraries from other repos.

My file structure:

├── libraries
│   ├── libraryX (git:master)
│   │   ├── build
│   │   │   └── libX.so
│   │   ├── CMakeLists.txt
│   │   ├── fileX.cpp
│   │   └── fileX.h
│   ├── libraryY (git:master)
│   │   ├── build
│   │   │   └── libY.so
│   │   ├── CMakeLists.txt
│   │   ├── fileY.cpp
│   │   └── fileY.h
│   └── libraryZ (git:master)
│       ├── build
│       │   └── libZ.so
│       ├── CMakeLists.txt
│       ├── fileZ.cpp
│       └── fileZ.h
└── projects
    ├── projectA (git:master)
    │   ├── build
    │   │   └── outA
    │   ├── CMakeLists.txt
    │   └── mainA.cpp
    └── projectB (git:master)
        ├── build
        │   └── outB
        ├── CMakeLists.txt
        └── mainB.cpp

This is an example CMakeLists.txt file inside libraryX:

cmake_minimum_required(VERSION 3.6)
project(libraryX LANGUAGES CXX)

set(libraries $ENV{LIBRARIES})
set(projects $ENV{PROJECTS})

add_library(libX SHARED fileX.cpp)

target_include_directories(libX PUBLIC ${libraries}/libraryX/)

Currently I am building every library manually, which generates *.so files and then I go to the project and build the executable. This is an example CMakeLists.txt from inside projectA:

cmake_minimum_required(VERSION 3.6)
project(projectA LANGUAGES CXX)

set(libraries $ENV{LIBRARIES})
set(projects $ENV{PROJECTS})

add_executable(outA mainA.cpp)

target_include_directories(outA
    PUBLIC
        ${libraries}/libraryX/
        ${libraries}/libraryY/
        ${libraries}/libraryZ/
        ${projects}/projectA/
)

target_link_libraries(outA
        ${libraries}/libraryX/build/libX.so
        ${libraries}/libraryY/build/libY.so
        ${libraries}/libraryZ/build/libZ.so
)

I am only interested in building/running one project (e.g. projectA/) at a time, but I don't want to manually precompile every library every time I make changes.

Also, I cannot move libraries/ under the project (e.g. projectA/libraries/libraryX) because projectB/ also depends on them, and I don't like having two clones of the same repository in one computer in sibling folders. I also cannot change and rearrange the content of repositories to suit myself, because different teams are working on them and plan to release them as full packages installable from apt-get in the future. Finally, I don't want to use a single top-level CMakeLists.txt inside the workspace because the projects A and B are independent from each other.

I tried looking into add_subdirectory and target_sources but could not understand how to target a source if I want the same library to be used by different executables in projectA and projectB.

Of course, I can run a bash script that builds cmakes in every library, but I thought that this is a job of CMake to do such things. I am clearly not seeing some obvious solution here.

Can anyone help me to force CMake to precompile the library before linking them to the target?

Thanks!

P.S. How can I tell CMake to compile *.dll files instead of *.so, and *.exe instead of Linux binaries?

I have read and watched a lot of tutorials and read a lot of stackoverflow answers about the architecture of the CMake projects. I tried to run the project in Visual Studio on Windows in hopes of it figuring it out itself -- didn't really help. I wrote a bash script that compiles libraries before linking them but it feels wrong, because I was promised that CMake actually does this thing for me. I also tried to use subdirectory system as mentioned in another stackoverflow answer but it did not really succeed.

CodePudding user response:

Your libraryX/CMakeLists.txt would look like this:

cmake_minimum_required(VERSION 3.6)
project(libraryX LANGUAGES CXX)

add_library(libX SHARED fileX.cpp)

target_include_directories(
    libX 
    PUBLIC 
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
  )

At the root directory of all sub-projects, you may add the CMakeLists.txt which performs add_subdirectory() for all libraries (first) and binaries (last).

Then in the projectX/CMakeLists.txt just link to CMake targets you need:

cmake_minimum_required(VERSION 3.6)
project(projectX LANGUAGES CXX)

add_executable(outA mainA.cpp)

target_include_directories(
    outA
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}
    # NOTE No need for any other paths
  )

target_link_libraries(
    outA
    PRIVATE
        libX
        libY
        libZ
  )

Update (after I spot that these are separate git repos):

You need to export your library targets (build and install 'em into the same prefix) and use find_package(libX) in your projectX/CMakeLists.txt to find a target to link with. Having separate libs is nice (compared to a monorepo ;).

  • Related