I'm working on a Websockets library for C++. My project has the library folder and a tests folder.
The tests folder contains many source files that each is compiled as a stand-alone executable and considered a test. The relevant CMakeLists for it looks like this:
file(GLOB TEST_SOURCES "src/*.cpp")
foreach(file ${TEST_SOURCES})
get_filename_component(_F_NAME ${file} NAME_WE)
add_executable(${_F_NAME} ${file})
target_link_libraries (${_F_NAME} ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(${_F_NAME} tiny_websockets_lib Catch)
if(WIN32)
target_link_libraries(${_F_NAME} wsock32 ws2_32)
endif()
add_test(NAME ${_F_NAME} COMMAND ${_F_NAME})
endforeach()
Basically for every cpp file in src/ I create a new executable and link to it my library (here called tiny_websockets_lib).
The libraries' CMakeLists.txt looks like this:
file(GLOB_RECURSE tinyws_SOURCES
"src/*.cpp"
)
file(GLOB_RECURSE tinyws_HEADERS
"include/*.h"
"include/*.hpp"
)
include_directories(include/tiny_websockets)
add_library(tiny_websockets_lib STATIC ${tinyws_HEADERS} ${tinyws_SOURCES})
set_target_properties(tiny_websockets_lib PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(tiny_websockets_lib PUBLIC include)
The Issue:
In the library I have some conditional compilation flags. For example, this flag:
#define _WS_CONFIG_NO_TRUE_RANDOMNESS
can be defined and it will change the behivour of the library.
The problem is that the library only compiles once, so if my test looks like this:
#define _WS_CONFIG_NO_TRUE_RANDOMNESS
#include <library_stuff_with_conditional_compile_ifdefs>
int main() {
// tests
}
The library will not be compiled again, and the flag will not be considered.
TLDR: To my understanding the library is compiled once and linked multiple times. I would like the library to recompile and consider the main file for every tests.
You can see the actual project and structure here: https://github.com/gilmaimon/TinyWebsockets
Thank You.
Related
I have code with one big directory of models which are going to be used in two binaries - the main program and tests. I want to put the models to the library which will compile once and then both main program and tests can use it without recompiling it again.
Here are my current directories:
root/
CMakeLists.cpp
src/
CMakeLists.txt
main.cpp
model/
/*a lot of cpp/hpp files*/
impl/
impl.cpp
impl.hpp (uses models)
test/
CMakeLists.txt
main.cpp
test.cpp (uses models and impl)
root/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(server)
set(CMAKE_CXX_STANDARD 14)
file(GLOB MODEL_SOURCES "src/model/*.cpp")
add_library(modelLibrary ${MODEL_SOURCES})
add_subdirectory(src)
add_subdirectory(test)
src/CMakeLists.txt
set(BINARY ${CMAKE_PROJECT_NAME})
file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h *.cpp)
set(SOURCES ${SOURCES})
add_executable(${BINARY} ${SOURCES})
include_directories(impl)
# can't see model files without this line
target_include_directories(${BINARY} PRIVATE ./model)
target_link_libraries(${BINARY} PUBLIC modelLibrary pistache pthread ssl crypto)
test/CMakeLists.txt
set(BINARY ${CMAKE_PROJECT_NAME}_test)
file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false *.hpp *.cpp)
set(SOURCES ${TEST_SOURCES})
add_executable(${BINARY} ${TEST_SOURCES})
add_test(NAME ${BINARY} COMMAND ${BINARY})
# can't see model files without this line
target_include_directories(${BINARY} PRIVATE ../src/model)
target_link_libraries(${BINARY} modelLibrary gtest gtest_main pthread)
I have failed with using modelLibrary as only source. It still compiles the models two times. I'd like to achieve solution where models are compiled once and they are reachable from both - main program and test.
I believe the reason you see the model files compiled twice is due to the recursive glob in src/CMakeLists.txt.
file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h *.cpp)
This recursive glob will also walk into src/model and grab the *.cpp files there. So the model files are getting compiled as part of modelLibrary as well as part of the server executable.
One way to fix this would be to remove the recursive glob and create a separate CMakeLists.txt in src/model with the contents of:
# I wouldn't actually glob, see note below.
file(GLOB MODEL_SOURCES "*.cpp")
add_library(modelLibrary ${MODEL_SOURCES})
target_include_directories(modelLibrary PUBLIC ".")
One would need to make a call add_subdirectory() for this new CMakeLists in either the root or the src/CMakeLists.txt.
Notice the use of target_include_directories in the possible solution. I put that there as I noticed it was being repeated in the test and executable. Using PUBLIC on modelLibrary means that consumers get the include directory just by using target_link_libraries.
Note about globbing: from CMake docs on GLOB
Note We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate. The CONFIGURE_DEPENDS flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if CONFIGURE_DEPENDS works reliably, there is still a cost to perform the check on every rebuild.
That note can be a bit hard to grok, but try to fully understand the consequences if you do decide to glob.
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.
I would like to create two executables: one executable for the application, and one for the testing of the application. To that end, I have the following in my CMakeLists.txt file:
include_directories(include)
file(GLOB SOURCE "src/*.cc")
file(GLOB TEST "test/*.cc")
add_executable(interest_calc ${SOURCE})
add_executable(interest_calc_test "src/interest_calc.cc" ${TEST})
Since both src and test directories contain main functions, I have to manually add source files to the "test" executable. Is there another, non-manual, way to add required source files to the "test" executable?
Further, is there a better way to test functionality than creating a separate test executable? If so, what/how?
One way to improve your process would be to pull the guts of your executable into a library, then have a nominal "main" executable which just calls into your library and a "test" executable which exercises the library however you want to test it.
This way, any changes you need to make go into the library and the executable build process is untouched.
Edit to show CMake with your example:
include_directories(include)
file(GLOB SOURCE "src/*.cc")
# Remove main from library, only needed for exec.
list(REMOVE_ITEM SOURCE "main.cc")
file(GLOB TEST "test/*.cc")
add_library(interest_calc_lib STATIC ${SOURCE})
add_executable(interest_calc "main.cc")
target_link_libraries(interest_calc interest_calc_lib)
add_executable(interest_calc_test ${TEST})
target_link_libraries(interest_calc_test interest_calc_lib)
There are already some good answers from Soeren and mascoj but I would like to give a more concrete recommendation.
When you already have a CMakeLists.txt for your executable and you like to add testing, I recommend adding a static dummy library. This library can have all the sources of the executable except the main method (it may be easiest to single out the main method in a separate file if you do not have that already). Using a static library will give you two benefits:
The final executable will behave exactly as your current one, so there is no need to deal with distribution of a new, shared library
You do not need to deal with exporting symbols or throwing exceptions across shared object boundaries
The changes to your CMakeLists.txt can be quite small. I will give an example here, assuming you use cmake 3.0 or newer. First, an example of CMakeLists.txt before adding the dummy library:
project(MyProject)
set(SOURCES src/First.cc src/Second.cc src/Third.cc)
add_executable(${PROJECT_NAME} ${SOURCES} src/Main.cc)
target_include_directories(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(${PROJECT_NAME}
$<$<CXX_COMPILER_ID:GNU>:-Wall;-pedantic)
target_compile_definitions(${PROJECT_NAME}
$<$<CONFIG:Debug>:DEBUG;_DEBUG>)
set_target_properties(${PROJECT_NAME}
PROPERTIES CXX_STANDARD 14)
target_link_libraries(${PROJECT_NAME}
Threads::Threads)
To add the dummy library and testing, you need to introduce a new target with a different name. I choose here to use ${PROJECT_NAME}_lib because this will be very non-intrusive on the CMakeLists.txt. Here is the updated version. Notice the use of ${PROJECT_NAME}_lib in place of ${PROJECT_NAME} in almost all places. Most properties are now passed down to the executable by making them PUBLIC. Only calls to set_target_properties() are not transitive and must be duplicated for library and executable.
project(MyProject)
set(SOURCES src/First.cc src/Second.cc src/Third.cc)
add_library(${PROJECT_NAME}_lib STATIC ${SOURCES})
add_executable(${PROJECT_NAME} src/Main.cc)
target_include_directories(${PROJECT_NAME}_lib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(${PROJECT_NAME}_lib PUBLIC
$<$<CXX_COMPILER_ID:GNU>:-Wall;-pedantic)
target_compile_definitions(${PROJECT_NAME}_lib PUBLIC
$<$<CONFIG:Debug>:DEBUG;_DEBUG>)
set_target_properties(${PROJECT_NAME}_lib
PROPERTIES CXX_STANDARD 14)
set_target_properties(${PROJECT_NAME}
PROPERTIES CXX_STANDARD 14)
target_link_libraries(${PROJECT_NAME}
${PROJECT_NAME}_lib
Threads::Threads)
Now you can link your tests against ${PROJECT_NAME}_lib with a different main method.
You could do like this :
In the current CMakeLists.txt, put these lines :
add_subdirectory(src)
add_subdirectory(test)
and then, in each directories add a CMakeLists.txt that link correctly sources to each files.
About test, I've heard that CMake can do test automation, but I don't really know how it works.
In my opinion the best solution is to create a library (shared or static) and two executables (one for the main program and one for the test main). After that you should link the library against the two applications.
In this answer I write down a explanation with a little example how you could managed the project with cmake.
My code is organized like this:
cpp
main.cpp (calls code from dataStructures/ and common/)
CMakeLists.txt (topmost CMakeLists file)
build
common
CMakeLists.txt (should be responsible for building common shared library)
include
utils.h
src
utils.cpp
build
dataStructures
CMakeLists.txt (build dataStructures shared library - dependent on common library)
include
dsLinkedList.h
src
dsLinkedList.cpp
build
build\ directories contain the built target. The actual code can be seen here: https://github.com/brainydexter/PublicCode/tree/master/cpp
As of now, CMakeLists.txt in each of the subdirectories build their own shared libraries. Topmost CMakeLists file then references the libraries and paths like this
Topmost CMakeLists.txt
cmake_minimum_required(VERSION 3.2.2)
project(cpp)
#For the shared library:
set ( PROJECT_LINK_LIBS libcppDS.dylib libcppCommon.dylib)
link_directories( dataStructures/build )
link_directories( common/build )
#Bring the headers, into the project
include_directories(common/include)
include_directories(dataStructures/include)
#Can manually add the sources using the set command as follows:
set(MAINEXEC main.cpp)
add_executable(testDS ${MAINEXEC})
target_link_libraries(testDS ${PROJECT_LINK_LIBS} )
How can I change the topmost CMakeLists.txt to go into subdirectories (common and dataStructures) and build their targets if they haven't been built, without me having to manually build the individual libraries ?
CMakeLists for common :
cmake_minimum_required(VERSION 3.2.2)
project(cpp_common)
set(CMAKE_BUILD_TYPE Release)
#Bring the headers, such as Student.h into the project
include_directories(include)
#However, the file(GLOB...) allows for wildcard additions:
file(GLOB SOURCES "src/*.cpp")
#Generate the shared library from the sources
add_library(cppCommon SHARED ${SOURCES})
dataStructures :
cmake_minimum_required(VERSION 3.2.2)
project(cpp_dataStructures)
set(CMAKE_BUILD_TYPE Release)
#For the shared library:
set ( PROJECT_LINK_LIBS libcppCommon.dylib )
link_directories( ../common/build )
#Bring the headers, such as Student.h into the project
include_directories(include)
include_directories(../common/include/)
#However, the file(GLOB...) allows for wildcard additions:
file(GLOB SOURCES "src/*.cpp")
#Generate the shared library from the sources
add_library(cppDS SHARED ${SOURCES})
Update:
This pull request helped me understand the correct way of doing this:
https://github.com/brainydexter/PublicCode/pull/1
and commitId: 4b4f1d3d24b5d82f78da3cbffe423754d8c39ec0 on my git
You are only missing a simple thing: add_subdirectory.
From the documentation:
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
Add a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.txt and code files are located. If it is a relative path it will be evaluated with respect to the current directory (the typical usage), but it may also be an absolute path. The binary_dir specifies the directory in which to place the output files. If it is a relative path it will be evaluated with respect to the current output directory, but it may also be an absolute path.
http://www.cmake.org/cmake/help/v3.0/command/add_subdirectory.html
It does exactly what you need.
I have a set of files that I want to make into a library and then use that library in another project. This it how it looks like right now
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET(CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
add_library (helperlibs lib1.cpp lib2.cpp lib3.cpp lib4.cpp )
INSTALL(TARGETS helperlibs
DESTINATION "${HOME}/lib"
)
INSTALL(FILES lib1.h lib2.h lib3.h lib4.h helperheader.h
DESTINATION "${HOME}/include/helperlibs"
)
In this code Lib4 depends on Lib1-3 and Lib3 depends on Lib1-2 and Lib2 depends on Lib1. Each of these cpp files also depend on a helperheader.h file that contains some definitions and structs.
In my project I have the following CMake file
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET( CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
SET(MYINCS ${HOME}/include/helperlibs)
SET(MYLIBDIR ${HOME}/lib)
SET(MYLIBS ${MYLIBDIR}/libhelperlibs.a )
include_directories(${MYINCS})
add_executable(main main.cpp)
target_link_libraries(main ${MYLIBS})
So what I am wondering if you want to create a static library and link to from a another project using cmake is this the way you should write?
Embed the search paths into the library target as properties and create an export.
This way executables in the same build tree will find the library and its include files without you having to specify paths (they become implicit).
I needed to read the cmake documentation carefully a few times before it dawned on me how it should work.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html#creating-packages
http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html
excerpt from a live example:
add_library(trustportal-util ${CMAKE_CURRENT_LIST_FILE} ${_source_files} ${_disabled_source_files} )
target_link_libraries(trustportal-util ${Boost_LIBRARIES})
if(APPLE)
find_library(SECURITY_FRAMEWORK Security)
target_link_libraries(trustportal-util ${SECURITY_FRAMEWORK})
else()
find_library(LIB_SSL ssl)
find_library(LIB_CRYPTO crypto)
target_link_libraries(trustportal-util ${LIB_SSL} ${LIB_CRYPTO})
endif()
target_compile_definitions(trustportal-util PUBLIC BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE)
target_include_directories(trustportal-util PUBLIC ${Boost_INCLUDE_DIRS})
target_include_directories(trustportal-util PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_include_directories(trustportal-util SYSTEM PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS trustportal-util
EXPORT trustportal-utilExport
DESTINATION lib
INCLUDES DESTINATION include
)
INSTALL(EXPORT trustportal-utilExport DESTINATION lib)
One option is to do what you are currently doing where you place the includes and libs in a common location, perhaps /usr/include and /usr/lib on linux, or ${HOME} on both Windows/Linux, up to you.
Another option is available if you use git. You can include the project inside another using submodules. Then use include_directory(${submodule}) and build and link directly for every project. The advantage of this approach is that dependencies are more localised. One problem with this method is if you recursively do this, you may end up with duplicates of some projects and cmake will complain that two libraries have the same name.