CMake: How to prevent using wrong subdirectory CMakeLists directly? - build

For example, if I have one main CMakeLists that builds all the dependencies of project and then builds project itself via add_subdirectory( project ).
But if you try to build via project/CMakeLists you'd fail (because of missing dependencies). SO what is the best way to prevent this scenario, e.g. check if a given CMakeLists was included by the other or if it's a "root".

In the CMakeLists.txt file in the subdirectory where this is required, add:
if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
message(FATAL_ERROR "Blah blah wrong directory")
endif()

Related

How to add_dependencies between projects in cmake

include_directories(${CMAKE_CURRENT_SOURCE_DIR})
AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} source)
project(abc)
#a_certain_source_file.cpp is a generated file built by another project.
add_library(${PROJECT_NAME} STATIC ${source} ${xyz_BIN_DIR}/a_certain_source_file.cpp)
add_dependencies(${PROJECT_NAME} xyz)
target_link_libraries(${PROJECT_NAME} PRIVATE xyz)
# include xyz_SOURCE_DIR directory to include a_certain_source_file.cpp
target_include_directories(${PROJECT_NAME} PRIVATE ${xyz_SOURCE_DIR})
# Installation
install(TARGETS ${PROJECT_NAME} DESTINATION ${DST_LIB_DIR})
I have a CMakeLists.txt as above. Trying to build project abc. But to build it, I also need "a_certain_source_file.cpp" which is an auto generated source file from another project called xyz. If xyz had been built from this same CMakeLists.txt, there would have been no problem in add_dependencies working. I am unable to get the dependency on "a_certain_source_file.cpp" resolved with the way i have my CMakeLists.txt right now. Any CMake Enthusiasts or specialists that can help ?
I also saw a close match here - cmake: add_dependencies does not work with external projects but I don't need anything downloaded. So am not sure if this is what I need.
Create custom command with add_custom_command marking ${xyz_BIN_DIR}/a_certain_source_file.cpp as OUTPUT and make it depend on a xyz target. This will teach CMake that ${xyz_BIN_DIR}/a_certain_source_file.cpp is a generated file, and what should it do to generate it.

How to build only one target for dependency?

I want to build an application under Windows using CMake + Visual Studio with a lot of dependencies, such as zlib. All of them are static libraries.
I've tried ADD_SUBDIRECTORY and this works pretty well but instead of building only depending target (zlibstatic) it builds all of them.
How to remove unused targets (with their solutions) or choose only one?
Mainly I'm searching for feature to define only needed targets.
Part of my CMakeLists.txt:
ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib")
TARGET_INCLUDE_DIRECTORIES(MyProject PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib")
TARGET_LINK_LIBRARIES(MyProject zlibstatic)
I finally figured out how to do it.
MyProject
├───build <- here I call cmake
├───deps
│ └───zlib
│ └───CMakeLists.txt
├───inc
├───src
└───CMakeLists.txt
# Include project but remove all tartgets
ADD_SUBDIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib EXCLUDE_FROM_ALL)
# Use only specific one target
ADD_DEPENDENCIES(MyProject zlibstatic)
# Include dirs from zlib source directory and from output directory becuse it generates some headers
TARGET_INCLUDE_DIRECTORIES(MyProject PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/deps/zlib
${CMAKE_CURRENT_BINARY_DIR}/deps/zlib
)
# Just to create beautiful structure in project tree
SET_PROPERTY(TARGET zlibstatic PROPERTY FOLDER Deps)
# Link after all
TARGET_LINK_LIBRARIES(MyProject zlibstatic)
I suggest you use vcpkg or conan instead to resolve your dependent library issue this is much cleaner and works well except for header only libraries.
You can of cause do that manually but than you loose the nice cmake setup.

CMake post-build custom command on multiple targets?

I'm learning CMake through a C++ hobby project using Visual Studio 2017, and the way I have it set up, I have one folder for source code and one folder for test code. I build the source code as a static library, and build the test code as an executable (using Catch2). The problem I have now is that these are two separate targets, and whenever one or both of these targets are rebuilt I want to run the testing executable. Now I can find out how to run a post-build event using ADD_CUSTOM_COMMAND, but that only works for a single target. Putting multiple targets after "TARGET" leads to only the last target being used (I tested this), and duplicating the custom command can lead to the tests being run twice, and also it seems like poor code style. Is there any way to do this elegantly? My CMake file looks like this:
# CMakeList.txt : Top-level CMake project file, do global configuration
# and include sub-projects here.
#
cmake_minimum_required (VERSION 3.8)
project ("SheepyEngine")
set (CMAKE_CXX_STANDARD 17)
set (HEADER_FILES 3rdParty/CImg/CImg.h)
set (SOURCE_DIRECTORY Source)
set (TEST_DIRECTORY Test)
# Include sub-projects.
add_subdirectory ("Source")
add_subdirectory ("Test")
# Include libraries
include_directories (
"${CMAKE_CURRENT_LIST_DIR}/3rdParty/CImg"
"${CMAKE_CURRENT_LIST_DIR}/3rdParty/Catch2/single_include"
)
add_library (SheepyEngine STATIC
"${SOURCE_DIRECTORY}/Game.cpp"
"${SOURCE_DIRECTORY}/Game.h"
"${SOURCE_DIRECTORY}/GameObject.h"
${HEADER_FILES})
target_include_directories(SheepyEngine PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdParty/CImg/)
add_executable(SheepyEngineTest "${TEST_DIRECTORY}/test.cpp" "3rdParty/Catch2/single_include/catch.hpp")
target_include_directories(SheepyEngineTest PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdParty/Catch2/)
# TODO: Add tests and install targets if needed.
if(${RUN_TESTS})
ADD_CUSTOM_COMMAND(
TARGET SheepyEngineTest SheepyEngine
POST_BUILD
COMMAND ${CMAKE_CURRENT_LIST_DIR}/Build/Debug/SheepyEngineTest.exe
)
endif()
The SheepyTestProgram target needs to be dependent on SheepyEngine:
target_link_libraries(SheepyEngineTest SheepyEngine)
Then the target of add_custom_command will be just SheepyEngineTest (add_custom_command accepts only a single target).

