5

I have a directory with c++ source and header files. I want to create a CMakeLists.txt to build this as a library for use in other CMake projects that include it as a sub directory.

Structure:

example/
    foo.h
    foo.cpp
    CMakeLists.txt

The problem I run into is CMake doesn't seem to put foo.h anywhere, so getting the parent CMake to know how to find the header file is beguiling me.

Here's my current CMakeLists.txt:

cmake_minimum_required(VERSION 3.8.2)
project(example)
set (CMAKE_CXX_STANDARD 11)

# add library target foo
add_library(foo STATIC foo.cpp)

# tell cmake where to find headers for it
target_include_directories(foo PUBLIC .)

# sad attempt to get it to output the header
set_target_properties(foo PROPERTIES PUBLIC_HEADER foo.h)

I DON'T want to have to do install. The idea here is that the library would be used by other CMake projects, not by the entire system.

Ideally, the foo.h would show up next to libfoo.a in the build directory.

I've tried calling it a "FRAMEWORK", no luck; that only makes is a macOs framework.

I believe I can jury rig this, but methinks there's a best practice out there.

Open to an answer that says "here's a better way", too...

UPDATE

It might help to clarify how I think I want to pull this project into another. I've seen other projects use something like this:

add_subdirectory(<path_to_foo>/foo foo_build)

which causes the foo build to happen in a subdirectory. This allows me to refer to the library using 'foo_build', which is nice and clean. However, I still have to point at the original include directory to get the .h file, which makes me feel like I'm missing something.

It seems like cmake would have a clean solution for this.

Jim B.
  • 4,512
  • 3
  • 25
  • 53
  • 1
    note: To *force* C++11 you also need [CXX_STANDARD_REQUIRED](https://cmake.org/cmake/help/latest/prop_tgt/CXX_STANDARD_REQUIRED.html#prop_tgt:CXX_STANDARD_REQUIRED) Also better to add it as a target properties to forward this requirement if needed... – Mizux Jan 02 '18 at 12:41

4 Answers4

4

I am fairly new to CMake but what I think you want is a 'add_custom_command'.

add_custom_command(TARGET foo.a POST_BUILD COMMAND copy foo.h ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})

That might work.

natersoz
  • 1,674
  • 2
  • 19
  • 29
3

What you are looking for is the following structure:

example/
- CMakeLists.txt
- src/
  - main.c
- sub/
  - foo/
    - CMakeLists.txt
    - src/
      - foo/
        - foo.c
        - foo.h

Your CMakeLists will look like the following

example/CMakeLists.txt

# use modern target-based cmake features
cmake_minimum_required (VERSION 3.0)

# projectname
project (ff1_selfcheck)

add_subdirectory (sub/foo)

# executable to create
add_executable(${PROJECT_NAME}
    src/main.c
)

# link libraries
target_link_libraries(${PROJECT_NAME}
    PRIVATE
        foo # imported target
)

example/sub/foo/CMakeLists.txt

# use modern target-based cmake features
cmake_minimum_required (VERSION 3.0)

# projectname
project (foo)

# executable to create
add_library(${PROJECT_NAME}
    src/foo.c
)

# directories where to search for header files
target_include_directories(${PROJECT_NAME}
    PUBLIC
        source # the headerfiles in source are the includes
)

By using the project name foo in target_link_libraries(...) you refer to the foo library target

Furthermore, by using the PUBLIC keyword in the foo library, your headers (your include directory) is automatically propagated to every CMake project that adds this library via add_subdirectory(...).

Therefore you don't need to copy your headers! CMake >= 2.8.12 is beautiful, isn't it?


If you really want to copy files via CMake, the following would work:

file(COPY srcDir
    DESTINATION dstDir
    FILES_MATCHING
        PATTERN "*.h"
)

Take a look here: https://cmake.org/cmake/help/latest/command/file.html#copy

Andreu Gimenez
  • 413
  • 5
  • 9
ataraxis
  • 1,257
  • 1
  • 15
  • 30
  • Thanks for the last part! I believe that's the response to the question title even if you stated a better solution for the OP – Andreu Gimenez Mar 16 '23 at 08:22
1

As a general rule for CMake, sources are kept in the source directory and binaries and other generated files are within the build directory. So you wish is not very CMake-ish.
CMake would put headers and libraries according to your wishes when you install the project. Then you can specify what to copy where.

As you don't want to install this module, the best way is to create a package by providing a CMake config file for your project. This means that your project Foo would generate a file FooConfig.cmake which contains the paths to includes and libraries. The other CMake project would use find_package(Foo) to look for the file. By adding a hint to Foo_DIR you can make CMake find your project in a non-standard directory.

Further reading:

Note, that configure_file is unrelated to what you wish, the confusing name has historic reasons. You can use this command, but per se it is unrelated.

UPDATE: after the update, I think that you want to use an external project. Behaves like an internal library, but pretty separated. See https://cmake.org/cmake/help/latest/module/ExternalProject.html

usr1234567
  • 21,601
  • 16
  • 108
  • 128
  • Thanks, this is very helpful. I updated the original post to clarify what I think I'm looking for, and it definitely should be cmake-y. I'm not just trying to get a quick fix; this is so I understand better. – Jim B. Jan 02 '18 at 17:27
  • @JimBaldwin I. Updated my answer accordingly. – usr1234567 Jan 02 '18 at 18:15
0

you should use generator expression for your "foo" include directory:

target_include_directories(foo PUBLIC 
    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR})

And since you don't want install rules not need to also add a $<INSTALL_INTERFACE:include>...

BTW you should don't care to copy the include file in the build directory (supposing you are building out of the source).

ps: if you also generate headers files simply add $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>

Zoe
  • 27,060
  • 21
  • 118
  • 148
Mizux
  • 8,222
  • 7
  • 32
  • 48