Build of multiple subprojects with library that has external dependency - c++

I'm struggling with an project that includes CMake (version 3.11) and subdirectories. It includes ITK as an external library.
I want to create library that I can use in the different subdirectories like MyApp to build multiple applications that are based on MyLib and every change in MyLib is updated in the other applications. The MyLib is a header-only templated library. In addition this kind of libraries do not get a own project in VisualStudio.
My questions are:
What is the correct way of using CMake in this scenario? I have searched and tried some example but was not successful. Ideally I can build applications depending on the MyLib independently. The top CMakeLists.txt may not pull in the includes from the library. This should be done by the applications.
What would change if the MyLib becomes a non-header-only library? In the future I may add non templated classes.
Bonus question:
How can I add the header-only library to VisualStudio? This is more a question for convencience.
Any help is appreciated and I can provide more details if necessary.
The source environment looks like this:
ProjectDirectory
- CMakeLists.txt
-- MyLib
- CMakeLists.txt
-- include
- File1.h (template header)
- File1.hxx (template body)
-- src
- (empty for now)
-- MyApp
- CMakeLists.txt
- AppFile1.cxx (File containing main function)
This is an out-of-source-build project. Ideally the compiled libraries and application are put in a directory like this (on Windows):
└── bin
├── Debug
│ ├── MyApp.exe
│ └── MyLib.lib
└── Release
├── MyApp.exe
└── MyLib.lib
ProjectDirectory CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(project_name)
# Setup build locations.
if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endif()
if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endif()
add_subdirectory(MyLib)
add_subdirectory(MyApp)
MyLib CMakeLists.txt:
find_package(ITK REQUIRED
COMPONENTS RTK ITKImageIO)
include(${ITK_USE_FILE})
set(HDRS
${CMAKE_CURRENT_SOURCE_DIR}/include/File1.h
${CMAKE_CURRENT_SOURCE_DIR}/include/File1.hxx
)
add_library(MyLib ${HDRS})
target_include_directories(MyLib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
src)
target_link_libraries(MyLib
PUBLIC ${ITK_LIBRARIES})
export(TARGETS MyLib FILE MyLibConfig.cmake)
MyApp CMakeLists.txt
find_package(ITK REQUIRED
COMPONENTS RTK ITKRegistrationMethodsv4 ITKTransformIO ITKImageIO)
include(${ITK_USE_FILE})
set(SRCS
AppFile1.cxx)
add_executable(MyApp ${SRCS})
include_directories(${CMAKE_SOURCE_DIR}/MyLib/include)
target_link_libraries(MyApp
${ITK_LIBRARIES}
MyLib)
Edit1: Remove the project(X) from MyLib and MyApp CMakeLists.txt and change target to MyLib.

You can have an empty project, just create a custom target:
add_custom_target(MyLib SOURCES ${CMAKE_MYLIB_SRC} ${CMAKE_MYLIB_HDR})
As your code is header only, any depend target will see the changes in the files and would be recompiled. There is nothing to do on top of this. You don't have to create the target, although for your third question, that's the answer.
But if ITK is only your dependency, now that you have a target, you can add PUBLIC properties on it, like dependent libraries that need to be linked against it because of your library.
In that case, the code for them needs to add:
target_link_library(${NEWTARGET} PRIVATE MyLib) # PUBLIC/PRIVATE has to be tailored
That's the answer for question 1.
If your library becomes non-header only, just change the add_custom_target call.

Related

Export custom library which uses Qt via CMake for use in another CMake project (Windows, Mingw-w64)

