First, I need to claim that I have been going around Stack Overflow and arrive answers of how to use ExternalProject to build Google Test within a project, for example here.
Now let's say what I have in my project is something like this
+-- CMakeLists.txt (the big CMake File for the entire project)
+-- tests
| +-- CMakeLists.txt (contain of all the small project in the tests)
| +-- Test1
| +-- CMakeLists.txt (test file for Test1 program)
| +-- test_1.cpp
| +-- test_1.h
| +-- Test2
| +-- CMakeLists.txt (test file for Test2 program)
| +-- test_2.cpp
| +-- test_2.h
| +-- Test3
| +-- CMakeLists.txt (test file for Test3 program)
| +-- test_3.cpp
| +-- test_3.h
Now, is there anyway that I can configure and build Google Test using ExternalProject in the big CMakeLists.txt file (at the root folder), and then use those library to build each separate tests in their corresponding folder. At the moment I can only download-built-link an entire new set of GTest libraries in each of the sub folder Test1, Test2, Test3 which is very inefficient. Is there an alternative way to get around this?
It is unclear without seeing your actual CMakeLists.txt files what's happening in your case. That said, used in the normal way, ExternalProject doesn't give you the CMake targets to link to directly, you have to do more manual work to get something you can add to the linked library list of your targets needing to use GTest. So depending on how you've manually defined those things, that could be why you are finding you need to build GTest in each TestX subdirectory.
In the other SO question you linked to, my answer there and the linked article therein mentions how to download and build GTest as part of the CMake stage rather than at build time. This gives you the CMake targets which you can then link against directly without having to define them manually. If you follow that method to download and build GTest in your top level CMakeLists.txt file, then the gtest and gtest main targets will be visible to all subdirectories. Furthermore, you won't have to go figure out the name of the library for each platform, etc. since the CMake target already handles that for you. Thus, in your various Test1, Test2 and Test3 subdirectories, all you'd have to do is to add gtest or gtest_main to the list of libraries each test program linked against.
My approach is to create a directory at the same level of your test projects:
+-- CMakeLists.txt (the big CMake File for the entire project)
+-- tests
| +-- CMakeLists.txt (contain of all the small project in the tests)
| +-- gtest
| +-- CMakeLists.txt (use ExternalProject_Add to import GTest)
| +-- Test1
| +-- CMakeLists.txt (test file for Test1 program)
| +-- test_1.cpp
| +-- test_1.h
| +-- Test2
| ...
In the CMakeLists.txt for gtest I define the gtest target and export the variables for the include dir and library path for the other projects to use.
It works for me and I get gtest built only once so all test projects can link against it. But I'll try to give a shot to Craig Scott's approach to build gtest during Makefile generation instead of at build time.
The CMakeLists.txt file under the gtest folder looks like this:
cmake_minimum_required(VERSION 2.8.11)
include(ExternalProject)
set(GMOCK_VERSION "1.7.0")
set(GMOCK_DIR "${CMAKE_CURRENT_BINARY_DIR}/gmock-${GMOCK_VERSION}")
ExternalProject_Add(project_gmock
SVN_REPOSITORY http://googlemock.googlecode.com/svn/tags/release-${GMOCK_VERSION}
PREFIX ${GMOCK_DIR}
CMAKE_ARGS -DCMAKE_C_COMPILER:PATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:PATH=${CMAKE_CXX_COMPILER}
# Disable update step
UPDATE_COMMAND ""
# Disable install step
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(project_gmock source_dir)
ExternalProject_Get_Property(project_gmock binary_dir)
include_directories(${source_dir}/gtest/include)
add_library(gtest STATIC IMPORTED GLOBAL)
set_target_properties(gtest PROPERTIES IMPORTED_LOCATION ${binary_dir}/gtest/libgtest.a)
set_target_properties(gtest PROPERTIES INCLUDE_DIRECTORIES ${source_dir}/gtest/include)
add_dependencies(gtest project_gmock)
get_property(GTEST_INCLUDE_DIR TARGET gtest PROPERTY INCLUDE_DIRECTORIES)
set(GTEST_INCLUDE_DIR "${GTEST_INCLUDE_DIR}" PARENT_SCOPE)
add_library(gtest_main STATIC IMPORTED GLOBAL)
set_target_properties(gtest_main PROPERTIES IMPORTED_LOCATION ${binary_dir}/gtest/libgtest_main.a)
add_dependencies(gtest_main project_gmock)
include_directories(${source_dir}/include)
add_library(gmock STATIC IMPORTED GLOBAL)
set_target_properties(gmock PROPERTIES IMPORTED_LOCATION ${binary_dir}/libgmock.a)
set_target_properties(gmock PROPERTIES INCLUDE_DIRECTORIES ${source_dir}/include)
add_dependencies(gmock project_gmock)
get_property(GMOCK_INCLUDE_DIR TARGET gmock PROPERTY INCLUDE_DIRECTORIES)
set(GMOCK_INCLUDE_DIR "${GMOCK_INCLUDE_DIR}" PARENT_SCOPE)
add_library(gmock_main STATIC IMPORTED GLOBAL)
set_target_properties(gmock_main PROPERTIES IMPORTED_LOCATION ${binary_dir}/libgmock_main.a)
add_dependencies(gmock_main project_gmock)
Related
My current project has the following structure:
root
|- parser
| |- include // a directory for headers
| |- src // a directory for sources
| |- parser.yy
| |- scanner.ll
| |- CmakeLists.txt
|
|- preprocessor
| |- include // a directory for headers
| |- src // a directory for sources
| |- CmakeLists.txt
|
|- main.cpp
|- CMakeLists.txt
Now I want to start generating assembly codes. I have to use staticstack and codegen (are classes which are going to be defined) in the parser.yy and in srcfolder of the parser foler.
I cannot figure out how to do that.
What's the best practice in CMake to do this ?
Should I make subdirectories named semstack and codegen in parser folder ? This doesn't seem correct to me since as a CMake project gets larger the hierarchy will get deeper and if more dependencies are there then it would be a mess.
Should I make these subdirectories in the root ? If then what is the syntax in CMake to use sibling subdirectory ?
If then what is the syntax in CMake to use sibling subdirectory ?
There is no special syntax for this since normal (i.e. not IMPORTED) targets are global. If you define a library in one subdirectory, it may be used in any other via target_link_libraries.
For instance:
# parser/CMakeLists.txt
add_library(parser ...)
add_library(proj::parser ALIAS parser)
target_include_directories(
parser PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
target_link_libraries(
parser PRIVATE proj::semstack proj::codegen)
The code for other subdirectories is similar.
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(proj)
add_subdirectory(semstack)
add_subdirectory(codegen)
add_subdirectory(parser)
add_subdirectory(preprocessor)
add_executable(main main.cpp)
target_link_libraries(
main PRIVATE proj::parser proj::preprocessor)
I create and link to ALIAS targets to avoid a typo in a target name being forwarded to the linker verbatim (names with :: in CMake are always considered targets and this is validated at generation time).
Basically I wanted to learn CMake and GTest. On top of that I wanted to try out CI's and I have added my simple project to Travis CI and AppVeyor. The project compiles well on my local machine (tested with vs 2017 and g++, not sure about the versions, because I am not near it at the moment) as well as on AppVeyor.
The project consists of a few headers compiled to a static library, executable for "manual testing" the static library and gtest (no tests at the moment, just a template).
The structure looks like that:
.
+-- .appveyor.yml
+-- .travis.yml
+-- CMakeLists.txt
+-- main
| +-- main.cpp [The manual testing exec]
| +-- CMakeLists.txt
+-- include
| +-- Utility.h
| +-- CMakeLists.txt
| +-- Other header files to compile to static library
+-- test
| +-- CMakeLists.txt
| +-- CMakeLists.txt.in
| +-- testutility
| | +-- main.cpp [sample exe file for gtest, not used yet]
| | +-- CMakeLists.txt
During build on Travis CI, it cannot find headers for the static library.
I have tried using
include_directories(".") on main CMakeLists.
Main CMakeLists.txt
cmake_minimum_required (VERSION 3.1)
project (primlibrary)
enable_testing()
include_directories(".")
add_subdirectory(include)
add_subdirectory(main)
add_subdirectory(test)
Travis CI error message
CMake Error at include/CMakeLists.txt:12 (add_library):
Cannot find source file:
Utility.h
Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
.hxx .in .txx
-- Generating done
-- Build files have been written to: /home/travis/build/SoIAS/PrimLibrary/build
The command "cmake .." exited with 1.
$ make
Scanning dependencies of target prim_library
[ 9%] Linking CXX static library libprim_library.a
[ 9%] Built target prim_library
Scanning dependencies of target manualtestingapp
[ 18%] Building CXX object main/CMakeFiles/manualtestingapp.dir/main.cpp.o
/home/travis/build/SoIAS/PrimLibrary/main/main.cpp:2:29: fatal error: include/Utility.h: No such file or directory
#include "include/Utility.h"
And include/CMakeLists.txt:
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(prim_library_srcs
ForwardList.h
LinkedList.h
Utility.h
tempfix.cpp
)
add_library(prim_library STATIC ${prim_library_srcs})
set_target_properties(prim_library PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(prim_library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
CI and git links:
- Travis CI last build
- Appveyor build
- Github
Do you know what am I doing wrong?
The problem was solved literally a dozen minutes after posting. The Utility.h file locally had capitalized first letter while the one on git was lower case. It was solved by using git command to capitalize the first letter of the file on git:
git mv include/utility.h include/Utility.h
I am attempting to write CMakeLists.txt for a platform. The platform has multiple projects which share some libraries and have their own executables. I wish to be able to build each project as a whole, as well as individual executables from within the project.
out-of-source root build works fine:
mkdir root-build && cd root-build && cmake ../root
out-of-source executable build does not work:
mkdir ui-build && cd ui-build && cmake ../root/project1/ui
My current directory structure is as follows:
root/
+-- CMakeListst.txt
+-- lib/
+-- CMakeLists.txt
+-- sharedLib1/
+-- CMakeLists.txt
+-- sharedLIb2/
+-- CMakeLists.txt
+-- project1/
+-- CMakeLists.txt
+-- ui/
+-- CMakeLists.txt
+-- executable1/
+-- CMakeLists.txt
+-- shared/
+-- CMakeLists.txt
+-- project2/
+-- CMakeLists.txt
+-- executable/
+-- CMakeLists.txt
My current project structure builds all targets and libraries as part of the project great, but I can't figure out how to build an executable in isolation.
The main cmake file for a project is root/project*/CMakeLists.txt. This file INCLUDES root/CMakeLists.txt, which does some setup common to all projects (e.g setting CMAKE_MODULE_PATH and find_package for third-party libraries). The project cmake file also calls add_subdirectory for each of it's executables and add_subdirectory(../lib libs).
This allows executables to link against any of the targets defined in the root/lib subdirectory. If I then try to build the executable independently of the project, CMake will not be able to find the target the executable needs to link against. Is there an elegant solution / pattern that would allow this? One way I thought of was to just add_subdirectory(../../ root) from the ui/CMakeLists.txt, with something resembling header guards to prevent an infinite add_subdirectory loop.
Use make executable1 (for example) to build individual targets.
I have a batch script which when run will simply call a cmake command to build a visual studio C++ project. However, that project should have two references to two static libraries... yet, no matter what I try I can't seem to get my project to link the libraries correctly. Here's how my folder structure looks:
.
+-- build
| +-- x64
| +-- DebugStatic
| +-- mylibA_static.lib
| +-- mylibB_static.lib
+-- Include
+-- Source
+-- myproject
| +-- Demo
| +-- build
| +-- cmake
| +-- GenerateVS2015.bat
| +-- CMakeLists.txt
| +-- Demo.cpp
| +-- stdafx.cpp
Now, I'm running the batch script from within the cmake folder inside the Demo project folder structure. My batch script is as follows:
#echo off
set startingDir=%CD%
set basepath=%~dp0
set builddir=%basepath%\..\build
if not exist %builddir% (mkdir %builddir%)
cd %builddir%
cmake -G "Visual Studio 14 2015 Win64" .. %*
cd %startingDir%
Nothing too complicated. Now, let's look at the CMakeLists.txt file:
cmake_minimum_required (VERSION 2.8)
PROJECT (Demo)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
add_definitions(-DBUILD_DLL)
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} xmllite.lib")
string(REGEX REPLACE "/bin/[^/]*$" "" VCINSTALLDIR "${CMAKE_C_COMPILER}")
message(STATUS "Guessed MSVC directory: ${VCINSTALLDIR}")
endif()
SET(SRCS
Demo.cpp
stdafx.cpp
)
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC ../../Include)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibA_static.lib ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibB_static.lib)
Using this logic, wouldn't CMake build a Visual Studio C++ project called Demo.vcproj... and inside that project file would linked two libraries called mylibA_static.lib and mylibB_static.lib inside the references. However, when I try this, I never get any sort of linked library.
First, in your batch script, you only generated the makefiles, but you didn't actually build the library. So I would suggest you add the line make after cmake and before cd.
Second, I would suggest you surround all your strings with double quotes, in order to be specific, and avoid confusion between constants and variables. Also, it is better to add additional slashes. So, change
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibA_static.lib ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibB_static.lib)
to
TARGET_LINK_LIBRARIES(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/../../build/x64/DebugStatic/mylibA_static.lib" "${CMAKE_CURRENT_SOURCE_DIR}/../../build/x64/DebugStatic/mylibB_static.lib)".
As for the relative pathes, I think you are fine. But be careful when you use CMAKE_SOURCE_DIR and CMAKE_CURRENT_SOURCE_DIR. Here are some notes from CMake Wiki:
CMAKE_CURRENT_SOURCE_DIR this is the directory where the currently
processed CMakeLists.txt is located in
CMAKE_SOURCE_DIR this is the directory which contains the top-level
CMakeLists.txt, i.e. the top level source directory
I read this brilliant tutorial about how to integrate Google Test with CMake. The outline of the project there looks like this:
+-- CMakeLists.txt
+-- main
| +-- CMakeLists
| +-- main.cpp
|
+-- test
| +-- CMakeLists.txt
| +-- testfoo
| +-- CMakeLists.txt
| +-- main.cpp
| +-- testfoo.h
| +-- testfoo.cpp
| +-- mockbar.h
|
+-- libfoo
| +-- CMakeLists.txt
| +-- foo.h
| +-- foo.cpp
|
+-- libbar
+-- CMakeLists.txt
+-- bar.h
+-- bar.cpp
(For the interested, the entire code of this example project can be checked out from here)
The top-level CMakeLists.txt contains (among others) the statements enable_testing() and add_subdirectory(test). Compiling and running test cases works perfectly with this setup, simply by running
mkdir build && cd build
cmake ..
make
make test
But how would I compile this project into production code, i.e. only the components test, libfoo and libbar, without all the unit tests?
Should I make the statements enable_testing() and add_subdirectory(test) somehow dependent on some build configuration variables? Or what's the best practice for this?
To build the tests only on request, I do it in this way:
Add an option option(BUILD_TEST "Build the unit tests" ON)
Include the test subdirs only in case BUILD_TEST is on
if(BUILD_TEST)
add_subdirectory(test)
endif()
In your case you can modify this to testfoo.
As you asked for production, you can use the following instead to build in debug mode only:
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_subdirectory(test)
endif()
What I do is to make a custom macro for unit test creation which does this:
set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test)
Then your tests will end up in a special directory (test) instead of the regular one (usually bin). Then for production you just copy the regular directory without the test directory.