CMake - objects being built twice unnecessarily? - c++

I have a list of files that need to be compiled for my main executable. My tests also need these files. When the test executable(s) are built, the object files are built again, even though earlier in the build they were built when the main executable was built.
Am I wrong in thinking this is not needed? If so is there a way to disable this?
Example:
set(SOURCES
${SOURCE_DIR}/file.c
${SOURCE_DIR}/another_file.c)
set(MAIN ${SOURCE_DIR}/main.c)
add_executable(main_executable ${SOURCES} ${MAIN})
add_executable(test1_ex ${PROJECT_SOURCE_DIR}/test/test1.cc ${SOURCES})

Put the common code in a library and link the library to both your application and tests.
# The application's sources - except main
set(SOURCES
${SOURCE_DIR}/file.c
${SOURCE_DIR}/another_file.c)
# build an application library
add_library(app_lib ${SOURCES})
# build an executable in terms of the application library
set(MAIN ${SOURCE_DIR}/main.c)
add_executable(main_executable ${MAIN})
target_link_libraries(main_executable PRIVATE app_lib)
# build a test executable in terms of the application library
add_executable(test1_ex ${PROJECT_SOURCE_DIR}/test/test1.cc)
target_link_libraries(test1_ex PRIVATE app_lib)

This could be due to the following scenario:
add_library(mylib1 file1.cpp common.cpp)
add_library(mylib2 file2.cpp common.cpp)
That created the same problem you're having for me, where common.cpp was being rebuilt for every library. My solution was this:
add_library(common_lib common.cpp)
add_library(mylib1 file1.cpp)
add_library(mylib2 file2.cpp)
target_link_libraries(mylib1 common_lib)
target_link_libraries(mylib2 common_lib)
That resolved the problem for me as the building (object creation) was done once, but it was linked to every other library.

Related

How can I write a CMakeList.txt file for heavily nested project?

Suppose my source code has the following directory structure:
C:\USERS\PC\SOURCE\REPOS\my_app_src
├───apps {a.hh, b.cc, c.hh, d.cc}
│ └───biosimulations {main1.hh, main1.cc, x.hh, y.cc}
└───core {w.cc, x.hh, y.hh, z.cc}
└───algorithms {p.hh, p.cc, q.hh, r.cc, s.hh, s.cc}
└───trees {r.hh, r.cc, main2.hh, main2.cc}
Each folder has any number of header and source files with any name.
How can I write a CMakeList.txt file for this project?
This is scientific software.
I need to be able to use various parts of the same library to compile and build executables for multiple applications.
For example, in the above sample, main1.exe and main2.exe are supposed to be two different executable files.
Sometimes, I need to be able to switch off one or another executable from compiling.
Do you want the project to support testing, installation, and/or packaging?
No, I don't need them. I just need to be able to compile and execute the apps.
What is in core?
Model classes. e.g., Atom, Protein, Chain, etc.
Are the source files for core part of a single library or executable?
Part of executable. There is no static or dynamic library in the project.
Using add_subdirectory and add_library commands, recursively add all of the source files in the directory structure.
cmake_minimum_required(VERSION 3.x)
project(my_app)
# Add subdirectories recursively
add_subdirectory(apps)
add_subdirectory(core)
add_subdirectory(simulations)
add_subdirectory(ui)
add_subdirectory(utils)
# Create the final executable
add_executable(my_app main.cpp)
# Link the libraries to the executable
target_link_libraries(my_app core simulations apps ui utils)
Then in each subdirectories(apps,core,simulations,ui, utils) you would need to add a new CMakeLists.txt that tells which source files are in that directory and create a library.
cmake_minimum_required(VERSION 3.x)
SET(SRC_FILES
program.cpp
)
#for static libraries
add_library(core STATIC ${SRC_FILES})
#for dynamically linking libraries
add_library(core SHARED ${SRC_FILES})
# If you need executable here
# add_executable(core ${SRC_FILES})
This needs to be repeated for all subdirectories, untill all sources are covered. Above examples gives general structure, you need to define CMAKE flags as required.

Is there a way to add library changes as a dependency in cmake?

