3

My C++ project doggo has a doggo/external/ directory for third-party code. Currently it contains gtest and a CMakeLists.txt:

# Google gtest for unit testing.
add_subdirectory(gtest)
message("gtest include dir: ${gtest_SOURCE_DIR}")
include_directories(${gtest_SOURCE_DIR})

My top-level doggo/CMakeLists.txt contains the line add_subdirectory(external) to find and build the third-party libraries. Everything works like a charm -- I can include gtest with #include <gtest/gtest.h>. Now I'd like to add the randomkit C library to doggo/external/, as is done here: randomkit from numpy.

How can I get randomkit to build in my doggo/external/ dir? What should the doggo/external/CMakeLists.txt look like?

I should then be able to include the C headers for use in my x.cpp files by including the headers inside an extern "C" { ... } block (details here).

UPDATE: How do I install randomkit here? I've included a CMakeLists.txt entry like that above but for randomkit, and the directory looks like,

external
├── CMakeLists.txt
├── gtest
│   └── ...
└── randomkit
    ├── CMakeLists.txt
    ├── distributions.c
    ├── distributions.h
    ├── randomkit.c
    └── randomkit.h

and the randomkit/CMakeLists.txt:

project(randomkit)
file(GLOB SOURCES "*.c")
add_library(randomkit SHARED ${SOURCES})
INSTALL(
    DIRECTORY ${CMAKE_SOURCE_DIR}/
    DESTINATION "/usr/local/"
    #DESTINATION ""
    FILES_MATCHING PATTERN "*.h*")

(second DESTINATION commented out to show I tried that as well)

Yet when I run the build steps for my top-level project doggo I get an error trying to #include <randomkit/distributions.h>:

doggo/src/random_fooz.cpp:10:37: fatal error: randomkit/distributions.h: No such file or directory

UPDATE 2: doggo/CMakeLists.txt:

project(doggo)
# Find and build third-party libraries
add_subdirectory(external)
# Add source dirs to the search path so cmake can find headers
include_directories(${CMAKE_SOURCE_DIR}/include/)
# Collect source files and build
file(GLOB_RECURSE doggo_srcs ${CMAKE_SOURCE_DIR}/src/*.cpp)
add_library(doggo ${doggo_srcs})
# Setup executables
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/)
add_subdirectory(exec)
# Tests
add_subdirectory(test)
5
  • A couple of questions: (1) Do you have the gtest code in a sub-sub-directory doggo/external/gtest and you're adding doggo/external/randomkit as another such directory? (2) Is your question primarily about cmake directory structure and CMakeLists.txt file content, or about the mechanics of using C with C++? The answer to Q1 is mostly about practicalities; having two or more independent projects in a single directory would be anarchy. If the answer to Q2 is that you're worried about the mechanics of CMake, then the duplicate is not helpful (it is incorrect). Commented Apr 27, 2018 at 15:08
  • (1) Yes. (2) The former. Having several third-parties in an external/ subdirectory is not uncommon, although I agree it should be limited to < three and only simple libraries, and probably avoided for a project that is to be scaled and platform-ind. So doggo is a personal project where I much prefer this structure w/ external/<third-party-lib>. I can additionally get the C++ library matplotpp to build just as easily as gtest here. It seems a small, simple C library like randomkit should be easy too, but it needs its own CMakeLists.txt. Commented Apr 27, 2018 at 15:30
  • It is recommended to incorporate gtest using ExternalProject cf github.com/google/googletest/tree/master/… Commented Apr 27, 2018 at 16:15
  • @JonathanLeffler I've updated the question to include a CMakeLists.txt for the third-party randomkit project. Please see if you can help! Commented Apr 30, 2018 at 22:10
  • I can spell cmake — I've not really learned it. I'm sorry, I can't reliably help you. Commented May 1, 2018 at 0:06

1 Answer 1

0

In the randomkit/CMakeLists.txt write:

project(randomkit)
file(GLOB SOURCES "*.c")
add_library(randomkit SHARED ${SOURCES})
target_include_directories(randomkit PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
INSTALL(
    DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
    DESTINATION "include"            # this a the subdirectory with ${CMAKE_INSTALL_PREFIX}
    FILES_MATCHING PATTERN "*.h*")

In the main CMakeLists.txt, you do:

add_library(doggo ${doggo_srcs})
target_link_libraries(doggo PUBLIC randomkit)
target_include_directories(doggo PUBLIC ${CMAKE_SOURCE_DIR}/include/)

Don’t use include_directories.

Now, because the randomkit target has the PUBLIC property with the right include directories, those include directories will be automatically used when building the doggo library. And again, because the doggo library has include directories and libraries in its public interface, executables that you link to doggo will automatically be linked to these libraries, and find their include files.

Note that the INSTALL command in randomkit/CMakeLists.txt is only executed when you actually run the install target. When building, the include files must be found in the source tree.

Sign up to request clarification or add additional context in comments.

8 Comments

1. doggo/CMakeLists.txt shouldn't have to link to every third-party library in doggo/external, just use add_subdirectory(external) to get to doggo/external/CMakeLists.txt. This works for gtest -- gtest's CMakeLists.txt doesn't know about doggo's, and vice-versa.
2. doggo/CMakeLists.txt sets up executables w/ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/) and add_subdirectory(exec). Then there's an exec/CMakeLists.txt that finds the executables and calls add_executable(${executable} ${local_src}) then target_link_libraries(${executable} doggo ${doggo_LIBRARIES}). For now I actually don't have any executables to run, just some unit tests in doggo/test/.
Note that you can do the same with header-only libraries: you can make a fake target and set its include path, do when you link to this target you get the right include directories set.
See Update2 above with main CMakeLists.txt.
Still fails w/ fatal error: randomkit/distributions.h: No such file or directory
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.