0

Previously, I'm looking for this kind of question but none of them are working as example this thread and this one.

Currently, I'm creating a C library which supposed to outputs a shared library (*.so or *.dll) file and multiple *.h files in a ./include directory. The shared library is successfully built and installed to /usr/local/lib. However, I have no idea how do I tell CMake to copy *.h files to the correct destination as below.

  • In POSIX: /usr/local/include
  • In Windows: C:\<Compiler Path>\include

I know I could workaround this by copying those *.h with Python script or using if-else logic on CMake. But at least tell me if there is a feature in CMake for doing that thing. Thanks!

Here's my CMakeLists.txt:

cmake_minimum_required(VERSION 3.12)
project (
  learn_c_library
  VERSION 1.0.0
  LANGUAGES C
)

# Specify CMake build and output directory
set(CMAKE_BINARY_DIR "${PROJECT_SOURCE_DIR}/.cmake")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/out")

# Use every .c file inside ./src
file(GLOB_RECURSE MY_SOURCE_PATH "src/*")

add_library(
  ${PROJECT_NAME} SHARED
  ${MY_SOURCE_PATH}
)
include_directories(
  ${PROJECT_NAME}
  "include/"
)

# Allows "make install" to install shared library
install(TARGETS ${PROJECT_NAME})
Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
Thor-x86_128
  • 157
  • 18
  • Can you show us what your current CMakeLists.txt looks like? Normally your CMake file defines *what* to install, and the caller defines *where* to install it, but you can override this if you really must. – Botje Aug 02 '21 at 09:03
  • 1
    [It is best practice to not use globs in CMake](https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-2.html), but if you absolutely refuse to list your sources, then at the _very, very least_ use `CONFIGURE_DEPENDS`. – Alex Reinking Aug 02 '21 at 15:16
  • 1
    Never set variable `CMAKE_BINARY_DIR`. This variable is automatically set by CMake and CMake has expectations about its value. But setting the variable you (or a person who build your project) could face with many subtle bugs. It is better to assume variable `CMAKE_BINARY_DIR` to be readonly one. – Tsyvarev Aug 02 '21 at 18:34
  • 1
    @AlexReinking wow that's the only reason why I should delete CMakeFiles directory before rebuilding. I like your blog, thanks for sharing! – Thor-x86_128 Aug 04 '21 at 08:20
  • @Tsyvarev The only reason why I added `CMAKE_BINARY_DIR` because Visual Studio Code using `build/` directory by default, but now it seems not relevant anymore. Currently I use [cmake.buildDirectory](https://github.com/microsoft/vscode-cmake-tools/blob/develop/docs/cmake-settings.md#cmake-settings) instead. – Thor-x86_128 Aug 04 '21 at 08:55

2 Answers2

2

This answer improves on your answer:

cmake_minimum_required(VERSION 3.14)

# ...

include(GNUInstallDirs)

install(DIRECTORY "include/"
        TYPE INCLUDE
        COMPONENT MyProj_Development)

The GNUInstallDirs module provides standard configuration points for customizing a project's install layout. These variables should essentially always be used instead of hardcoding path/directory names.

The TYPE INCLUDE argument to install() was introduced in CMake 3.14 and uses GNUInstallDirs's CMAKE_INSTALL_INCLUDEDIR variable to determine the destination.

Finally, and especially if your project is a library, all your install() rules should be given a COMPONENT argument to allow splitting files between runtime and development packages (and potentially others like documentation and architecture-independent data files).

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
  • It's confirmed working, but I still confused the difference between `TARGET` and `COMPONENT`. Could you explain please? – Thor-x86_128 Aug 04 '21 at 08:48
  • 1
    The `COMPONENT` argument allows you to split your package up into, well, multiple components. Have you ever seen something like `libfoo` and `libfoo-dev` and `libfoo-doc` in a Linux distribution? There's more documentation on the CMake website, here: https://cmake.org/cmake/help/latest/command/install.html – Alex Reinking Aug 04 '21 at 08:53
0

After days of fiddling CMake, finally I found the solution!

Let's assume you store your public *.h inside ./include directory, then here's the syntax for installing your *.h files to your machine:

install(
  DIRECTORY
    "include/" # Your global headers location in the project
  DESTINATION
    include # This is your system's default "include" location
)

I got the solution from here with usr/myproject replaced to be include

Thor-x86_128
  • 157
  • 18
  • You should _not_ hardcode `include`, but rather `include(GNUInstallDirs)` and use the `CMAKE_INSTALL_INCLUDEDIR` variable. See the docs: https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html If you're using recent CMake (which you should be), you can also just write `TYPE INCLUDE` instead of `DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}`. See: https://cmake.org/cmake/help/latest/command/install.html#installing-files – Alex Reinking Aug 02 '21 at 15:08