zephyrproject-rtos/west

Exported variables are not seen by CMake when calling west

efra-mx-aqua opened this issue · 6 comments

Exported variables are not seen by west. I have notices that when signing binaries

In our project we have been building the code using CMake, but now we are doing the transition to use West.

how to reproduce

Before building define these variables

export HEADER_SIZE=0x0200
export SLOT_SIZE=0x67000
export IMG_FW_VERSION=1.2.3+456

Add this to the "prj.conf"

CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS=" --header-size ${HEADER_SIZE}  --align 8 --erased-val 0xff --version ${IMG_FW_VERSION} --slot-size ${SLOT_SIZE}"

Build using:

west build -b nrf52_adafruit_feather $ZEPHYR_BASE/samples/hello_world -p always -- -DCONFIG_MCUBOOT_SIGNATURE_KEY_FILE=\"/path/to/any-valid-key.pem\"

Result

The image signing fails because the variables HEADER_SIZE, IMG_FW_VERSION and SLOT_SIZE do not exits

[146/146] Linking C executable zephyr/zephyr.elf
FAILED: zephyr/zephyr.elf zephyr/zephyr.map zephyr/zephyr.hex zephyr/zephyr.bin zephyr/zephyr.lst zephyr/zephyr.stat zephyr/zephyr.signed.bin zephyr/zephyr.signed.confirmed.bin zephyr/zephyr.signed.hex zephyr/zephyr.signed.confirmed.hex /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.map /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.hex /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.bin /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.lst /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.stat /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.bin /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.confirmed.bin /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.hex /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.confirmed.hex 
: && ccache /opt/toolchains/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-gcc   zephyr/CMakeFiles/zephyr_final.dir/misc/empty_file.c.obj zephyr/CMakeFiles/zephyr_final.dir/isr_tables.c.obj zephyr/CMakeFiles/zephyr_final.dir/dev_handles.c.obj -o zephyr/zephyr.elf  zephyr/CMakeFiles/offsets.dir/./arch/arm/core/offsets/offsets.c.obj  -fuse-ld=bfd  -Wl,-T  zephyr/linker.cmd  -Wl,-Map=/home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr_final.map  -Wl,--whole-archive  app/libapp.a  zephyr/libzephyr.a  zephyr/arch/common/libarch__common.a  zephyr/arch/arch/arm/core/aarch32/libarch__arm__core__aarch32.a  zephyr/arch/arch/arm/core/aarch32/cortex_m/libarch__arm__core__aarch32__cortex_m.a  zephyr/arch/arch/arm/core/aarch32/mpu/libarch__arm__core__aarch32__mpu.a  zephyr/lib/libc/minimal/liblib__libc__minimal.a  zephyr/lib/posix/liblib__posix.a  zephyr/soc/arm/common/cortex_m/libsoc__arm__common__cortex_m.a  zephyr/soc/arm/nordic_nrf/nrf52/libsoc__arm__nordic_nrf__nrf52.a  zephyr/drivers/clock_control/libdrivers__clock_control.a  zephyr/drivers/console/libdrivers__console.a  zephyr/drivers/gpio/libdrivers__gpio.a  zephyr/drivers/serial/libdrivers__serial.a  zephyr/drivers/timer/libdrivers__timer.a  modules/hal_nordic/nrfx/libmodules__hal_nordic__nrfx.a  -Wl,--no-whole-archive  zephyr/kernel/libkernel.a  -L"/opt/toolchains/gcc-arm-none-eabi-10-2020-q4-major/bin/../lib/gcc/arm-none-eabi/10.2.1/thumb/v7e-m/nofp"  -L/home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr  -lgcc  -Wl,--print-memory-usage  zephyr/arch/common/libisr_tables.a  -mcpu=cortex-m4  -mthumb  -mabi=aapcs  -mfp16-format=ieee  -Wl,--gc-sections  -Wl,--build-id=none  -Wl,--sort-common=descending  -Wl,--sort-section=alignment  -Wl,-u,_OffsetAbsSyms  -Wl,-u,_ConfigAbsSyms  -nostdlib  -static  -no-pie  -Wl,-X  -Wl,-N  -Wl,--orphan-handling=warn && cd /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr && /snap/cmake/1186/bin/cmake -E rename zephyr_final.map zephyr.map && /opt/toolchains/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-objcopy --gap-fill 0xff --output-target=ihex --remove-section=.comment --remove-section=COMMON --remove-section=.eh_frame zephyr.elf zephyr.hex && /opt/toolchains/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-objcopy --gap-fill 0xff --output-target=binary --remove-section=.comment --remove-section=COMMON --remove-section=.eh_frame zephyr.elf zephyr.bin && /opt/toolchains/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-objdump -d -S zephyr.elf > zephyr.lst && /opt/toolchains/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-readelf -e zephyr.elf > zephyr.stat && /home/efra-mx/workspaces/aqua/iotbrd2/.venv/bin/python3 -m west sign --quiet --tool imgtool --tool-path /home/efra-mx/workspaces/aqua/iotbrd2/.venv/bin/imgtool --build-dir /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build --bin --sbin /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.bin --hex --shex /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.hex -- --key /home/efra-mx/aquarobur-rsa-2048.pem --header-size  --align 8 --erased-val 0xff --version  --slot-size  && /home/efra-mx/workspaces/aqua/iotbrd2/.venv/bin/python3 -m west sign --quiet --tool imgtool --tool-path /home/efra-mx/workspaces/aqua/iotbrd2/.venv/bin/imgtool --build-dir /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build --bin --sbin /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.confirmed.bin --hex --shex /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.confirmed.hex -- --key /home/efra-mx/aquarobur-rsa-2048.pem --header-size  --align 8 --erased-val 0xff --version  --slot-size  --pad --confirm
Memory region         Used Size  Region Size  %age Used
           FLASH:       16156 B       200 KB      7.89%
            SRAM:        4032 B        64 KB      6.15%
        IDT_LIST:          0 GB         2 KB      0.00%
