First Commit
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
---
|
||||
aliases:
|
||||
- CMakeLists.txt
|
||||
---
|
||||
CMake is a great tool to build c++ projects across different platforms and manages all dependencies as well as how to install the project on different platforms. A great guide to modern CMake can be found [here](https://cliutils.gitlab.io/modern-cmake/). And a good example project [here](https://gitlab.com/CLIUtils/modern-cmake/-/tree/master/examples/extended-project).
|
||||
|
||||
It also integrates well with the [[GTest Framework|GoogleTest]] framework, which allows to define the tests in a separate folder. They are built together with the project and executed using `ctest`.
|
||||
# CMake
|
||||
## Nomenclature
|
||||
| Definition | Meaning |
|
||||
| ---------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Target | Executables, libraries or custom commands that can be installed. |
|
||||
| Library | A collection of code that is compiled into `lib<name>.a` or `<name>.lib` and can be used in other projects by linking to it. |
|
||||
| Executable | Is a binary file that executes a certain program. On Windows usually it is `<name>.exe`. |
|
||||
## How does it work?
|
||||
On a high level, we want to understand what CMake does.
|
||||
|
||||
## Default Setup for Simple Projects
|
||||
In most projects so far, we define the project, look for the needed dependencies, define our executable or library, link the dependencies and finally install it to the system.
|
||||
For such a simple project the file tree looks like this:
|
||||
```sh
|
||||
├── CMakeLists.txt
|
||||
├── include
|
||||
│ └── some.h
|
||||
└── src
|
||||
├── some.cpp
|
||||
└── things.h
|
||||
```
|
||||
|
||||
For this a typical CMakeLists.txt file would look something like the following:
|
||||
- [ ] Add all the important commands here for cmake to have a small snippet to use in the future #todo/b
|
||||
```cmake
|
||||
project("SomeLibrary" VERSION 0.1.0)
|
||||
|
||||
add_library(${PROJECT_NAME} src/some.cpp)
|
||||
|
||||
```
|
||||
|
||||
# CMake Targets
|
||||
## Library
|
||||
A library is a code that can be imported and used in other code and thus usually the header files that define the public classes are shared in a raw format, whereas the implementation might just be compiled into a `.so` file (or similar). Typically these files are installed into `install_path/include/library_name/...` and `install_path/lib/library_name.so`, respectively (`.so` stands for shared object). For system installs (usually invoked with `sudo make install`) the `install_path` is `/usr/local/` (if not changed manually in the cmake configuration).
|
||||
|
||||
A good tutorial and overview of what is needed in cmake can be found [here](https://iamsorush.com/posts/cpp-cmake-config/) or in [this very good description](https://cliutils.gitlab.io/modern-cmake/chapters/install/installing.html). As a general overview we need to do the following things:
|
||||
1. add library target and all dependencies
|
||||
2. make the target including specifying where it will be installed (`DESTINATION` keyword)
|
||||
3. export the target to a `library_nameTargets.cmake` file (defines all cmake related stuff that I do not care about)
|
||||
4. In order to actually install the library to be found by cmake we need to have 3 files:
|
||||
1. `library_nameConfig.cmake`: we define it ourselves and import the next file
|
||||
2. `library_nameTargets.cmake`: this is automatically written by step 3
|
||||
3. `library_nameConfigVersion.cmake`: contains information about the version of the library
|
||||
To do this with CMake we need to do the following in the main `CMakeLists.txt` file:
|
||||
```cmake
|
||||
# 1. Add library target and dependencies
|
||||
add_library(library_name SHARED) # call to create a library target
|
||||
target_include_directories(library_name PRIVATE "${PROJECT_SOURCE_DIR}") # tell the target where to find important include files
|
||||
add_subdirectory("subdirectory_name") # add subdirectories if needed - with their own CMakeLists.txt files
|
||||
|
||||
# 2. Make Install target
|
||||
# Finally we need to install the library
|
||||
# this defines the library_nameTargets variable (because of EXPORT. Nothing is actually installed)
|
||||
install(TARGETS library_name
|
||||
EXPORT library_nameTargets # this file is written here and later included
|
||||
FILE_SET HEADERS
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
INCLUDES DESTINATION include)
|
||||
|
||||
|
||||
# 3. Export the install target
|
||||
# Now we define where the file will be installed, as well as defining a CMAKE namespace. This is saved to the
|
||||
install(EXPORT library_nameTargets
|
||||
FILE library_nameTargets.cmake
|
||||
NAMESPACE libName::
|
||||
DESTINATION lib/cmake/library_name)
|
||||
# if your project has no dependencies you can replace library_nameTargets.cmake with library_nameConfig.cmake and skip the last step (no. 5), because the needed file has already been written here.
|
||||
|
||||
|
||||
# 4. write the actual .cmake files
|
||||
include(CMakePackageConfigHelpers) # load helper to create config file
|
||||
|
||||
# creates file library_nameConfigVersion.cmake which is needed when you try to find a package in another project with find_package()
|
||||
write_basic_package_version_file(
|
||||
"library_nameConfigVersion.cmake"
|
||||
VERSION ${library_name_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion)
|
||||
|
||||
# 5. write the library_nameConfig.cmake file which is needed.
|
||||
# finally the install file library_nameConfig.cmake is actually copied over to library_nameConfig.cmake which is needed to find the library with find_package() in other projects
|
||||
install(FILES "library_nameConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/library_nameConfigVersion.cmake"
|
||||
DESTINATION lib/cmake/geo)
|
||||
```
|
||||
The file `library_nameConfig.cmake` contains the following:
|
||||
```cmake
|
||||
include(CMAKEFindDependencyMacro)
|
||||
# find_dependency(xxx 2.0) # if any dependencies are needed
|
||||
# this includes the Targets.cmake file thatis created in step 3 in the cmake file above.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/library_nameTargets.cmake)
|
||||
```
|
||||
|
||||
## Executable
|
||||
|
||||
# Uninstall Target
|
||||
The default sequence to install a cmake project is the following:
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --build .
|
||||
sudo make install
|
||||
```
|
||||
The last command will execute the installation which basically copies important files (as specified in the CMakeLists.txt) into a system directory (usually `/usr/local/`). When this happens a file called `install_manifest.txt` is created in the build folder which lists all installed files. In order to undo the installation you can run the [following command:](https://stackoverflow.com/a/44649542/7705525)
|
||||
```bash
|
||||
xargs rm < install_manifest.txt
|
||||
```
|
||||
|
||||
If you want to get fancier you can also create a [uninstall target](https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake), which basically iterates through the `install_manifest.txt` file and removes any file and folder (if empty) listed in the file.
|
||||
|
||||
# How is CMake used in ROS2 and Colcon
|
||||
ROS2 makes extensive use of `CMake` in its buildsystem. It is all hidden behind the `colcon` command, but the projects all contain `CMakeLists.txt`-files that define how a ROS2 package is compiled. A good example to look at source code is the [[ROS2 - NAV2 Library|NAV2]]-Library.
|
||||
- [ ] #todo/b add cmake specifics and how ament, and colcon uses it. Also how testing is done.
|
||||
- [ ] [Ament_cmake user documentation](https://docs.ros.org/en/rolling/How-To-Guides/Ament-CMake-Documentation.html) #todo/b
|
||||
- [ ]
|
||||
|
||||
# Flashcards
|
||||
#learning/cpp
|
||||
how to run unit tests (gtest) of a cmake project ;; `ctest` after having built the project
|
||||
<!--SR:!2023-12-10,14,290-->
|
||||
how to define a executable in a cmake project ;; `add_executable(name sourcefile1 sourcefile2 ...)`
|
||||
<!--SR:!2024-03-15,59,310-->
|
||||
how to define a library in a cmake project ;; `add_library(name sourcefile1 sourcefile2 ...)`
|
||||
<!--SR:!2023-12-09,13,290-->
|
||||
|
||||
# Resources
|
||||
- https://decovar.dev/blog/2021/03/08/cmake-cpp-library/
|
||||
- https://raymii.org/s/tutorials/Cpp_project_setup_with_cmake_and_unit_tests.html
|
||||
- https://iamsorush.com/posts/cpp-cmake-config/
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
aliases:
|
||||
- GoogleTest
|
||||
- GTest
|
||||
---
|
||||
Also known as gtest is a unit testing library for C++. It is also used by ROS2.
|
||||
|
||||
|
||||
# Flashcards
|
||||
#learning/cpp
|
||||
which GTest to use to compare two `std::string` objects? ;; `EXPECT_EQ(s1, s2)`
|
||||
<!--SR:!2023-12-12,16,290-->
|
||||
which GTest to use to compare two `char[]` objects? ;; `EXPECT_STREQ(s1, s2)`
|
||||
<!--SR:!2023-12-13,17,290-->
|
||||
difference between `ASSERT_EQ` and `EXPECT_EQ`;; **assert** will stop execution if it fails, **expect** will run all following code as well and only report at the end.
|
||||
<!--SR:!2024-03-08,52,314-->
|
||||
`Ctest` is not finding any tests ;; in `CMakeLists.txt`: `enable_testing()` needs to before `include_directories(test)`. Else it does not work
|
||||
<!--SR:!2024-01-25,9,274-->
|
||||
# Resources
|
||||
Get started quickly with [[CMake]]: https://gitlab.cern.ch/google/googletest/-/blob/master/docs/quickstart-cmake.md
|
||||
|
||||
A good resource:
|
||||
[IBM C++ Testing documentation](https://developer.ibm.com/articles/au-googletestingframework/#list2)
|
||||
@@ -0,0 +1,29 @@
|
||||
A good article: https://cplusplus.com/doc/tutorial/files/
|
||||
|
||||
# Key Takeaways
|
||||
* There are two modes: binary and text. Text mode formats (e.g. ASCII) the bytes being written (and vice versa when being read), whereas in binary mode this does not happen
|
||||
* There are three libraries: `fstream`, `ofstream`, `ifstream`. f: file, o: output, i: input
|
||||
* There are position pointers that keep track of where in the file we're writing to or reading from: they are called **put** and **get** position pointers respectively
|
||||
* In order to **read the pointers** use: `tellg()` and `tellp()` for get and put, respectively.
|
||||
* In order to **change the pointers** use: `seekg(position)` and `seekp(position)` for get and put, respectively. The arguments can also be `seekg(offset, direction)`
|
||||
* There are helper flags for the offset: `ios::beg` for beginning of file, `ios::cur` for current location in the file, `ios::end` for the end of the file
|
||||
|
||||
Example:
|
||||
```c++
|
||||
// obtaining file size
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
using namespace std;
|
||||
|
||||
int main () {
|
||||
streampos begin,end;
|
||||
ifstream myfile ("example.bin", ios::binary);
|
||||
begin = myfile.tellg();
|
||||
myfile.seekg (0, ios::end);
|
||||
end = myfile.tellg();
|
||||
myfile.close();
|
||||
cout << "size is: " << (end-begin) << " bytes.\n";
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user