The used code is based on a „cmake“ tutorial example,
but converted from „C++“ to „C“.
The program will be split into several sources in the same directory.
|
1 2 3 4 5 6 7 |
project_root/ ├── CMakeLists.txt # Root CMakeLists ├── main.c # Test program └── mathfunctions/ ├── CMakeLists.txt # Subdirectory CMakeLists ├── mysqrt.c # Implementation └── mysqrt.h # Header file |
The root „CMakeLists.txt“ (without verbose messages):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
cmake_minimum_required(VERSION 3.10) project( sqrt_test LANGUAGES C ) # Set C standard only set( CMAKE_C_STANDARD 11 ) set( CMAKE_C_STANDARD_REQUIRED ON ) # Add the mathfunctions subdirectory (creates library target) # additional functions need separate add_subdirectory() entries add_subdirectory(mathfunctions) # Create the test executable add_executable( ${CMAKE_PROJECT_NAME} main.c ) # Link the mathfunctions library to the executable target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE mathfunctions ) # Link the math library for any additional math functions target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE m ) |
The „main.c“ source code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <stdio.h> #include <stdlib.h> #include “mysqrt.h” int main(int argc, char *argv[]) { if (argc < 2) { printf(“Usage: %s number\n”, argv[0]); return 1; } /* Convert input to double */ char *endptr; double inputValue = strtod(argv[1], &endptr); /* Validate conversion */ if (*endptr != ‘\0’ || endptr == argv[1]) { printf(“Error: ‘%s’ is not a valid number.\n”, argv[1]); return 1; } /* Calculate square root using our library function */ double outputValue = mathfunctions_sqrt(inputValue); printf(“\nFinal result: \nThe square root of %.2f is %.2f\n”, inputValue, outputValue); return 0; } |
The sub-directory „mathfunctions/CMakeLists.txt“:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# Create a static library from the math functions add_library( mathfunctions STATIC mysqrt.c ) # Include directories for the library target_include_directories( mathfunctions PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) # just to show the CMakeLists.txt is recognized message( “The mysqrt files are in: ${CMAKE_CURRENT_SOURCE_DIR}” ) # Link the math library (needed for sqrt, pow, etc. if used) target_link_libraries( mathfunctions PUBLIC m ) |
The sources „mysqrt.h“ and „mysqrt.c“:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// mysqrt.h #ifndef MYSQRT_H #define MYSQRT_H #ifdef __cplusplus extern “C” { #endif /* Public API: square root function */ double mathfunctions_sqrt(double x); #ifdef __cplusplus } #endif #endif /* MYSQRT_H */ |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// mysqrt.c #include <stdio.h> #include “mysqrt.h” /* Internal helper function – static limits visibility to this file */ static double mysqrt_internal(double x) { if (x <= 0) { return 0; } double result = x; /* Do ten iterations */ for (int i = 0; i < 10; ++i) { if (result <= 0) { result = 0.1; } double delta = x – (result * result); result = result + 0.5 * delta / result; printf(“Computing sqrt of %f to be %f\n”, x, result); } return result; } /* Public wrapper function */ double mathfunctions_sqrt(double x) { return mysqrt_internal(x); } |
The compile steps:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# go to directory cd learning_cmake/my_cmake_code_examples/step1_b_ansi_c/ # cleansing the build directory rm –rf build/ # configure cmake cmake –B build | tee cmake_conf.txt # compile cmake —build build | tee cmake_build.txt # with cleansing (optional) cmake —build build —clean–first | tee cmake_build.txt # execute with build/sqrt_test 16 |
Now, in preparation/understanding how to extend a program with additional functions in sub-directories I will add a function calculating the input values third power.
We are learning Now, in preparation/understanding how to extend a program with additional functions in sub-directories I will add a function calculating the input values third power.
We are learning „cmake“ not „C“!
The extended Project Structure:
|
1 2 3 4 5 6 7 8 9 10 11 |
project_root/ ├── CMakeLists.txt # Root CMakeLists ├── main.c # Test program ├── mathfunctions │ ├── CMakeLists.txt # Subdirectory CMakeLists │ ├── mysqrt.c │ └── mysqrt.h ├── my_math_fnctns ├── CMakeLists.txt # Subdirectory CMakeLists ├── thirdpower.c └── thirdpower.h |
The extended root „CMakeLists.txt“:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
cmake_minimum_required( VERSION 3.10 ) project( sqrt_test LANGUAGES C ) # Set C standard set( CMAKE_C_STANDARD 11 ) set( CMAKE_C_STANDARD_REQUIRED ON ) # Add the mathfunctions subdirectory (creates library target) add_subdirectory( mathfunctions ) # added # add the my_math_functions subdirectory add_subdirectory( my_math_fnctns ) # Create the test executable add_executable( ${CMAKE_PROJECT_NAME} main.c ) # Link the mathfunctions library to the executable target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE mathfunctions ) #added target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE my_math_fnctns ) # Link the math library for any additional math functions target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE m ) |
The extended „main.c“:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include <stdio.h> #include <stdlib.h> #include “mysqrt.h” // added #include “thirdpower.h” int main(int argc, char *argv[]) { if (argc < 2) { printf(“Usage: %s number\n”, argv[0]); return 1; } /* Convert input to double */ char *endptr; double inputValue = strtod(argv[1], &endptr); /* Validate conversion */ if (*endptr != ‘\0’ || endptr == argv[1]) { printf(“Error: ‘%s’ is not a valid number.\n”, argv[1]); return 1; } // added printf(“=== Computing with input: %.2f ===\n\n”, inputValue); /* Calculate square root using our library function */ double outputValue = mathfunctions_sqrt(inputValue); // added /* Calculate cube using our second library function */ double cubeValue = my_math_fnctns_cubed(inputValue); printf(“\n”); // added /* Display final results */ printf(“=== Final Results ===\n”); printf(“\nFinal result: \nThe square root of %.2f is %.2f\n”, inputValue, outputValue); // added printf(“Cube of %.2f is %.2f\n”, inputValue, cubeValue); return 0; } |
The additional „my_math_fnctns/CMakeLists.txt“:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Create a static library from the math functions add_library( my_math_fnctns STATIC thirdpower.c ) # Include directories for the library target_include_directories( my_math_fnctns PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) # No special math library needed for simple multiplication |
The additional sources „thirdpower.h“ and „thirdpower.c“:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// thirdpower.h #ifndef THIRDPOWER_H #define THIRDPOWER_H #ifdef __cplusplus extern “C” { #endif /* Public API: cube function */ double my_math_fnctns_cubed(double x); #ifdef __cplusplus } #endif #endif /* THIRDPOWER_H */ |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// thirdpower.c #include <stdio.h> #include “thirdpower.h” /* Internal helper function – static limits visibility */ static double cubed_internal(double x) { double result = x * x * x; printf(“Computing cube of %f to be %f\n”, x, result); return result; } /* Public wrapper function */ double my_math_fnctns_cubed(double x) { return cubed_internal(x); } |
The compile steps:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# go to directory cd learning_cmake/my_cmake_code_examples/step1_b_ansi_c/ # cleansing the build directory # this is needed now, because of the additional source rm –rf build/ # configure cmake cmake –B build | tee cmake_conf.txt # compile cmake —build build | tee cmake_build.txt # with cleansing cmake —build build —clean–first | tee cmake_build.txt # execute with $ $ build/sqrt_test 16 |
The execution should return something look like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$ build/sqrt_test 16 === Computing with input: 16.00 === Computing sqrt of 16.000000 to be 8.500000 Computing sqrt of 16.000000 to be 5.191176 Computing sqrt of 16.000000 to be 4.136665 Computing sqrt of 16.000000 to be 4.002258 Computing sqrt of 16.000000 to be 4.000001 Computing sqrt of 16.000000 to be 4.000000 Computing sqrt of 16.000000 to be 4.000000 Computing sqrt of 16.000000 to be 4.000000 Computing sqrt of 16.000000 to be 4.000000 Computing sqrt of 16.000000 to be 4.000000 Computing cube of 16.000000 to be 4096.000000 === Final Results === Final result: The square root of 16.00 is 4.00 Cube of 16.00 is 4096.00 |
One more thing needs to be implemented and tested.
How to use functions specified in a directory outside
of the project structure for common usage by different projects?
For answering this I will create a new example in the next chapter.
A third example using external libraries
Some aditional remarks:
The key „cmake“ concepts for multiple subdirectories
| Concept | Explanation |
|---|---|
| „add_subdirectory()“ | Called once per subdirectory; creates a target |
| Target Names | Each „add_library()“ creates a unique target name („mathfunctions“, „my_math_fnctns“) |
| „target_link_libraries()“ | Links each library separately to your executable |
| Include Paths | Each library exposes its own headers via „target_include_directories()“ |
| Dependency Chain | Libraries can depend on each other if needed (not shown here) |
Common Patterns for Scaling
Adding a third subdirectory – just repeat the pattern.
In root „CMakeLists.txt“ add „add_subdirectory(your_third_dir)“ f.e.:
|
1 2 3 4 5 |
target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE your_third_lib ) |
If libraries depend on each other:
|
1 2 3 4 5 6 7 |
# if mathfunctions needs „my_math_fnctns“ target_link_libraries( mathfunctions PRIVATE my_math_fnctns ) |
Using „find_package()“ instead.
For larger projects, you might use „find_package()“ to locate pre-built libraries:
|
1 2 3 4 5 6 7 8 9 |
find_package( MathFunctions REQUIRED ) target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE MathFunctions ) |
I will use these suggestions later when creating a real peoject for the RP2xxx.