I am compiling an application using CMake 3.16.3 and G++ 9.3 on Ubuntu 20.04. This is the current source directory:
. (this is called MyApp)
├── src
│ ├── /* source files */
│ └── CMakeLists.txt
├── tests
│ ├── /* source files */
│ └── CMakeLists.txt
├── build-release
│ └── pgo /* folder for .gcda files */
├── build_release.sh
└── CMakeLists.txt*
I am setting the flags in CMakeLists.txt (the root one also marked with asterisk) as follows:
set(MYAPP_PGO "-fprofile-dir=${MYAPP_PGO} -fprofile-generate=${MYAPP_PGO}")
// apply flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG -O3 -march=native -flto ${MYAPP_PGO}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MYAPP_PGO}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MYAPP_PGO}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MYAPP_PGO}")
add_subdirectory(src)
add_subdirectory(tests)
No flags are being set in src/CMakeLists.txt and tests/CMakeLists.txt.
MYAPP_PGO is set in bash and is the absolute path of build-release/pgo. I have a script (build_release.sh) that builds the program:
# excluded other lines and flags for brevity
cd build-release
cmake -DMYAPP_PGO="$(pwd)/pgo/" ..
make -j1
After the profile run, I see that there are multiple files in build-release/pgo, one for each cpp file in src and test, such as
#home#johndoe#MyApp#build-release#src#CMakeFiles#MYAPPLICATION_myapp.dir#sample_source.cpp.gcda
However, when compiling again with
// this is a multithreaded program
set(MYAPP_PGO "-fprofile-dir=${MYAPP_PGO} -fprofile-use=${MYAPP_PGO} -fprofile-correction")
// apply flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG -O3 -march=native -flto ${MYAPP_PGO}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MYAPP_PGO}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MYAPP_PGO}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MYAPP_PGO}")
add_subdirectory(src)
add_subdirectory(tests)
I get a warning stating that there are missing profile files:
/home/johndoe/MyApp/src/sample_source.cpp: In function ‘_GLOBAL__sub_I_sample_source.cpp’:
/home/johndoe/MyApp/src/sample_source.cpp:215:1: warning: ‘/home/johndoe/MyApp/src/sample_source.cpp/build-release/pgo//#home#johndoe#MyApp#build-release#src#CMakeFiles#MYAPPLICATION_myapp.dir#sample_source.cpp.gcda’ profile count data file not found [-Wmissing-profile]
215:1 refers to the last character in this source file.
I have tested the speed of the program with and without PGO and have seen no improvement. Although PGO is not guaranteed to bring any speed ups, I tend to believe that PGO did not work as expected here. Am I doing something wrong?
For anyone wondering...
I did not write the original build system of the project. I discovered that it compiled one executable and one library from the same source. Whilst the executable was profiled and recompiled successfully (PGO was done flawlessly), the library was not used anywhere. Hence the missing profile files. Because the profiler outputs extremely long names, I thought the error came from the executable. Thank you all for the help.
Related
I have tbb downloaded and placed in my repository directory:
> tree deps/tbb/ -d
deps/tbb/
├── bin
├── cmake
│ └── templates
├── include
│ ├── serial
│ │ └── tbb
│ └── tbb
│ ├── compat
│ ├── internal
│ └── machine
└── lib
├── ia32
│ └── gcc4.8
└── intel64
└── gcc4.8
In my CMakeLists.txt I have tried this:
include_directories("deps/tbb/include")
find_library(TBB_LIB
NAMES
tbbbind_debug
tbbbind
tbb_debug
tbbmalloc_debug
tbbmalloc_proxy_debug
tbbmalloc_proxy
tbbmalloc
tbb_preview_debug
tbb_preview
tbb
HINTS "${CMAKE_PREFIX_PATH}/deps/tbb/lib/intel64/gcc4.8"
)
add_executable(${PROJECT_NAME}
src/main.cpp
)
target_link_libraries(${PROJECT_NAME} PUBLIC ${TBB_LIB})
But building with cmake, linker throws this error:
/usr/lib64/gcc/x86_64-suse-linux/7/../../../../x86_64-suse-linux/bin/ld: cannot find -lTBB_LIB-NOTFOUND
collect2: error: ld returned 1 exit status
I couldn't figure out what is missing. Thanks.
Update
This commit resolves the previous error:
- HINTS "${CMAKE_PREFIX_PATH}/deps/tbb/lib/intel64/gcc4.8"
+ HINTS "deps/tbb/lib/intel64/gcc4.8"
But, new errors are thrown:
undefined reference to `tbb::interface7::internal::task_arena_base::internal_current_slot()'
Update
Other than find_library, what CMake tools are available to link to TBB shared libraries?
I have tried some CMake tools, but I cannot figure out how to link to TBB *.so files correctly!
TBB has native CMake support. On my system with the Intel oneAPI installed, the config package is installed here:
/opt/intel/oneapi/tbb/latest/lib/cmake/tbb/TBBConfig.cmake
Therefore, I just need to add /opt/intel/oneapi/tbb/latest to my CMAKE_PREFIX_PATH. In my CMakeLists.txt, I wrote this:
cmake_minimum_required(VERSION 3.21)
project(test-tbb)
find_package(TBB REQUIRED)
add_library(main main.cpp)
target_link_libraries(main PRIVATE TBB::tbb)
target_compile_features(main PRIVATE cxx_std_11)
TBB provides the IMPORTED target TBB::tbb, which is what you should link to.
main.cpp is just the source from here: https://stackoverflow.com/a/36782794/2137996
Build like so:
$ cmake -G Ninja -S . -B build -DCMAKE_PREFIX_PATH=/opt/intel/oneapi/tbb/latest
$ cmake --build build --verbose
[1/2] /usr/bin/c++ -isystem /opt/intel/oneapi/tbb/2021.3.0/include -MD -MT CMakeFiles/main.dir/main.cpp.o -MF CMakeFiles/main.dir/main.cpp.o.d -o CMakeFiles/main.dir/main.cpp.o -c /home/alex/test2/main.cpp
[2/2] : && /usr/bin/cmake -E rm -f libmain.a && /usr/bin/ar qc libmain.a CMakeFiles/main.dir/main.cpp.o && /usr/bin/ranlib libmain.a && :
Inspired by #AlexReinking answer, here is the final implementation:
project(my-cpp-service VERSION 0.1.0)
# Equivalent to command-line option of `-DCMAKE_PREFIX_PATH=...`
list(APPEND CMAKE_MODULE_PATH "deps/tbb/cmake/")
find_package(TBB REQUIRED)
add_executable(${PROJECT_NAME}
src/main.cpp
)
target_link_libraries(${PROJECT_NAME} PUBLIC
TBB::tbb
)
This post helped me solved the problem:
https://stackoverflow.com/a/41909627/3405291
The errors got resolved by:
include_directories("deps/tbb/include")
# https://stackoverflow.com/a/41909627/3405291
find_library(LIB_TBB NAMES tbb HINTS "deps/tbb/lib/intel64/gcc4.8")
find_library(LIB_TBBbind NAMES tbbbind HINTS "deps/tbb/lib/intel64/gcc4.8")
find_library(LIB_TBBmalloc_proxy NAMES tbbmalloc_proxy HINTS "deps/tbb/lib/intel64/gcc4.8")
find_library(LIB_TBBmalloc NAMES tbbmalloc HINTS "deps/tbb/lib/intel64/gcc4.8")
find_library(LIB_TBB_preview NAMES tbb_preview HINTS "deps/tbb/lib/intel64/gcc4.8")
add_executable(${PROJECT_NAME}
src/main.cpp
)
target_link_libraries(${PROJECT_NAME} PUBLIC
${LIB_TBB}
${LIB_TBBbind}
${LIB_TBBmalloc_proxy}
${LIB_TBBmalloc}
${LIB_TBB_preview}
)
I'm trying to print coverage with lcov on a C++ project that is using Catch2 for tests. I'm able to run my tests and get results. However, I'm unable to get any coverage. This is the error that is shown.
Capturing coverage data from .
Found gcov version: 9.3.0
Using intermediate gcov format
Scanning . for .gcda files ...
geninfo: WARNING: no .gcda files found in . - skipping!
Finished .info-file creation
Combining tracefiles.
Reading tracefile coverage.base
lcov: ERROR: no valid records found in tracefile coverage.base
My current toolchain is WSL. I'm using Conan for dependency management. The solution has the following structure:
my project/
├─ build/
│ ├─ build files
├─ core/
│ ├─ library files
├─ main/
│ ├─ main runtime
├─ tests/
│ ├─ test runtime/
├─ CMakeLists.txt
Each folder has it's CMakeLists.txt file and is identified as a target. I'm also using this CMake Module to register a target for coverage.
My root CMakeLists.txt looks like this:
cmake_minimum_required(VERSION 3.16)
project(my-project)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "-O0")
include(build/conanbuildinfo.cmake)
conan_basic_setup()
add_subdirectory(core)
option(BUILD_TESTING "Builds only the test executable." OFF)
option(CODE_COVERAGE "Collect coverage from test library" OFF)
if(BUILD_TESTING)
enable_testing()
add_subdirectory(tests)
add_test(NAME project-tests COMMAND ./bin/tests)
if(CODE_COVERAGE)
include(CodeCoverage.cmake)
append_coverage_compiler_flags()
setup_target_for_coverage_lcov(NAME coverage EXECUTABLE ./bin/tests BASE_DIRECTORY ../coverage)
endif()
else()
add_subdirectory(main)
endif()
To get my coverage, I'm using the following commands (on build/).
cmake .. -DCODE_COVERAGE=ON -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug
make
make coverage
From what I understand, it seems to be missing some files necessary for coverage information, but I don't know how to make them. From what I've looked online, I have all the necessary compiler flags. I can't see what is wrong/missing in here.
I believe you forgot to add appropriate flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
I'm trying to build two projects using cmake at same-time.
My folder structure is as follows:
project
├── CMakeLists.txt
├── build
├── out
├── lib
├── yanthra_engine
│ ├── CMakeLists.txt
│ └── ...
└───sandbox
├── CMakeLists.txt
└── ...
main CmakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(yanthra_console VERSION 0.1 DESCRIPTION "A 3d Game Engine.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fexceptions")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;Debug" CACHE STRING "Build type selections" FORCE)
add_subdirectory(yanthra_engine)
add_subdirectory(sandbox)
yanthra_engine/CMakeLists.txt
set(THIRD_PARTY_DIR "../../../third-party")
set(MAIN_SOURCE_DIR "../../main/src")
include_directories(${THIRD_PARTY_DIR}/SDL/include)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib )
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
file(GLOB_RECURSE CPP_HEADERS ${MAIN_SOURCE_DIR}/*.hpp)
file(GLOB_RECURSE CPP_SOURCES ${MAIN_SOURCE_DIR}/*.cpp)
add_library(
yanthra
SHARED
${CPP_HEADERS}
${CPP_SOURCES}
)
set (CMAKE_SHARED_LINKER_FLAGS "-F../yanthra_engine/Frameworks -framework SDL2 -framework OpenGL")
sandbox/CMakeLists.txt
set(THIRD_PARTY_DIR "../../main")
set(MAIN_SOURCE_DIR "./src")
include_directories(${THIRD_PARTY_DIR}/include)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../out)
file(GLOB_RECURSE CPP_HEADERS ${MAIN_SOURCE_DIR}/includes/*.h)
file(GLOB_RECURSE CPP_SOURCES ${MAIN_SOURCE_DIR}/*.cpp)
file(GLOB_RECURSE LIB ${MAIN_SOURCE_DIR}/../lib/*.dylib)
add_executable(
yanthra_sandbox
${CPP_HEADERS}
${CPP_SOURCES}
)
set_target_properties(
yanthra_sandbox
PROPERTIES
LINK_FLAGS
"-F../yanthra_engine/Frameworks -framework SDL2 -framework OpenGL"
)
target_link_libraries(yanthra_sandbox PRIVATE ${LIB})
I would like to know if I'm generating library file in each build mode, how will I link it with it's corresponding executable, given the fact that each mode builds its output to to its own folder i.e for library its lib/Debug (for debug mode) and for executable its out/Debug.
You don't seem to link the link the yanthra target. You should do this though, since this will automatically choose the library compiled with the current configuration.
sandbox/CMakeLists.txt
...
target_link_libraries(yanthra_sandbox PRIVATE yanthra ${LIB})
...
As for importing the other libs: It would be preferrable to use find_library. This should automatically set the link options and make adding them for yanthra_sandbox unnecessary.
yanthra_engine/CMakeLists.txt
...
list(APPEND CMAKE_FRAMEWORK_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Frameworks")
find_library(SDL2_LIB SDL2 REQUIRED)
find_library(OPEN_GL_LIB OpenGL REQUIRED)
target_link_library(yanthra PUBLIC "${SDL2_LIB}" "${OPEN_GL_LIB}")
This should allow you to remove the compiler flags from both targets. If there are no dependencies in the lib directory you could also remove the search for the libraries on the file system/
I'm writing code in C++ and I need to read a file, problem is my file structure is a bit complex and I just cannot figure out how to use my ifstream to read it. I think I tried all possible combinations... but it just doesn't work, I guess I'm doing something wrong but I can't figure it out.
Here is a minimal reproduction of my problem.
structure :
.
├── build
├── CMakeLists.txt
└── src
├── file
│ └── test.txt
├── load
│ └── loadfile.hpp
└── main.cpp
CMakeLists.txt :
cmake_minimum_required(VERSION 3.10)
project(BaseProject)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-O3 -g -std=c++17 -Wall -Wextra -pedantic")
file(GLOB SRC
"src/*.h"
"src/*.hpp"
"src/load/*.h"
"src/load/*.hpp"
"src/load/*.cpp"
)
add_executable(exec ${SRC} src/main.cpp)
main.cpp
#include "load/loadfile.hpp"
int main(){
load();
return 0;
}
test.txt : (not very relevent but meh)
test
loadfile.hpp
#include <fstream>
#include <string>
#include <iostream>
void loadFile(const std::string& file){
std::ifstream i(file, std::ifstream::in);
std::string str;
i >> str;
std::cerr << str;
i.close();
}
void load(){
loadFile("../file/test.txt");
}
output is empty and program finishes normally.
This might help, when you call install your files will be copied to destination specified.
cmake_minimum_required(VERSION 3.10)
project(BaseProject)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-O3 -g -std=c++17 -Wall -Wextra -pedantic")
set(OUTPUT_DIR "Yours to fill, working dir")
file(GLOB RESOURCES "./file/test.txt")
file(GLOB SRC
"src/*.h"
"src/*.hpp"
"src/load/*.h"
"src/load/*.hpp"
"src/load/*.cpp"
)
add_executable(exec ${SRC} src/main.cpp)
install (FILES ${RESOURCES} DESTINATION ${OUTPUT_DIR})
Answer from Some programmer dude in the comments:
Now might be a good time to learn about the concept of current working
directory. When you run a program, its process will have a current
working directory. If you run from a console or terminal then it's
usually the terminals current directory. Relative paths (paths not
beginning with a /) are always relative to the current working
directory. You need to make sure that the relative path in the program
is valid for the programs current working directory when running.
I'm using CMake with my project and set up a cdash server for continuous/nightly building. Everything works well and by setting up a crontab, we have hourly/nightly build/test results uploaded to our cdash server automatically.
My next step is to add test coverage report to the build. I find the document here https://gitlab.kitware.com/cmake/community/-/wikis/doc/ctest/Coverage but frankly it's a bit far from a practical guide.
Currently I've added the required flag (instead of -fprofile-arcs -ftest-coverage, I find --coverage better), the compilation process generates .gcno files. But then I'm stuck. The command
make NightlyCoverage
doesn't seem to do anything. Could anybody tell me what is the next to do? The result that I want, is by doing make NightlyCoverage, coverage reports are generated and uploaded to cdash server.
I've been using https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake successfully.
Just followed the guidelines: added the files to my CMAKE_MODULE_PATH directory, added
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
if(CMAKE_COMPILER_IS_GNUCXX)
include(CodeCoverage)
setup_target_for_coverage(${PROJECT_NAME}_coverage ${PROJECT_TEST_NAME} coverage)
endif()
in my CMakeLists.txt. I also added manually gcov as a dependency for my target:
if(CMAKE_COMPILER_IS_GNUCXX)
target_link_libraries(${PROJECT_TEST_NAME} gcov)
endif()
With this, I just type
make my_project_coverage
and I get the html report in the coverage directory of my build tree.
I use gcovr to make a GCC Code Coverage Report without the CodeCoverage.cmake :
$ cd /path/to/your/project
$ mkdir build && cd build && cmake ..
$ make && make test
$ gcovr -r ../ .
I set up my project 'foo' in the following way. Copied the cmake file from the https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake to a subdirectory 'cmake_modules'. In the CMakeLists.txt file after the add_executable(foo ...) I added the following:
if(CMAKE_COMPILER_IS_GNUCXX)
LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
include(CodeCoverage)
APPEND_COVERAGE_COMPILER_FLAGS()
set(COVERAGE_LCOV_EXCLUDES 'dir1/*' 'dir2/*') // this is optional if you want to exclude some directory from the report
SETUP_TARGET_FOR_COVERAGE_LCOV(NAME foo_coverage
EXECUTABLE foo
DEPENDENCIES foo)
endif()
After cmake, build the target
make
make foo_coverage
And open the report with index.html file in the foo_coverage folder in the build folder
This answer is the same as #rcomblen's answer but its a bit outdated so I'll share my solution. Here's what I did:
created a toy project
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ tree -L 2
.
├── CMakeLists.txt
├── cmake
│ └── CodeCoverage.cmake
├── src
│ ├── domath.cpp
│ ├── domath.h
│ └── testdomath.cpp
└── third_party
└── googletest
Where
// src/domath.h
#ifndef COVERAGETEST_DOMATH_H
#define COVERAGETEST_DOMATH_H
class domath {
public:
int add(int a, int b);
};
#endif //COVERAGETEST_DOMATH_H
and
// src/domath.cpp
#include "domath.h"
int domath::add(int a, int b) {
return a + b;
}
and
// src/testdomath.cpp
#include "gtest/gtest.h"
#include "domath.h"
TEST(DoMathTests, testAdd){
domath m;
int actual = m.add(4, 6);
ASSERT_EQ(10, actual);
}
download googletest and put it under third party directory
copy the gist so kindly shared by the other answers on this thread into cmake/CodeCoverage.cmake
install gcovr. This step is vital, because the other answers on this thread no longer work with the version of gcovr that I already had:
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ pip install gcovr
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ gcovr --version
gcovr 4.2
(base) ciaran#DESKTOP-K0APGUV:/mnt/d/CoverageTest$ which gcovr
/home/ciaran/miniconda3/bin/gcovr
Note, we need the output of which gcovr for the cmake script.
4) Write a cmake script that create a library, a test executable and use the CodeCoverage.cmake module:
cmake_minimum_required(VERSION 3.15)
project(CoverageTest)
set(CMAKE_CXX_STANDARD 14)
# setup googletest
add_subdirectory(third_party/googletest)
# create our library
add_library(DoMath STATIC src/domath.h src/domath.cpp)
add_dependencies(DoMath gtest gtest_main)
# create the test executable
add_executable(TestDoMath src/testdomath.cpp)
target_include_directories(TestDoMath PRIVATE third_party/googletest/googletest)
target_link_libraries(TestDoMath PRIVATE
DoMath gtest gtest_main)
add_dependencies(TestDoMath DoMath gtest gtest_main)
# now for coverage bits
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
if (CMAKE_COMPILER_IS_GNUCXX)
include(CodeCoverage)
append_coverage_compiler_flags()
# we need to turn off optimization for non-skewed coverage reports
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
# optional excludes - None needed here
# set(COVERAGE_EXCLUDES)
# importantly, set the path to the gcovr executable that you downladed
set(GCOVR_PATH "/home/ciaran/miniconda3/bin/gcovr")
# Works
setup_target_for_coverage_gcovr_xml(
NAME TestDoMathCoverageXml
EXECUTABLE TestDoMath
DEPENDENCIES TestDoMath DoMath
)
# Works
setup_target_for_coverage_gcovr_html(
NAME TestDoMathCoverageHtml
EXECUTABLE TestDoMath
DEPENDENCIES TestDoMath DoMath
)
# This one did not work for me:
setup_target_for_coverage_lcov(
NAME TestDoMathCoverageLcov
EXECUTABLE TestDoMath
DEPENDENCIES TestDoMath DoMath
)
endif ()
And thats it. Now just build the new targets.
Good luck.