How to build multiple configurations of an ESP-IDF application - cmake

I have one ESP-IDF application and two hardware boards. I use a preprocessor definition to control which hardware board version to build. For now, I'm modifying the config in the sdkconfig file via menuconfig. But I would like to build both versions at once from a script, or build only one specific config without the manual process of menuconfig.
I have a header that looks like this, and works when HW_VER is set correctly:
#if HW_VER == 2
#define BTN_GPIO 9
#elif HW_VER == 3
#define BTN_GPIO 10
#endif
And from the a script I would like to build each by selecting a value for HW_VER, for example:
idf.py build -DHW_VER=2
idf.py build -DHW_VER=3
The idf.py build command runs cmake and ninja. I'm new to cmake, so perhaps there is a natural way to do this?
I would also like to build release and debug builds, turn on/off memory debugging etc. from the command line.
I've tried idf.py build -DHW_VER=2 but I've learned that these vars are only sent to cmake and not to the preprocessor. The HW_VER macro remains undefined.
Using add_definitions() in my CMakeLists.txt can set HW_VER, but doesn't help me make different builds from the same files.
Using a config variable like CONFIG_HW_VER in the sdkconfig works to control builds using menuconfig but I don't see a way to automate this.
I've considered modifying the configuration variable, CONFIG_HW_VER in the sdkconfig file programmatically, but this file is under source control, and it is auto generated by menuconfig, so that doesn't seem wise.
Similarly I can modify the CMakeLists.txt file programmatically, but that file is also under source control, and isn't a trivial format.

