Generator expression $<INSTALL_PREFIX> doesn't work with env variable DESTDIR

Using the generator expression $<INSTALL_PREFIX> in install(CODE ) should work with CMake 3.27 and later. But the generator expression doesn’t work with the enviroment variable DESTDIR. I don’t know if this is the intended behavior.

Example

cmake_minimum_required(VERSION 3.27)

project(install_code_bug)

install(CODE "file(TOUCH $<INSTALL_PREFIX>/test_file.txt)")

Configure the above project cmake -B ./build -S ./ && mkdir install.

Running unset DESTDIR && cmake --install ./build --prefix "$(pwd)/install" will result in file ‘install/test_file.txt’ been created.

However, if DESTDIRis used like this export DESTDIR="$(pwd)/install" && cmake --install ./build it will result in an error:

CMake Error at build/cmake_install.cmake:46 (file):
  file problem touching file: /usr/local/test_file.txt

cmake version 3.31.6

OS: Ubuntu 25.10

$ENV{DESTDIR}$<INSTALL_PREFIX> is what you should use here.

This only works if `DESTDIR` is set during configuration.

Yes, it’ll need the proper level of escaping to end up in the code as intended.

Thank you, yes, that works. I think I understand the underlying difference in the CMake install script between file() and install but isn’t it still error-prone that you have to write \$ENV\\{DESTDIR\}$<INSTALL_PREFIX> for INSTALL(CODE …) but not for INSTALL(FILES …)?

If escaping is a worry, you can use install(SCRIPT) instead. CMake generates the appropriate code to use DESTDIR itself for install(FILES). CMake isn’t going to parse your CODE or SCRIPT to second guess your intent if you miss it yourself; the install prefix is something different from DESTDIR and knowing what the intent of any given $<INSTALL_PREFIX> usage is intended to be is beyond CMake’s scope.