CMake with gmock

I just want to make sure that my understanding about CMakeLists.txt is correct. My dummy project structure:
|-+ dummy
|-+ CMakeLists.txt
|-+ src
|-- CMakeLists.txt
|-- Converter.cpp
|-- Converter.hpp
|-- main.cpp
|-+ tests
|-- CMakeLists.txt
|-- Converter_ut.cpp
|-+ thirdparty
|-+ gmock-1.7.0
My goal is to create build process with CMake. This is my first attempt so I assume that there are some mistakes. It works but I am not sure if I understand everything correctly and I would be thankful if you could share with some comments / suggestions.
dummy/CMakeLists.txt
cmake_minimum_required (VERSION 2.8.11)
project (SUB)
add_subdirectory (src)
add_subdirectory (tests)
cmake_minimum_required is pretty self-explanatory,
project (SUB) sets project variables like ${SUB_SOURCE_DIR} and ${SUB_BINARY_DIR},
add_subdirectory, tells CMake to go and process CMakeLists.txt in the following directories
src/CMakeLists.txt
add_library (Sub
main.cpp
Converter.cpp)
target_include_directories (Sub PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# Executable
add_executable (converter
Converter.cpp)
target_link_libraries (converter Sub)
add_library, creates library called "Sub" from two source files,
target_include_directories, tells the compiler where are the header files for "Sub" library (is that "PUBLIC" really needed here?),
add_executable, creates "converter" executable from Converter.cpp (why main.cpp is not needed here?),
target_link_libraries, links "Sub" library with "converter" executable
tests/CMakeLists.txt
# GMOCK
set (GMOCK_DIR "../thirdparty/gmock-1.7.0")
add_subdirectory(${GMOCK_DIR} ${CMAKE_BINARY_DIR}/gmock)
include_directories(SYSTEM ${GMOCK_DIR}/include ${GMOCK_DIR}/gtest/include)
# Executable
add_executable (tests
Converter_ut.cpp)
target_link_libraries (tests gmock_main Sub)
set (GMOCK_DIR ...), sets local variable "GMOCK_DIR" with my gmock folder location,
add_subdirectory, tells CMake to jump into gmock location and run their CMakeLists.txt, what is the second argument? {CMAKE_BINARY_DIR}/gmock?
add_executable, creates second executable file
target_link_libraries, links gmock_main library with second executable, "Sub" library is needed here because Converter_ut.cpp
needs to include "Converter.hpp" from src directory
Thank you in advance. I have read plenty of sites / tutorials already but I am still not sure about that.
One more thing - I cannot really imagine project with plenty of source files. Isn't there a better way to add source files to add_library and add_executable functions than listing it manually? Something like "take all *.cpp files from current directory"?
Thanks.
Cmake is not properly a programming language supporting a full paradigm, so use it, but if possible never start creating "a framework with it" (it would be cumbersome without proper syntactic sugar), it is intended to make small scripts not to write thousand lines of code (and despite few good frameworks exists, I tend to not use them: "If I cannot code it in few lines, then it's not job for CMAKE").
The important parts are (not that it is slightly different, I copy-pasted the improved version i still have to commit):
cmake_minimum_required( VERSION 2.8)
project( Infectorpp2)
# find additional cmake scripts (I'm driving away from this)
set( CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# create list of files from a glob
file( GLOB INFECTOR_SOURCE_FILES
"include/Infectorpp/priv/*.cpp")
# create list of files from a glob
file( GLOB INFECTOR_HEADER_FILES
"include/Infectorpp/priv/*.hpp"
"include/Infectorpp/*.hpp")
# or just "add_executable" the dollar "${}" just expand the list
add_library( libInfectorpp2 STATIC
${INFECTOR_SOURCE_FILES}
${INFECTOR_HEADER_FILES})
If you are not using 3rd party libraries, then you do not need to add target_include_directories because for your own application relative paths suffice.
For the testing part you are mostly ok to me, but I would do:
enable_testing()
## details omitted...
# create list of files from a glob
file( GLOB GMOCK_TESTS_SOURCE_FILES
"locationToTestFiles/*.cpp")
# Executable
add_executable (tests
${GMOCK_TESTS_SOURCE_FILES})
target_link_libraries (tests gmock_main Sub)
add_test(tests tests)
Also note that CMAKE is the only reason why I find useful having different extensions for C++ files, because of GLOBS, if you want to exclude some file you have to change its extension (to cc, c++, cxx or what your prefer).
You can do mostly anything following the same pattern, note that with GLOB you have to re-configure CMake to detect newly added files, however still better than adding them manually to build script (and anyway that will not cause a whole recompilation, CMake keep track of data and will avoid to re-compile old files)
Some people find useful adding files manually to CMake scripts because "I can keep old file there". Don't do that, move old files into an "old" folder, or just let your subversion system keep memory of them for you.
You will catch most errors earlier, and you will have a "ready to ship" project (you won't accidentally left wrong files that users will attempt to compile)
Another important point:
Do out of source builds, from your script I guess you are still not doing that.

CMake: Build Multiple Executables in one Project with Static Library

I'm working on a project that consists of 3 server executables and one library for shared code. I want it to be cross-platform, so I'm using CMake (since Xcode is being a pain anyway) to handle the build process. I'm having trouble with setting up the CMakeLists so that I can include the library from a directory at the same level when I'm building the executable.
Here's the directory structure (and the CMake files):
tethealla2.0/
CMakeLists.txt
libtethealla/
CMakeLists.txt
encryption/
utils/
patch_server/
CMakeLists.txt
login_server/
CMakeLists.txt
ship_server/
CMakeLists.txt
My top-level CMake (tethealla2.0/CMakeLists.txt, only includes the sub-project that should compile):
project(tethealla CXX)
cmake_minimum_required(VERSION 2.6)
add_subdirectory(libtethealla)
add_subdirectory(patch_server)
tethealla2.0/libtethealla/CMakeLists.txt, which generates a static library:
project(Libtethealla C)
cmake_minimum_required(VERSION 2.6)
include_directories(encryption)
set(ENC_DR encryption/)
set(ENCRYPTION_SOURCES
${ENC_DR}/psobb-crypt.c
${ENC_DR}/psogc-crypt.c
${ENC_DR}/psobb-crypt.c
${ENC_DR}/encryption.c
)
add_library(tethealla STATIC ${ENCRYPTION_SOURCES})
tethealla2.0/patch_server/CMakeLists.txt thus far:
project(patch_server CXX)
cmake_minimum_required(VERSION 2.6)
add_executable(server main.cc)
target_link_libraries(server tethealla)
So it makes more sense if I build it from the top level since tethealla2.0/CMakeLists.txt will inherit the targets from each of the subdirectories and the one in patch_server will have access to the tethealla library. However what I want is to be able to build from within these subdirectories to generate Xcode projects so that I can work on/recompile them individually. To do so I need to be able to get to the libtethealla/build directory (where CMake outputs) to access the libtethealla.a library from patch_server. Is this possible?
On kind of another note, even in building from the top-level directory my source in patch_server can't include "encryption.h", the header file for the library in encryption. Which seems to be building fine. Any thoughts on that are also greatly appreciated!
My solution is to use add_subdirectory with relative patch to shared_lib directory. I don't think that this is a perfect solution it has its caveats:
Logic very similar to a header guard must be added to library CMakeLists.txt to prevent from defining targets multiple times.
Each CMakeList.txt file must know the relative path to the library, if one want to move library all CMakeLists must be updated.
Let's assume that the directory structure looks like this:
root/
CMakeLists.txt
shared_lib/
CMakeLists.txt
inc/
foo.h
src/
foo.c
exec1/
CMakeLists.txt
main.c
exec2/
CMakeLists.txt
main.c
root/CMakeList.txt
cmake_minimum_required(VERSION 2.6)
add_subdirectory(shared_lib)
add_subdirectory(exec1)
add_subdirectory(exec2)
I have decided that shared_lib/CMakeLists.txt will export a variable named SHARED_DIR_INCLUDE_DIR. This approach helps to decouple things a little bit.
root/exec1/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_subdirectory(./../shared_lib shared_lib)
include_directories(${SHARED_LIB_INCLUDE_DIR})
set(SRCS main.c)
add_executable(exec1 ${SRCS})
target_link_libraries(exec1 shared_lib)
if() in the fourth line solves the issue with target's multiple definition in case the CMakeLists file is added multiple times. The second and the third lines exports the include directory for library in SHARED_LIB_INCLUDE_DIR
root/shared_lib/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
set(SHARED_LIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(SHARED_LIB_INCLUDE_DIR ${SHARED_LIB_INCLUDE_DIR} PARENT_SCOPE)
if(TARGET shared_lib)
message("shared_lib is already defined")
else()
include_directories(${SHARED_LIB_INCLUDE_DIR})
set(LIB_SRCS ./src/foo.c)
add_library(shared_lib STATIC ${LIB_SRCS})
endif()