With this example I am getting closer to my project structure.
Understanding this step should help in future projects to use existing libraries located anywhere in the accessible directories, for instance existing Arduino libraries, without the need to copy them.
I will use an implementation of calculating Fibonacci numbers within limits.
The code and the libraries are somewhere in my home directory.
|
1 2 |
$ echo $HOME /var/home/kps |
Here exists a directory „learning_cmake“ and within all projects and libraries are placed:
|
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 |
learning_cmake/ └── my_cmake_code_examples ├── common_libraries │ └── fibonacci │ ├── CMakeLists.txt │ ├── fibo.c │ └── fibo.h ├── step0_ansi_c │ ├── build │ ├── CMakeLists.txt │ ├── compile_steps.txt │ ├── hello_world.c │ └── step0_ansi_c.geany ├── step1_a_ansi_c │ ├── build │ ├── CMakeLists.txt │ ├── compile_steps.txt │ ├── main.c │ └── step1_a_ansi_c.geany ├── step1_b_ansi_c │ ├── build │ ├── CMakeLists.txt │ ├── compile_steps.txt │ ├── main.c │ ├── main (Copy).c │ ├── mathfunctions │ │ ├── CMakeLists.txt │ │ ├── mysqrt.c │ │ └── mysqrt.h │ ├── my_math_fnctns │ │ ├── CMakeLists.txt │ │ ├── thirdpower.c │ │ └── thirdpower.h │ └── step1_b_ansi_c.geany └── step1_c_ansi_c ├── build ├── CMakeLists.txt ├── compile_steps.txt ├── main.c ├── mathfunctions │ ├── CMakeLists.txt │ ├── mysqrt.c │ └── mysqrt.h ├── my_math_fnctns │ ├── CMakeLists.txt │ ├── thirdpower.c │ └── thirdpower.h └── step1_c_ansi_c.geany |
This shows the evolution of the learning process from a simple single file approach, split to several files in sub-directories till using a common library path.
Her are the details about the common library with that fibonacci function.
The „common_libraries/fibonacci/CMakeLists.txt“ file:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
```cmake # the local CMakeLists.txt # Create a static library named ‘fibonacci’ from fibo.c add_library( fibonacci STATIC fibo.c ) # Make the header file available to anyone linking this library # PUBLIC means the include directory is passed to the executable # AND any other library linking to ‘fibonacci’ target_include_directories( fibonacci PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) # just for claryrification message( “This message comes from the external library in: ${CMAKE_CURRENT_SOURCE_DIR}” ) |
The header file „common_libraries/fibonacci/fibo.h“:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* the header */ #ifndef FIBO_H #define FIBO_H #include <stdio.h> #include <limits.h> // Function to calculate the nth Fibonacci number // Returns -1 if n is negative or if the result overflows long long fibonacci(int n); // Function to print the sequence up to the largest safe integer void print_fibonacci_sequence(void); #endif // FIBO_H |
The sorce code „common_libraries/fibonacci/fibo.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 |
/* the c source */ #include “fibo.h” long long fibonacci(int n) { // calculation makes sense only when values are >= 2 if (n < 0) return –1; if (n == 0) return 0; if (n == 1) return 1; long long prev = 0; long long curr = 1; for (int i = 2; i <= n; ++i) { // Check for overflow before adding if (curr > LLONG_MAX – prev) { return –1; // Overflow detected } long long next = prev + curr; prev = curr; curr = next; } return curr; } void print_fibonacci_sequence(void) { printf(“Fibonacci Sequence (up to largest safe integer):\n”); int n = 0; long long val; while ((val = fibonacci(n)) != –1) { printf(“F(%d) = %lld\n”, n, val); n++; } printf(“Stopped at F(%d) due to overflow.\n”, n); } |
This example uses a copy of the previous example.
Only the „CMakeLists.txt“ and the „main.c“ files needs to be modified.
Here are these modified files.
The file „step1_c_ansi_c/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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
cmake_minimum_required( VERSION 3.28 ) project( sqrt_test LANGUAGES C ) # Set C standard set( CMAKE_C_STANDARD 11 ) set( CMAKE_C_STANDARD_REQUIRED ON ) # add the references to external libraries # — External Library Configuration — # Define the base path for external libraries. # Defaults to ~/common_libraries, but can be overridden: cmake -DEXTERNAL_LIB_DIR=/custom/path set( EXTERNAL_LIB_DIR “$ENV{HOME}/learning_cmake/my_cmake_code_examples/common_libraries” CACHE PATH “Base directory for external libraries” ) # Check if the fibonacci library exists at the expected location set( FIBO_LIB_PATH “${EXTERNAL_LIB_DIR}/fibonacci” ) if(EXISTS “${FIBO_LIB_PATH}/CMakeLists.txt”) message(STATUS “Found external fibonacci library at: ${FIBO_LIB_PATH}”) # Add the external library as a subdirectory # The second argument ‘fibonacci_ext’ is the alias for the subdirectory scope (optional but good practice) add_subdirectory(“${FIBO_LIB_PATH}” fibonacci_ext) # Flag to track if we successfully loaded it set(HAS_FIBO_LIB TRUE) else() message(WARNING “External fibonacci library not found at ${FIBO_LIB_PATH}. Skipping.”) set(HAS_FIBO_LIB FALSE) endif() ########################################## # — Internal Libraries — # Add the mathfunctions subdirectory (creates library target) add_subdirectory( mathfunctions ) # added # add the my_math_functions subdirectory add_subdirectory( my_math_fnctns ) # — Executable — # Create the test executable add_executable( ${CMAKE_PROJECT_NAME} main.c ) # Link internal libraries # Link the local mathfunctions, my_math_fnctns libraries to the executable target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE mathfunctions my_math_fnctns ) # obviously we can call with one statement # Link external fibonacci library if it was found # HAS_FIBO_LIB was specified above if(HAS_FIBO_LIB) target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE fibonacci ) endif() # Link the math library for any additional math functions target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE m ) # the following is optional # Add a custom command to generate the assembly listing after the executable is built # This runs ‘objdump -d’ on the final linked binary and saves it to a .s file # — Post-Build Assembly Generation — add_custom_command( TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND objdump –d $<TARGET_FILE:${CMAKE_PROJECT_NAME}> > ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.s COMMENT “Generating assembly listing for ${CMAKE_PROJECT_NAME}” VERBATIM ) message( “This message comes from the root CMakeLists.txt in: ${CMAKE_CURRENT_SOURCE_DIR}” ) |
„cmake“ reads the files in a linear order.
This implies some commands must be in a certain order.
Right after setting the „C“ standard we add the reference to the external library.
The „EXTERNAL_LIB_DIR“ references
„$ENV{HOME}/learning_cmake/my_cmake_code_examples/common_libraries“.
The first part „$ENV{HOME}“ expands to „/var/home/kps“ which is my home directory.
The complete reference looks like this:
„/var/home/kps/learning_cmake/my_cmake_code_examples/common_libraries“
The same approach may be used when using libraries from the Arduino world, because they are placed in a well known way.
In my case:
„/var/home/kps/programming/arduino_libraries/libraries/“
But this will be used in one of the future examples when using these libraries without the Arduino IDE.
After specifying this path it can be used in „set()“ command to create a variable „FIBO_LIB_PATH“.
|
1 2 3 4 5 6 7 8 9 10 11 |
set( EXTERNAL_LIB_DIR “$ENV{HOME}/learning_cmake/my_cmake_code_examples/common_libraries” CACHE PATH “Base directory for external libraries” ) # Check if the fibonacci library exists at the expected location set( FIBO_LIB_PATH “${EXTERNAL_LIB_DIR}/fibonacci” ) |
As always in programming it is necessary to check if a value is correct or an item exists.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
if(EXISTS “${FIBO_LIB_PATH}/CMakeLists.txt”) message(STATUS “Found external fibonacci library at: ${FIBO_LIB_PATH}”) # Add the external library as a subdirectory # The second argument ‘fibonacci_ext’ is the alias for the subdirectory scope (optional but good practice) add_subdirectory(“${FIBO_LIB_PATH}” fibonacci_ext) # Flag to track if we successfully loaded it set(HAS_FIBO_LIB TRUE) else() message(WARNING “External fibonacci library not found at ${FIBO_LIB_PATH}. Skipping.”) set(HAS_FIBO_LIB FALSE) endif() |
After all this we add the internal libraries one by one and tell the linker to include them.
Then we tell the linker to use the external library, too.
|
1 2 3 4 5 6 7 8 9 |
# Link external fibonacci library if it was found # HAS_FIBO_LIB was specified above if(HAS_FIBO_LIB) target_link_libraries( ${CMAKE_PROJECT_NAME} PRIVATE fibonacci ) endif() |
That’s it for the root „CMakeLists.txt“ file.
It should be possible to use the function „print_fibonacci_sequence()“ anywhere in the „main.c“ file.
The up to now complete „step1_c_ansi_c/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 51 52 53 54 55 56 57 58 |
#include <stdio.h> #include <stdlib.h> #include “mysqrt.h” // added #include “thirdpower.h” // added #include “fibo.h” // This will be found automatically because of target_include_directories 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); // added printf(“The fibonacci sequence \n”); print_fibonacci_sequence(); return 0; } |
Here the function is used as the last action in the „main.c“ file.
And when executing it with „$ build/sqrt_test 16“ the output should show:
|
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 |
$ 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 The fibonacci sequence Fibonacci Sequence (up to largest safe integer): F(0) = 0 F(1) = 1 F(2) = 1 F(3) = 2 F(4) = 3 F(5) = 5 F(6) = 8 F(7) = 13 F(8) = 21 F(9) = 34 F(10) = 55 ... F(90) = 2880067194370816120 F(91) = 4660046610375530309 F(92) = 7540113804746346429 Stopped at F(93) due to overflow. |
I am now ready to move away from native compiling with „C“and the standard Linux compiler towards cross compiling using the RP2xxx processors on Pico boards using the the RP2xxx Software Development Kit (SDK) with it’s libraries.
But this is something for the not so far away future,
Stay tuned!