A bit of background on what I am trying to achieve: I already have a project that I have developed in CMake (it is collection of CMake projects: basically a state machine with a few helper libraries). Now, I want to develop a GUI in Qt that this state machine can control (and make it do stuff). However, for the life of me, I cannot figure out how to properly export the library which has the Qt classes and functions to another CMake package's executable. I am on Windows, and using the Mingw-w64 compiler and make program.
I was experimenting with 2 simple CMake projects, one which has a simple Qt form (which I just copied from and modified this repository and the corresponding youtube video given in the README), and the other which has the executable. I was trying to link the first project with the second project, however, whenever I build the autogenerated makefile, it fails in the end, saying that symbols are undefined (For example undefined reference to run_stuff::run_stuff()'). I apologize if the files are dirty, I have been trying out various stuff to no avail.
Things I have tried:
I have followed the answer here very carefully.
I have tried setting CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS to ON, as suggested by many sites (like this one) for running DLLs
I have tried making the library static (hence doing away with the need for DLLs)
I have tried setting the target_include_dirs from private to public and also commenting out that line. (not sure what that does, but okay)
However, no matter what I do, I always end with linking errors in the end, and my executable in the second project is not able to access any functions from the Qt project. Any suggestions on what I should try next? Would be really helpful if you could point out errors I am making in my CMakeLists.txt files (once again apologies for the messy code and CMakeLists)
My project CMake file looks like this:
cmake_minimum_required(VERSION 3.14)
# if (WIN32)
# set(CMAKE_SHARED_LIBRARY_PREFIX "")
# endif ()
set(CMAKE_MAKE_PROGRAM $ENV{MAKE})
set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "" FORCE)
set(CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(WINDOWS_EXPORT_ALL_SYMBOLS ON)
add_subdirectory(QT6CMake)
add_subdirectory(Exec)
The library CMake file:
cmake_minimum_required(VERSION 3.14)
if (WIN32)
project(MY_PROJECT LANGUAGES CXX)
elseif(UNIX)
project(MY_PROJECT)
endif()
set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "" FORCE)
#======================= INCLUSION OF Qt =======================#
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_PREFIX_PATH $ENV{QTDIR})
find_package(Qt6Core REQUIRED)
find_package(Qt6Widgets REQUIRED)
#=================== INCLUSION OF Project Files ====================#
set(FORMS_DIR "${CMAKE_SOURCE_DIR}/forms")
set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
set(SOURCE_DIR "${CMAKE_SOURCE_DIR}/src")
include_directories(${FORMS_DIR})
include_directories(${INCLUDE_DIR})
include_directories(${SOURCE_DIR})
file(GLOB_RECURSE SOURCES
"${FORMS_DIR}/*.ui"
"${FORMS_DIR}/*.qrc"
"${INCLUDE_DIR}/*.h"
"${SOURCE_DIR}/*.cpp"
)
#=================== SETUP EXECTUABLE ====================#
# Enable debug logging on RELWITHDEBINFO configuration
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS
$<$<CONFIG:RELWITHDEBINFO>:QT_MESSAGELOGCONTEXT>
)
# Add the forms directory to the AUTOUIC search paths
set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS} ${FORMS_DIR})
# Add the executable
if (WIN32)
add_executable(MY_PROJECT WIN32 ${SOURCES})
elseif(UNIX)
add_executable(MY_PROJECT ${SOURCES})
endif()
# Add the target includes for MY_PROJECT
target_include_directories(MY_PROJECT PRIVATE ${FORMS_DIR})
target_include_directories(MY_PROJECT PRIVATE ${INCLUDE_DIR})
target_include_directories(MY_PROJECT PRIVATE ${SOURCE_DIR})
#===================== LINKING LIBRARIES =======================#
target_link_libraries(MY_PROJECT Qt6::Widgets)
The Exec CMake file:
cmake_minimum_required(VERSION 3.14)
project(somexec)
add_definitions(${MY_PROJECT_DEFINITIONS})
include_directories(${MY_PROJECT_INCLUDE_DIRS})
if (WIN32)
add_executable(${PROJECT_NAME} WIN32 main.cpp)
elseif(UNIX)
add_executable(${PROJECT_NAME} main.cpp)
endif()
target_link_libraries(${PROJECT_NAME} MY_PROJECT)
Here is the codebase.
EDIT:
Finally the code seems to be working, courtesy of Guillaume Racicot's multiple edits and fixes. I am going to leave this repository public in case anyone wants to see the codebase. Also, right now, I don't understand all the CMake commands that he has used, will try to understand those as well
The CMake code from the tutorial uses very old fashioned and outdated CMake practices. It works when creating a simple project, but won't work when doing libraries. To share files between projects, you need to export the CMake targets. You can do that by creating this file first:
cmake/yourlibrary-config.cmake
include(CMakeFindDependencyMacro)
# write it like you find_package of your cmake scripts
find_dependency(Qt6 COMPONENTS Core Widgets REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/yourlibrary-targets.cmake")
Then, add this to your main project cmake files. You should have a CMake file that looks like this:
cmake_minimum_required(VERSION 3.21)
project(yourlibrary CXX)
# First, create your library. List all the files one by one. Don't use globs
add_library(yourlibrary src/mainwindow.cpp)
# Then add an alias of your library to enable target syntax
add_library(yourlibrary::yourlibrary ALIAS yourlibrary)
# Automatically generate Qt ui and moc
set_target_properties(yourlibrary PROPERTIES
AUTOUIC ON
AUTOMOC ON
AUTOUIC_SEARCH_PATHS forms
)
# Then, include gnuinstalldir to get the platform's standard directories:
include(GNUInstallDirs)
# Then, carefully add your include directories. All of your `target_include_directories` must look like this
target_include_directories(yourlibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # include directory in your build tree
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> # include directory when installed
)
# Then, include Qt and link qt
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)
target_link_libraries(yourlibrary PUBLIC Qt6::Core Qt6::Widgets)
# Now, create the install script. We install all header files under `include/yourlibrary` to install them in `<prefix>/include/yourlibrary`:
install(
DIRECTORY include/yourlibrary
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h"
)
# We add `yourlibrary` target into the export set.
# The export set will contain all targets to be imported by the other project.
# It also installs the library to the install script so they are installed:
install(TARGETS yourlibrary EXPORT yourlibrary-targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
# Now, we install the export set. This will generate a CMake file exporting all the target for other projects to use:
install(EXPORT yourlibrary-targets
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yourlibrary"
NAMESPACE yourlibrary::
)
# Now, we also export the current buildtree. Other project will be able to import the project directly from a build dir:
configure_file(cmake/yourlibrary-config.cmake yourlibrary-config.cmake COPYONLY)
export(
EXPORT yourlibrary-targets
NAMESPACE yourlibrary::
FILE "${PROJECT_BINARY_DIR}/yourlibrary-targets.cmake"
)
# The file we created earlier:
install(
FILES cmake/yourlibrary-config.cmake
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yourlibrary"
)
I omitted the installation of the generated headers since they are usually considered private for the project.
For the library, have a source tree looking like this:
yourlibrary
├── cmake
│   └── yourlibrary-config.cmake
├── forms
│   └── mainwindow.ui
├── include
│   └── yourlibrary
│   └── mainwindow.h
├── src
│ └── mainwindow.cpp
└── CMakeLists.txt
To use the library, you have two choices.
Either you embed it in your project using add_subdirectory(yourlibrary)
Either you build it separately and use find_package(yourlibrary REQUIRED)
You can't do both.
I usually prefer using find_package, but it require a package manager to avoid manually building all dependencies.
If you use add_subdirectory
Then remove find_package(yourlibrary REQUIRED) from your project. Simply add the directory and have your main project file look like this:
cmake_minimum_required(VERSION 3.21)
project(exec LANGUAGES CXX)
# Embed the project
add_subdirectory(yourlibrary)
add_executable(myexec main.cpp)
target_link_libraries(myexec PUBLIC yourlibrary::yourlibrary)
I assume a project tree like this:
SampleProject
├── yourlibrary
│ └── ...
├── CMakeLists.txt
└── main.cpp
If you build yourlibrary separately
First, build the library and (optionally) install it in a known location.
Now, you can build your library that uses Qt. You will need a CMakeLists.txt that looks like this:
cmake_minimum_required(VERSION 3.21)
project(myexec LANGUAGES CXX)
# also finds Qt and all
find_package(yourlibrary REQUIRED)
add_executable(myexec main.cpp)
target_link_libraries(myexec PUBLIC yourlibrary::yourlibrary)
If you install the yourlibrary library, it will simply work. If you want to use it from its build tree, simply run the CMake command with -DCMAKE_PREFIX_PATH=/path/to/yourlibrary/build. It should point to the build directory of the library, or the installation prefix if you installed it in a custom location.

"No such file or directory" when linking shared library with CMake [duplicate]

This question already has answers here:
How to properly add include directories with CMake
(12 answers)
CMake link to external library
(6 answers)
Closed 1 year ago.
I have am trying to link a precompiled so file to my executable in cmake. I'm not sure if I am misunderstanding how to use a shared library or not but I assumed that once I compile a shared library, any application can link to it and use it's functions. When compiling the executable, I get this error:
logger.h: No such file or directory
#include "logger.h"
^~~~~~~~~~
compilation terminated.
Here is my cmake file for the executable
cmake_minimum_required(VERSION 2.8)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# set PROJECT home directory
set(PROJECT_HOME "/opt/PROJECT")
# set the project name
project(ClServer)
# add the executable
add_executable(ClServer server.cpp)
add_library(logger SHARED IMPORTED libClLogger.so)
message ("Here")
set_target_properties(logger
PROPERTIES IMPORTED_LOCATION "#{PROJECT_HOME}/base"
)
target_link_libraries(ClServer
${Logger}
)
I've also tried using find_library and adding an absolute path to the shared library using target_link_libraries, but the same error occurs.
Here is my cmake file for the logger shared library:
cmake_minimum_required(VERSION 2.8)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# set Clamor home directory
set(PROJECT_HOME "/opt/PROJECT")
# set the project name
project(ClLogger)
# set boost paths
set(Boost_NO_SYSTEM_PATHS TRUE)
if (Boost_NO_SYSTEM_PATHS)
set(BOOST_ROOT "${PROJECT_HOME}/ext/Boost/boost_1_76_0")
set(BOOST_LIBRARYDIR "${PROJECT_HOME}/ext/Boost/boost_1_76_0/stage/lib")
set(BOOST_INCLUDEDIR "${PROJECT_HOME}/ext/Boost/boost_1_76_0")
endif (Boost_NO_SYSTEM_PATHS)
# provides the linker with the appropriate directories and components
find_package(Boost 1.76.0 COMPONENTS log log_setup thread filesystem system)
if(Boost_FOUND)
include_directories(${BOOST_INCLUDEDIR})
link_directories(${BOOST_LIBRARYDIR})
endif()
message (STATUS Boost_LIBRARIES:)
message (STATUS ${Boost_LIBRARIES})
message (STATUS BOOST_INCLUDEDIR:)
message (STATUS ${BOOST_INCLUDEDIR})
# add PROJECT source code libraries
add_library(ClLogger SHARED logger.cpp)
# link the appropriate libraries
target_link_libraries(ClLogger
libpthread.so.0
${Boost_LIBRARIES}
)
# redirect binaries to base folder
set_target_properties(ClLogger
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_HOME}/base"
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_HOME}/base"
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_HOME}/base"
)
To consume a shared library, you need two things. First, shared library file and second public header file. Header file will get used in compilation stage and shared library file in linking stage. Error you are seeing is a compilation failure which says it failed to find header file "logger.h". Use target_include_directories in cmake of your executable to tell cmake where to look for header logger.h.
Additionally if you want cmake to keep watch on logger.h modification, you can add logger.h as source file of your executable.
After a bit of research and testing in my local machine,
i have updated my answer with the simple example tested on eclipse CDT and cmake in WSL2.(should work on any other environment fine ).
the following example should provide an answer to the question.
cmake_test/
├── CMakeLists.txt
├── build
├── cmake_test.cpp
└── **companyLibrary**
├── CMakeLists.txt
├── build
├── include
│   └── Company.h
└── src
└── Company.cpp
from the above project hierarchy companyLibrary is the one which generates the .so (shared Library), which will be used by cmake_test.cpp source file which is at the root level of the project.
cmake_test/companyLibrary/CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (companyLibrary_test)
set(CMAKE_BUILD_TYPE Release)
#adding the header <.h> files into the project
include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_library(testCompany SHARED ${SOURCES})
install(TARGETS testCompany DESTINATION /usr/lib)
this generates the .so file in this example (libtestCompany.so)
sudo make install
this will put .so file in /usr/lib folder
now this shared library will be used by the root project cmake_test.cpp file
cmake_test/CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (cmake_test)
set ( PROJECT_LINK_LIBS libtestCompany.so )
link_directories( "/usr/lib" )
include_directories( "companyLibrary/include" )
add_executable( libtest cmake_test.cpp )
target_link_libraries( libtest ${PROJECT_LINK_LIBS} )
add_executable and target_link_libraries should solve the problem
as show above.
full source code availabe here on GitHub.
hope it helps :)