I have an application and a library that I build with CMake and I would like to make the build process easier if possible. As it stands, if there is a change to the library and I rebuild the application, CMake doesn't rebuild the modified library files. Similarly, if I rebuild the library and then run make on the application, it will say there is nothing to do. My current workaround is to use bash scripts to rebuild everything, but that unnecessarily re-compiles a lot of files and I would like to handle it all within the build directory for the application if possible.
Update with simple example:
There are 3 folders: app, lib, and include. include contains test_program.h, lib contains test_program.cpp, and app contains test.cpp which includes test_program.h.
Here is the CMakeLists.txt for lib:
include_directories(.)
add_library (test_lib STATIC
test_program.cpp
)
Here is the CMakeLists.txt for app:
include_directories(
.
../include
)
link_directories(
../../lib/build
)
add_executable(test_exe
test.cpp
)
target_link_libraries(
test_exe
test_lib
)
I would like to make it so that if I make a change to test_program.cpp, I can simply run cmake ../ and then make in app/build and it will use the updated version of test_program.cpp.
Update:
I have added a top level CMakeLists.txt and a build to the top level of the project. The file is very simple and seems to do what I was wanting from the beginning:
add_subdirectory(app)
add_subdirectory(lib)
I would be happy to take suggestions if there are any improvements to be made here.

CMake project with independent subprojects

