├── .gitignore ├── myLib ├── include │ └── library.h ├── src │ └── library.c └── CMakeLists.txt ├── myApp ├── main.c └── CMakeLists.txt ├── myLibTest ├── minunit.h ├── main.c └── CMakeLists.txt ├── LICENSE ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/* -------------------------------------------------------------------------------- /myLib/include/library.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int32_t TestFunction(void); -------------------------------------------------------------------------------- /myLib/src/library.c: -------------------------------------------------------------------------------- 1 | #include "library.h" 2 | 3 | int32_t TestFunction(void) { 4 | return 0; 5 | } -------------------------------------------------------------------------------- /myApp/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "library.h" 4 | 5 | int main(/*int argc, char** argv*/) 6 | { 7 | printf("Library function output: %d\n", TestFunction()); 8 | return 0; 9 | } -------------------------------------------------------------------------------- /myLibTest/minunit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define mu_assert(message, test) do { if (!(test)) return message; } while (0) 4 | #define mu_run_test(test) do { char *message = test(); tests_run++; if (message) return message; } while (0) 5 | extern int tests_run; -------------------------------------------------------------------------------- /myApp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is the CMake file for the app in this folder. You should have 2 | # arrived here coming from the end of the top-level CMakeLists.txt file. 3 | 4 | # The following commands work exactly as in the case for tests. See the 5 | # CMakeLists.txt file there for further explanation. 6 | 7 | add_executable(myApp) 8 | target_sources(myApp 9 | PRIVATE 10 | main.c 11 | ) 12 | target_link_libraries(myApp PRIVATE mylib) 13 | -------------------------------------------------------------------------------- /myLibTest/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "minunit.h" 4 | 5 | #include "library.h" 6 | 7 | int tests_run = 0; 8 | 9 | static char* all_tests() { 10 | mu_assert("TestFunction should return 0", TestFunction() == 0); 11 | return 0; 12 | } 13 | 14 | int main(/*int argc, char** argv*/) 15 | { 16 | char *result = all_tests(); 17 | if (result != 0) { 18 | printf("%s\n", result); 19 | } 20 | else { 21 | printf("ALL TESTS PASSED\n"); 22 | } 23 | printf("Tests run: %d\n", tests_run); 24 | return result != 0; 25 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sebastian Schöner 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /myLibTest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is the CMake file for the tests in this folder. You should have 2 | # arrived here coming from the end of the top-level CMakeLists.txt file. 3 | 4 | # Add an executable build target for the tests. This will be used to build the 5 | # executable that actually runs the code. 6 | # 7 | # Find more information here: 8 | # https://cmake.org/cmake/help/latest/command/add_executable.html 9 | add_executable(mylibTest) 10 | 11 | # Add the sources as before. 12 | target_sources(mylibTest 13 | PRIVATE 14 | main.c 15 | ) 16 | 17 | # The test executable will of course depend on the library that we actually 18 | # want to test. Note that `PRIVATE` is specified to indicate that whoever might 19 | # depend on this build target should not also depend on `mylib` (although it 20 | # is unlikely that someone depends on the tests, it is still good practice). 21 | # 22 | # Find more information here: 23 | # https://cmake.org/cmake/help/latest/command/target_link_libraries.html 24 | target_link_libraries(mylibTest PRIVATE mylib) 25 | 26 | # Add the executable that we have just created as a test. This instructs CTest 27 | # to include it when running tests. 28 | # 29 | # Find more information here: 30 | # https://cmake.org/cmake/help/latest/command/add_test.html 31 | add_test(myTest mylibTest) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is the main entry point for CMake. Follow the comments below to get 2 | # a better understanding of what is happening. 3 | 4 | 5 | # This sets the minimum cmake version required. The FATAL_ERROR option forces 6 | # some really old CMake versions to fail instead of just emiting a warning. 7 | # 8 | # Find more information here: 9 | # https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html 10 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 11 | 12 | # This sets up the project. We're giving it a name, a version, a description, 13 | # a URL, and what languages to use. The usual languages to use are 14 | # * C (for plain C), 15 | # * and CXX (for C++). 16 | # The project command also sets some variables for the project that we can 17 | # ignore for now. 18 | # 19 | # Find more information here: 20 | # https://cmake.org/cmake/help/latest/command/project.html 21 | project("myLib" 22 | VERSION 0.1.0 23 | DESCRIPTION "A library of sorts" 24 | LANGUAGES C 25 | ) 26 | 27 | # CMake allows you to output debug information during the build process. 28 | # Here, we are outputting the value of the variable PROJECT_SOURCE_DIR that 29 | # was set by the `project` directive above. 30 | # This line is, of course, completely unnecessary, so you can safely remove it. 31 | message("Set project source dir: ${PROJECT_SOURCE_DIR}") 32 | 33 | # This enables the CTest module of CMake. It allows you to easily add tests to 34 | # your project and report the results back to a build server, if needed. We 35 | # will not be making use of the latter feature here. 36 | # 37 | # Find more information here: 38 | # https://cmake.org/cmake/help/latest/module/CTest.html 39 | include(CTest) 40 | 41 | # This tells CTest that we are actually want to run tests. This directive needs 42 | # to be in the top-level CMake file (this one). 43 | enable_testing() 44 | 45 | # Now we will set up some compiler options. The assumption is that we are 46 | # either using Clang/GCC or MSVC. The flags have two effects: 47 | # * enable all (or at least a reasonable amount of) warnings, 48 | # * treat every warning as an error. 49 | if (MSVC) 50 | # Sets compile options for this directory and all subdirectories. 51 | add_compile_options(/W4 /WX) 52 | else() 53 | add_compile_options(-W -Wall -pedantic -Werror) 54 | endif() 55 | 56 | # This adds the directives from the CMakeLists.txt files from the given 57 | # subdirectories. Go there now to read them. 58 | add_subdirectory(myLib) 59 | add_subdirectory(myLibTest) 60 | add_subdirectory(myApp) 61 | -------------------------------------------------------------------------------- /myLib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is the CMake file for the library in this folder. You should have 2 | # arrived here coming from the end of the top-level CMakeLists.txt file. 3 | 4 | # Add a library as a build target. This directive says that we want a static 5 | # library named `mylib`. We will specify the source files used in that library 6 | # further down. 7 | # Build targets in CMake are added via either `add_library` or via 8 | # `add_executable`. Each build target will be available to be build later in 9 | # the generated actual build scripts. Build targets can have properties 10 | # attached to them, such as what source files are related to them, what 11 | # includes they offer, their dependencies etc. 12 | # It is generally preferred to bind any sort of information to a build target 13 | # instead of having it in a globally available variable, so make sure to 14 | # always check whether there is a command starting with `target_` available. 15 | # 16 | # Find more information here: 17 | # https://cmake.org/cmake/help/latest/command/add_library.html 18 | add_library(mylib STATIC) 19 | 20 | # We need to collect all files to compile for the library that we have just 21 | # added. This sets the `SOURCES` property of the `mylib` build target. 22 | # 23 | # Note that we could also have included the source files in the call to 24 | # `add_library`, but this here is more explicit and hence my recommendation. 25 | # Note the usage of `PRIVATE` here, which specifies that the sources are not 26 | # visible to other build targets. 27 | # 28 | # When you have more files, this could look like this instead: 29 | # 30 | # target_sources( mylib 31 | # PRIVATE 32 | # src/library.c 33 | # src/fileA.c 34 | # src/fileB.c 35 | # ) 36 | # 37 | # Find more information here: 38 | # https://cmake.org/cmake/help/latest/command/target_sources.html 39 | target_sources( mylib 40 | PRIVATE 41 | src/library.c 42 | ) 43 | 44 | # Finally, let's specify the include directories used by the library. There are 45 | # three different kinds of includes that we might want to specify: 46 | # * PRIVATE includes -- these are includes that are visible to this build 47 | # target. 48 | # * INTERFACE includes -- these are includes that are visible to every other 49 | # build target that depends on this target. 50 | # * PUBLIC includes -- this is a combination of PRIVATE and INTERFACE. 51 | # These three kinds actually make sense for build target properties in general 52 | # and hence permeates through all of CMake. 53 | # In our case, we do not need INTERFACE-only includes, so they are not 54 | # specified. 55 | # This will specifc that the `include` directory is available publicly where as 56 | # anything in `src` is available to be included from inside the library. 57 | target_include_directories( mylib 58 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include 59 | PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src 60 | ) 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C/CPP with CMake - example project 2 | This is an example project that illustrates how to use CMake >= 3.14 with C. The setup that I am assuming is the following: 3 | * Our program consists of three parts: 4 | * a library (myLib) living in `myLib`, 5 | * an application (myApp) living in `myApp` using that library, 6 | * tests for the library living in `myLibTest`. 7 | * We do not use an external library for testing but want to write the unit tests on our own. 8 | * We are only interested in getting our project to compile/run/pass tests. This repository does not cover installation/deployment. 9 | 10 | ## What is CMake? 11 | Uh, I find that not entirely easy to answer. To me, CMake is primarily a tool for producing build scripts (think `make` files or VS/XCode projects) that are then processed by said build system to produce an artifact (a library, executable...). CMake can also be used to generate scripts for packing and installing the artifact (think `make install`), but this is not what I am interested in here. 12 | So, in short, CMake helps with: 13 | * configuring your project for different compilers and build systems, 14 | * handling dependencies between your projects, 15 | * locating external dependencies (not covered), 16 | * running your tests and reporting their results, 17 | * deploying your software (not covered). 18 | 19 | ## Usage 20 | The main purpose of this repository is educational. Start by reading `CMakeLists.txt` from this repository. It is heavily commented and will point you to other files. Once you are done with this, here is how to actually execute the code: 21 | 22 | 1. Create a directory `build` (or any other name, really, but this is the name chosen by convention) to contain all of the files build by CMake plus some of its metadata and caches: 23 | ``` 24 | mkdir build 25 | ``` 26 | 2. Switch to the directory and execute `cmake ..` which will tell CMake to take the source from the parent directory and use the current directory as the build directory: 27 | ``` 28 | cd build 29 | cmake .. 30 | ``` 31 | You should see some output from CMake, telling you what compiler it is using etc. CMake will choose a sensible default build system to produce build scripts or similar for. Usually, this is either `make`, Visual Studio, or XCode. You can change this by using 32 | ``` 33 | cmake -G"Ninja" .. 34 | ``` 35 | for example. Visit [this page](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) for more information. 36 | CMake also automatically creates configurations for debug and release builds. You can ask for a specific version like so: 37 | ``` 38 | cmake -DCMAKE_BUILD_TYPE=Release .. 39 | ``` 40 | 3. Create a build from there with the build system that you have chosen. For make, this is simply a case of running 41 | ``` 42 | make 43 | ``` 44 | 4. Run the tests by using `ctest`: 45 | ``` 46 | ctest 47 | ``` 48 | This will show you the status of your tests. 49 | 50 | ### TL;DR for `make` 51 | ``` 52 | mkdir build 53 | cd build 54 | cmake .. 55 | make 56 | ctest 57 | ``` 58 | 59 | ## Older CMake Versions 60 | This project will not work out of the box with CMake <= 3.10 because the `add_library` and `add_executable` directives before 3.11 always require source files. The easiest way to fix this is to, well, add the source files to those directives itself like so: 61 | ``` 62 | add_executable(myApp 63 | main.c 64 | # potentially more source files here 65 | ) 66 | ``` 67 | 68 | ## Changes required for C++ 69 | If you are using this template with C++, you might want to change the tests: They do not use `const char*` right now for their messages and you likely need to change that to make it work with C++11 (for good reason) 70 | --------------------------------------------------------------------------------