How to include external modules headers when FetchContent_Declare is used?

I want to use modern CMake project structure convention in my new project to follow good practices and to be consistent with many other projects, but I'm facing problem with include path.
My directories structure looks as follows:
.
├── build
├── CMakeLists.txt
├── extern
│ └── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
I want to use FetchContent_Declare instead of git submodules to manage the third-party libraries.
The root CMakeLists.txt:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(ProjectName "MyApp")
project(${ProjectName})
add_subdirectory(extern)
add_subdirectory(src)
The extern CMakeLists.txt:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
include(FetchContent)
FetchContent_Declare(
extern_spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.8.2)
FetchContent_GetProperties(extern_spdlog)
if(NOT extern_spdlog_POPULATED)
FetchContent_Populate(extern_spdlog)
add_subdirectory(
${extern_spdlog_SOURCE_DIR}
${extern_spdlog_BINARY_DIR}
)
endif()
The src CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
set(BINARY ${CMAKE_PROJECT_NAME})
file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h *.cpp)
set(SOURCES ${SOURCES})
add_executable(${BINARY}_run ${SOURCES})
add_library(${BINARY}_lib STATIC ${SOURCES})
target_link_libraries(${BINARY}_run extern_spdlog)
And the main.cpp:
#include <spdlog/spdlog.h>
int main(int argc, char* argv[]) {
spdlog::info("Welcome to spdlog!");
return 0;
}
CMake executes and the spdlog library is cloned properly and can be found in the build directory at: build/_deps/extern_spdlog-src.
If I call make in the build directory then I get fatal error: spdlog/spdlog.h: No such file or directory.
How to add include directories for the external libraries used in the application?
I was initially confused about this as well. It doesn't help that the names of the FetchContent targets often match the names of the imported targets. What worked for me was similar (though not identical) to what the first comment states. I used target_link_libraries(target spdlog::spdlog) (if you want to use the pre-compiled library or spdlog::spdlog_header_only if you want to use the header source only version.