I use two ways to feed custom configurations into an ESP IDF project.
Firstly, the light weight stuff like preprocessor definitions from the environment are quite simple. You have to configure CMakeLists.txt file (the one in project root) to pass variable values from environment into the build process. For example, to create something equivalent to preprocessor definitions -DMY_NUMBER=123 and -DMY_STRING="abc" add this somewhere before the "project" line:
add_compile_definitions(MY_NUMBER=$ENV{MY_NUMBER})
add_compile_definitions(MY_STRING=\"$ENV{MY_STRING}\")
...
project(myproject)
Assuming you're working in Bash, build with:
$ MY_NUMBER=123 MY_STRING="abc" idf.py build
or (for a slightly more "sticky" enviroment):
$ export MY_NUMBER=123 MY_STRING="abc"
$ idf.py build
You can use cmake to add more advanced logic, e.g. setting default values in case the environment doesn't set anything.
Secondly, the more powerful configuration tool for ESP IDF is the menuconfig target and sdkconfig file. As you've already noticed, playing with sdkconfig directly is not so easy. In my projects I consider this a generated temporary file and I never commit it to git. Instead I delete it. When sdkconfig is missing, idf.py will take file sdkconfig.defaults, copy it into sdkconfig and work with this. That is your best mechanism for supporting different hardware configurations - no sdkconfig and instead different variants of sdkconfig.defaults for each hardware you wish to support.
As an example, assume you have two different HW versions described in sdkconfig.defaults.hw_ver1 and sdkconfig.defaults.hw_ver2 and you wish to build for HW ver2 configuration:
$ rm sdkconfig && cp sdkconfig.defaults.hw_ver2 sdkconfig.defaults
$ idf.py reconfigure
Now you can build for this configuration like you usually would:
$ idf.py build
When you wish to build for the other FW configuration, re-execute the previous commands with sdkconfig.defaults.hw_ver1
All this is rather thoroughly documented in the Build System documentation, so feel free to dig in.

Related

CMake: What is the advantage of using -D (command line) over add_compile_definitions or target_compile_definitions or set?

New to CMake. When adding libraries I see a lot of instructions saying do this:
cmake -D OPENSSL_ROOT_DIR=/usr/local/opt/openssl
Why is that preferred over putting it in the actual CMakeLists.txt? e.g.
set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
You do not commit the first one to the repository while you either commit the second one or have to be careful to not commit it with other stuff.
The second method is usually used to allow users of some project to modify some aspect of that project. So whatever pertains to the project itself (not customizable) goes straight to the CMake files everything else might be provided from the command line.
Note, also, that the CMake presets add an ability to customize the build outside the CMake files so the method of providing variables directly via a console call is something I'd expect going away and getting replaced with presets. But the way they work is still the same: variables defined in the preset get to CMake cache with generated console call (with an IDE it might be different).

Variable interpolation in -D option

As a package manager for a Linux distribution, I want to install docs into a separate prefix. With CMake projects, the docs installation location is controlled by CMAKE_INSTALL_DOCDIR from GNUInstallDirs module. Unfortunately, unlike the other directory variables, this one contains the project name so I cannot just use cmake "-DCMAKE_INSTALL_DOCDIR=$myDocPrefix/doc".
With GNU Make, I would run make "DOCDIR=$myDocPrefix/doc/\$(PROJECT_NAME)" and have Make interpolate it but the documentation of CMake’s -D option does not mention interpolation and I understand that CMake uses much more complex system of cache entries where interpolation might be problematic (especially if the referenced variable is not yet in cache).
I could pass tailor-made CMAKE_INSTALL_DOCDIR to each CMake project but would be bothersome as I would have to do that in every package definition manually; being able to define configureCmakeProject function and have it take care of everything automatically would be better. When setting it manually, I would also want to make sure it matches the PROJECT_NAME of the respective CMake project – well, I could resign on that and just use $packageName from the package definition instead but keeping packages as close to upstream as possible is preferred.
Alternately, I could try to grep CMakeLists.txt for project command but that seems fragile and might still result in misalignments. I doubt it is possible to extract it using some CMake API since the project is not configured at the time and we actually need the value to configure the project.
Is there a way I can configure CMAKE_INSTALL_DOCDIR to use custom prefix but still keep the project name set by the CMake project?

Using CMake to generate multiple Code Composer build configurations

My goal is to use a script/CMake to create a "Debug" build configuration and a "Release" build configuration that can be switched between within Code Composer Studio's UI (using the "Build Configuration -> Set Active..." option).
Currently,
A script is ran that runs CMake with desired commands (toolchain, etc). A Code Composer Studio project is generated (as described in CMakeLists.txt)
CCS project is imported into CCS
The problem is this only generates a "Debug" build configuration. Is it possible to add a command to CMakeLists.txt, or to cmake command line, or even ccs command line that allows multiple build configurations to be generated?
The only difference between the two will basically be defining NDEBUG, and possibly changing optimization level.
I had this same question...then realized I am the one who originally asked this ~4 years ago! Anyways, I found a way to do this:
Using Code Composer, create the build configuration(s) as you want them to behave. When done, copy the .cproject file to a cproject.in "template". CMake will use this template to generate an identical .cproject for any future cmake builds. Make sure to replace any hardcoded values (ex: project name) with proper cmake variables.
For me, my CMakeLists.txt called configure_file(path/to/cproject.in ${CMAKE_SOURCE_DIR}/.cproject #ONLY).
Also be sure to delete your CMakeCache and CMakeFiles if they already exist...I believe those were preventing me from seeing the resulting change.

cmake suppress finding package and set path manually

I'm currently automating my the installation process for multiple instances of an application. This application uses cmake for building and uses some libraries for which no findModule.cmake files exist. Since I'm could find a good example how to generate a findModule.cmake file for existing libraries for example OpenCascade. When setting up the buildprocess manually one can easily adapt the include and lib path in ccmake. Since I want to automate this I'm looking for a way to do this by passing the options to cmake on the command line. Here is how I try to achieve this for OpenCascade:
cmake -DOCC_FOUND:INTERNAL=TRUE -DOCC_INCLUDE_DIR:PATH=/usr/include/opencascade -DOCC_LIBRARY:FILEPATH=/usr/lib/libTKernel.so -DCMAKE_BUILD_TYPE:STRING=Release ..
Unfortunately this doesn't work. Since the option for building are build-depended, passing a previously configured CMakeCache.txt file is not working.
Thanks for any suggestions to achieve what I'm trying to do.

Making my program compile on both windows and linux, what should I do about libraries?

I'm using CMake to generate my makefiles and VC solutions. I have my program running on linux just fine. I'm using a bunch of libraries, e.g. Lua. On my linux computer I just link to them and make sure in include the headers. All of the .so files are in some standard place. On Windows though I'm not sure what to do. Should I just include the entire Lua project into my own repository and link to that instead? Do I install Lua to some standard place like c:\program files\lua5.1 and link to that? Something else?
Your libraries can be in any place, you just need to say - where are they.
Before running cmake set up pathes of all your extern libs with some .bat file:
set LIBRARYPATH =path\to\your\library\
set include=%include%;%LIBRARYPATH%\include
set lib=%lib%;%LIBRARYPATH%\lib
Start cmd, run this .bat and then cmake should find all
I would provide a configuration field/variable for ccmake that the user can or must specify.
The mark_as_advanced can be used to make a custom variable only appear in the advanced mode. This would be suitable if you have a standard path (as you have mostly on windows). Yet, it still let's the user specify the value if needed.
Or you can simply set a variable the with the 'set' command if you don't want it in advanced.
Just make sure you check if the users entered a valid value.