I am trying to create a cmake project which has the following directory structure:
root_folder
lib
common_library_for_all_submodules
submodule_1
src
main.cpp
tests
main_test.cpp
submodule_2
src
main.cpp
tests
main_test.cpp
Being new to C++ and also to CMake I have the following confusions and would be really glad if someone can guide me in the right direction here.
Coming from a Java world I know that this is possible to create in a maven project using the modules tag in pom.xml. Is there an equivalent to this in CMake? If yes what to do we call it and can someone give me an example?
I want to then import this project into CLion and when I run the root project, all the submodules should be compiled and relevant tests be run.
Note: submodule_1 and submodule_2 are not using each other's code. They are entirely independent. But they will need to share some common libraries from the root_folder/lib
Thanks a lot in advance
You can use several CMakeLists.txt files, use add_subdirectory statement. To declare library (which will be used in another subproject) use add_library. To declare app - add_executable. To link library to app - target_link_libraries(app1 PRIVATE utils common). This is short and very common description-example. There are more options and parameters.
I'm attaching examples here:
CMakeLists.txt
project(Example)
add_subdirectory(3rd-party)
add_subdirectory(apps)
add_subdirectory(libs)
libs/CMakeLists.txt
add_subdirectory(common)
add_subdirectory(utils)
libs/utils/CMakeLists.txt
FILE(GLOB SOURCES *.cpp *.h)
add_library(utils STATIC ${SOURCES})
target_include_directories(utils PUBLIC .)
apps/CMakeLists.txt
add_subdirectory(app1)
add_subdirectory(app2)
apps/app1/CMakeLists.txt
FILE(GLOB_RECURSE SOURCES src/*.cpp src/*.h)
add_executable(app1 ${SOURCES})
target_link_libraries(app1 PRIVATE utils common)
In this example libraries utils and common are independent and could be built without each other and without apps

Cannot link local libraries in CMake

I'm developing a c++ program on visual studio that will be deployed on linux, and it is debugged on linux through an ssh. Currently, this is the structure of my folder:
ANT
-xscommon
--xscommon_config.h
-xscontroller
-xstypes
ANT.cpp
ANT.h
CMakeLists.txt
CMakeSettings.json
hashes.h
quaternionic.h
stars.h
Currently, all the .h, .cpp, .o, .cpp.o, .a files that I think I have to link to are kept within the three xs------- directories. I am quite new to cmake, and this linking to these libraries is giving me trouble; I am able to link correctly to the includes, but there are undefined references errors thrown when I don't do linking, and when I attempt linking, it throws errors. This is my current CMakeLists.txt file:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic h"ere.
#
cmake_minimum_required (VERSION 3.8)
project ("ANT")
link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(ANT PUBLIC xscommon_config)
When I run this, the builder says the following:
/usr/bin/ld: cannot find -lxscommon_config
I need to look for these libraries in the directory that ANT.cpp is in, as this is where they are kept, however nothing I do (and I have messed around with configurations for hours now) will tell camke to look for these libraries in the src folder. it always goes to /usr/bin/ld.
I really just need to know what to tell CMake such that it will look in the correct place for each file, that is if I am telling it to look for the correct file (I am fairly sure I am).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Update
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
So I have remade the CMakeLists.txt file to this:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.15)
project ("ANT")
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
And still get undefined references. I am going to try building the libraries instead. Additionally, I have contacted the manufacturer of the IMUs which use this SDK, as colleagues have not been able to fix this either.
The problem is you are linking to a library that has not been build.
This
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
tries to link to a library called xscommon to the target ANT but you have not build xscommon anywhere in your project.
If xscommon is a pre-build library and you just want to import it then add the library and set the IMPORTED target property:
add_library(xscommon [STATIC | SHARED | MODULE] IMPORTED)
If you want to build xscommon in your root CMakeLists.txt. Add xscommon as a library and include the location of the headers.
add_library(xscommon [STATIC | SHARED | MODULE]
xxx/xxx.cpp #list all source files that build the library - use relative path
)
target_include_directories(xscommon PRIVATE
xxx/xxx #path to the location of library header files
)
Also you don't need to add the header files when adding the executable. So this
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
can be simplified to
add_executable(
ANT
"ANT.cpp"
)
Suppose your dir is like this:
ANT
-xscommon
--xscommon_config.h
--xscommon_config.cpp
...
First add a CMakeLists.txt file to xscommon/:
ANT
-xscommon
--CMakeLists.txt
--xscommon_config.h
--xscommon_config.cpp
...
Now in xscommon/CMakeLists.txt we will create a library, that will be imported and linked in the main CMakeLists.txt file:
xscommon/CMakeLists.txt:
#define another target, let's name it 'xscommon'
add_library(xscommon
xscommon_config.h
xscommon_config.cpp
#more sources if you want
)
Now in the main CMakeLists.txt file:
cmake_minimum_required (VERSION 3.8)
project ("ANT")
#remove this line
#link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
# add the xscommon directory, this will make the library target defined there available here
add_subdirectory(xscommon)
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
# use PUBLIC if the xscommon library will be part of the public interface of your
# library. But since it is an executable, PRIVATE is better here.
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Use the above method, you can create more libraries and link to them.
Note that it is not necessary to create separate cmake files for each subdirectory but it is considered a good practice and modularizes your code. If you want to do this in the main cmake file instead of creating a subdirectory, add this to the main cmake:
add_library(xscommon
xscommon/xscommon_config.h
xscommon/xscommon_config.cpp
#more sources if you want
)
Update according to the changes in question
Your current CMakeLists.txt:
These three lines below are not doing anything, definitely not what you think. add_library() command has the word "add" in it, I know, but it doesn't add any library, just like add_executable doesn't add any executable. It creates a library.
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
How to create a library in cmake out of two file a.cpp and a.h:
add_library(myALib "a.cpp")
That's it. If you have more sources, you will include them accordingly of course. In your case you will have to add the sources of xscommon and others accordingly.
Once you have created the libraries, you need to link them to your executable. If you won't you will get undefined reference errors because compiler can locate the declarations in header files but not the definitions of your code which exists in .cpp files.
So, how do you link? Simple:
target_link_libraries(TARGET_NAME PUBLIC | PRIVATE LIBRARY_NAME)
# TARGET_NAME: can be `executable` or `library`
# PUBLIC or PRIVATE (for exe, it is usually private)
# LIBRARY_NAME: Name of library you want to link to TARGET_NAME
# So if you wanted to link "myALib" which I created above to ANT, you would do:
target_link_libraries(ANT PRIVATE myALib)
# Note: You need to add this line **after** add_executable() because target "ANT" will be created after that. You can do the linking after the "target_include_directories" command.

Cmake and Gtest

I want to use Google C++ Testing and I am completely beginner to cmake and gtest.
I have a class called Filter which uses a 3d party library called jane.
For this case I have a cmakeFile which builds my project nicely as follows:
cmake_minimum_required(VERSION 3.1.2)
project(Filter)
include(../../../cmake/CMakeMacros.txt)
set_variables()
#add 3rdparty libraries
add_jane()
#add framework libraries
add_framework_libs(
ip/Image
)
include_directories(
../include
${FW_INCLUDE_DIRS}
)
#set project's source and include files
set(INCS
../include/${PROJECT_NAME}.h
../include/${PROJECT_NAME}.tpp
../include/FilterMask.h
)
set(SRCS
../src/${PROJECT_NAME}.cpp
../src/FilterMask.cpp
)
#set link directories
link_directories(
${FW_LIBRARY_DIRS}
)
#build project as static library (*.lib)
add_library(${PROJECT_NAME} STATIC
${INCS}
${SRCS}
)
#link libraries against project
target_link_libraries( ${PROJECT_NAME}
${FW_LIBRARIES}
)
#if a test executable should be build
if(Test_BUILD_EXAMPLES)
#build test executable
add_executable(${PROJECT_NAME}Test
../src/main.cpp
)
#link library against executable
target_link_libraries(${PROJECT_NAME}Test
${PROJECT_NAME}
)
endif(Test_BUILD_EXAMPLES)
and also I have read this simple tutorial on https://github.com/snikulov/google-test-examples with this cmake file https://github.com/snikulov/google-test-examples/blob/master/CMakeLists.txt and tried to build my project again to combine these cmake files together (may be in very silly way) but I can not achieve it since days.
The problem is that when I want to test a simple project with just a header file I can use this cmake file but as soon as I try to test my project containing a 3rd party library I run into different errors.
Can someone please tell me how I can edit a correct cmake file to test my project with googleTest using a cmake file !?
If you want to link against a 3rd party library you typically first:
find_package() or use the pkg config support to check the library is available on the build host and pick up a reference to it.
Include the reference from step #1 in target_link_libraries()
So that's what you need to do for your 3rd party lib. For your own code which you want to bring under test you probably want to put it all inside your own libraries and then link your tests against those.
If you have multiple test executables to separate & isolate each test suite into its own binaries you probably want an alternative technique to avoid over linking and limit the code inside the test suite to the actual unit under test. (This is also quite useful when your code base is in flux and builds only partially but you still wish to check that what builds continues to pass relevant tests.)
In that case you may want to define your units under test as OBJECT type libraries and then instead of doing target_link_libraries() against those object libs you include the objects as part of the sources for the executable using this syntax: $<TARGET_OBJECTS:NameOfObjLibHere> (cmake generator expressions).
So in the case of units which depend on a 3rd party lib, say, Qt5 Core, you'd have snippets like this:
# define the dependency on 3rd party project Qt5, (sub)component: Core, Test)
set(MY_QT_VERSION "5.4.0")
find_package(Qt5 ${MY_QT_VERSION} REQUIRED COMPONENTS Core CONFIG)
# define the object lib for a unit to be tested (Item1)
set(item1_srcs item1.cpp util1.cpp)
add_library(Item1 TYPE OBJECT ${item1_srcs})
# ensure that necessary compiler flags are passed
# when building "Item1" separately
# note that PRIVATE may also be INTERFACE or PUBLIC
# read the cmake docs on target_include_*** to determine which applies.
# you probably want to hide this behind a convenience macro.
target_include_directories(Item1 PRIVATE $<TARGET_PROPERTY:Qt5::Core,INTERFACE_INCLUDE_DIRECTORIES>)
target_compile_options(Item1 PRIVATE $<TARGET_PROPERTY:Qt5::Core,INTERFACE_COMPILE_OPTIONS>)
# find the unit testing framework (dependency)
# this sample uses Qt5 Test (Qt5::Test) but you could use GTest, too
find_package(Qt5 ${MY_QT_VERSION} REQUIRED COMPONENTS Test CONFIG)
# define an executable which contains test sources + unit under test
# link against the testing framework (obviously) as per normal
# note the Qt5::Core dependency here: remember Item1 depends on Qt5::Core (!)
set(test_item1_srcs, test_item1.cpp $<TARGET_OBJECTS:Item1>)
add_executable(test_item1 ${test_item1_srcs)
target_link_libraries(test_item1 Qt5::Core Qt5::Test)
# inform cmake/ctest integration about our test
# so it knows to execute it during `make test` phase.
# and other cmake/ctest integration falls into place as well, possibly
add_test(test_item1 test_item1)