Integrating GTest with existing CMake Project: share the same target_sources

I have a large C++ library, and want to do some testing with GTest.
At the moment, the build is handled with CMake, in particular there is one CMakeLists.txt file in the root directory like the following
make_minimum_required(VERSION 3.13.0)
project(mylib)
find_package(PkgConfig REQUIRED)
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
set(CMAKE_INSTALL_RPATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/protobuf/src/.libs/")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
SET(BASEPATH "${CMAKE_SOURCE_DIR}")
INCLUDE_DIRECTORIES("${BASEPATH}")
add_executable(mylib run.cpp)
add_subdirectory(src)
add_subdirectory(proto)
target_include_directories(mylib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/lib/math
${CMAKE_CURRENT_SOURCE_DIR}/lib/protobuf/src
... some dirs ...
)
target_link_directories(mylib PRIVATE
... some libs ..
)
target_link_libraries(mylib
${CMAKE_CURRENT_SOURCE_DIR}/lib/math
${CMAKE_CURRENT_SOURCE_DIR}/lib/protobuf/src
.....
)
target_compile_options(mylib PUBLIC -D_REENTRANT -fPIC)
Then in the src directory and every sub-directory there is a CMakeLists.txt file, for example this is in src/
target_sources(mylib
PUBLIC
includes.hpp
)
add_subdirectory(algorithms)
add_subdirectory(collectors)
add_subdirectory(hierarchies)
add_subdirectory(mixings)
add_subdirectory(runtime)
add_subdirectory(utils)
My question here is the following: what is the least painful way to integrate GTest in the current project? I was thinking of having a test/ subdirectory, like I've seen here: Adding Googletest To Existing CMake Project
However this example requires that for each executable you manually list all the files it includes. Is there a quicker way to use the sources that are already added to 'mylib'?
You can split the current mylib executable target into two targets
mylib, a library target that is very much like the current mylib target, but without the run.cpp file
mylib_exe an executable target that compiles run.cpp and links to mylib
Now your test files can link to mylib.

CMakeLists.txt for third-party C files within C++ project

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)
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.