Quick Guide to CMake
To use pico-jxglib in a Pico SDK project, you need to edit the CMake configuration file CMakeLists.txt. You only need to add a few lines, so you can integrate it without knowing all the details, but understanding what CMake actually does will help you feel more comfortable. This page explains the basics of CMake1.
CMake generates files for build tools like make or ninja based on the contents of CMakeLists.txt. You might think you could just write a Makefile directly, but CMake conveniently handles differences in compilers and OSes, which is why many development platforms use it.
Here is the core part of a CMakeLists.txt generated by the Pico SDK template:
add_executable(hoge hoge.c)
target_link_libraries(hoge
pico_stdlib)
target_include_directories(hoge PRIVATE
${CMAKE_CURRENT_LIST_DIR})
Here's what each part does:
add_executable()declares the creation of an executable. The first argument is the target name, followed by the source files. If there are multiple source files, separate them with spaces or newlines. The target name is used as the base name for the executable (e.g.,hoge.elforhoge.uf2), and is also used as the first argument for commands starting withtarget_liketarget_link_librariesandtarget_include_directories.target_link_libraries()specifies libraries to link with this target.target_include_directories()specifies include paths for compiling the source files.PRIVATEmeans the include path is only valid within thisCMakeLists.txt. Other keywords includePUBLIC(valid for this and all parentCMakeLists.txt) andINTERFACE(not valid for thisCMakeLists.txt, but valid for all parentCMakeLists.txt).
The variable CMAKE_CURRENT_LIST_DIR contains the directory where this CMakeLists.txt is located.
Commands can be written in a single call like this:
Or split into multiple calls like this:
Up to this point, you might think of CMake as "just a slightly different way to write a classic Makefile." However, the behavior of target_link_libraries() needs some explanation.
If you look at the CMakeLists.txt file above, you might wonder where files like pico_stdlib.lib, pico_stdlib.a, or pico_stdlib.so are located. You might search in the .pico_sdk directory or your project's build directory, but you won't find such files. The pico_stdlib specified here is not a file name, but the name of a target declared with add_library() in the Pico SDK's CMakeLists.txt2.
Here is an example of the CMakeLists.txt that declares pico_stdlib (simplified for clarity):
add_library(pico_stdlib INTERFACE)
target_sources(pico_stdlib INTERFACE
${CMAKE_CURRENT_LIST_DIR}/stdlib.c
)
target_link_libraries(pico_stdlib INTERFACE
pico_stdlib_headers
pico_platform
pico_time
pico_divider
pico_binary_info
pico_printf
pico_runtime
pico_stdio
hardware_gpio
hardware_uart
)
The INTERFACE keyword is important. Targets declared with INTERFACE are not actually compiled themselves; instead, they pass information about source files, include paths, and linked libraries to the project that calls target_link_libraries().
In the file above, pico_stdlib links to other libraries like pico_stdlib_headers and pico_platform, which are also declared as INTERFACE. This means all the information about source files and so on is passed up to the original project3.
Because INTERFACE libraries are not pre-built, the main project that calls target_link_libraries() will compile the source files that make up those libraries.
This approach has big advantages. Since the library source files are compiled in your project, you can use preprocessor macros to include only the code you need, or change behavior at compile time. This can improve performance and reduce object size.
Another important point is that include paths are automatically passed to the calling project just by using target_link_libraries(). You don't need to manually specify include paths for each library you use.
pico-jxglib follows the Pico SDK and declares its libraries as INTERFACE.
-
CMake is a large system, and even I only know the basics described here. ↩
-
If the specified target name is not found, CMake will search for an actual library file like
*.libor*.ain the library search path. ↩ -
For more details, see section "2.2. Every Library is an INTERFACE Library" in the Raspberry Pi Pico-series C/C++ SDK. At first, it can be confusing if you think of a "library" as a physical file like
hoge.liborhoge.a. ↩