Usage: imgtool sign [OPTIONS] INFILE OUTFILE
Try 'imgtool sign -h' for help.

Error: Invalid value for '-v' / '--version': Invalid version number, should be maj.min.rev+build with later parts optional
FATAL ERROR: command exited with status 2: /home/efra-mx/workspaces/aqua/iotbrd2/.venv/bin/imgtool sign --version 0.0.0+0 --align 4 --header-size 512 --slot-size 204800 --key /home/efra-mx/aquarobur-rsa-2048.pem --header-size --align 8 --erased-val 0xff --version --slot-size /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.bin /home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build/zephyr/zephyr.signed.bin
ninja: build stopped: subcommand failed.

Expected result

That the variables a visible, therefore the signing will work

workaround

Calling cmake directly

rm -rf build
cmake -DBOARD=nrf52_adafruit_feather -S $ZEPHYR_BASE/samples/hello_world -B build -DCONFIG_MCUBOOT_SIGNATURE_KEY_FILE=\"/path/to/any-valid-key.pem\"
make -C build

log:

[100%] Linking C executable zephyr.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       16156 B       200 KB      7.89%
            SRAM:        4032 B        64 KB      6.15%
        IDT_LIST:          0 GB         2 KB      0.00%
Generating files from zephyr.elf for board: nrf52_adafruit_feather
make[2]: Leaving directory '/home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build'
[100%] Built target zephyr_final
make[1]: Leaving directory '/home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build'
make: Leaving directory '/home/efra-mx/workspaces/aqua/iotbrd2/ar-firmware/build'

output:

build/zephyr/zephyr.bin  build/zephyr/zephyr_prebuilt.elf
build/zephyr/zephyr.dts  build/zephyr/zephyr_prebuilt.map
build/zephyr/zephyr.elf  build/zephyr/zephyr.signed.bin
build/zephyr/zephyr.hex  build/zephyr/zephyr.signed.hex
build/zephyr/zephyr.lst  build/zephyr/zephyr.stat
build/zephyr/zephyr.map

environment

OS: linux
zephyr: 2.7.2
west: 0.11, 0.14

workaround: call CMake directly

Can you please elaborate on that? I'd be very surprised to learn that prj.conf files support environment variables (with or without west which does not seem relevant here)

@efra-mx-aqua thanks for the report; could you please provide the cmake commands you use which work as expected, for comparison?

@marc-hb @mbolivar-nordic I have updated the description with the missing information

Thanks @efra-mx-aqua . I can't build nrf52_adafruit_feather, it fails with some with HAS_NORDIC_DRIVERS error for me. Are you using the Zephyr SDK? If not could you share a reproduction that uses it?

I tried to reproduce with qemu_x86. As I expected, samples/hello_world/prj.conf does NOT expand environment variables. In other words, with export CLI_OPT=--neither-an-option, both west and cmake fail exactly the same below:

--- a/samples/hello_world/prj.conf
+++ b/samples/hello_world/prj.conf
+# Fails with error: unrecognized command-line option '--not-an-option'
+CONFIG_COMPILER_OPT="--not-an-option"
--- a/samples/hello_world/prj.conf
+++ b/samples/hello_world/prj.conf
+# Fails with error: ${CLI_OPT}: linker input file not found: No such file or directory
+# NO variable expansion
+CONFIG_COMPILER_OPT="${CLI_OPT}"

I'm not sure what weird "magic" causes environment variables to be expanded in the following line but it's the expansion that looks like a bug to me:

CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS=" --header-size ${HEADER_SIZE}  --align 8 --erased-val 0xff --version ${IMG_FW_VERSION} --slot-size ${SLOT_SIZE}"

Environment variables are generally speaking a "build code smell": they hide very well and they cause subtle differences which are very difficult to find and reproduce. Many commands "cleanse" the environment.
Also, CMake does not support defining build-time environment variables:

https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#how-can-i-get-or-set-environment-variables

I can't build nrf52_adafruit_feather

I figured out what was wrong, sorry for the noise. I can reproduce now, thanks for the detailed steps.

This is all down to a difference between Ninja and Make, it has nothing to do with west. The variable expansion relies on a bug/loophole in Make that was fixed in Ninja. If you ask west to use make then you can exploit the same loophole from west too:

west build -b nrf52_adafruit_feather samples/hello_world -p always -- -G'Unix Makefiles'

I highly recommend against this though because: 1. Environment variables are bad, see above 2. This gives the wrong impression that environment variables are supported in .conf files (they're not). 3. Most Windows users don't have Make 4. Make is old and baroque, don't use it as the CMake backend. 5. Relying on ninja vs make differences is super confusing.

Maybe you could generate some .conf files? Or maybe the signing tool needs a configuration file.

Anyway this is about environment variables, kconfig and Make and has absolutely nothing to do with west: closing.

PS: setting CONFIG_MCUBOOT_SIGNATURE_KEY_FILE in prj.conf is more convenient for reproduction

@marc-hb thank you for the information. I am by no mean expert in how the Kconfig works, so I tried that and noticed that defining CONFIG_MCUBOOT_EXTRA_IMGTOOL_ARGS with environment variables was working.

I have had problems to pass the image version to the signing script, but that is another issue.