Unexpected interactions between `CMAKE_SYSROOT` and `CMAKE_FIND_ROOT_PATH_MODE_PACKAGE`

TL;DR: Why does leaving CMAKE_SYSROOT unset in a toolchain file cause CMake to ignore CMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY and allow find_package to search CMAKE_PREFIX_PATH anyway?

Context: I’m playing around with cross-compilation using the Conan package manager.
Conan writes packagename-config.cmake files for all dependencies in a build directory, and then provides a CMake toolchain file that adds this build directory to CMAKE_PREFIX_PATH, so that find_package() picks up the config.cmake files generated by Conan. So far, so good.

Unexpected observation #1:
However, when cross-compiling, I use the following in my toolchain file, to avoid accidentally picking up packages from the machine I’m building on:

set(CMAKE_SYSTEM_NAME "Linux")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

The strange thing is that CMake still picks up Conan’s packages, even though they are only available in CMAKE_PREFIX_PATH, which I would expect to be skipped during cross-compilation.
It seems that the Conan devs rely on this themselves, though, for example in these tests: conan/test_cmaketoolchain_paths.py at d66a3c00ef66947ec8c9e0b2bfba69cfcdce7387 · conan-io/conan · GitHub

Unexpected observation #2:
However, when also setting CMAKE_SYSROOT in the toolchain file, suddenly, find_package no longer considers the Conan config.cmake files in CMAKE_PREFIX_PATH. This seems to me the expected behavior, but it is unexpected to me that simply setting CMAKE_SYSROOT changes the behavior of find_package for CMAKE_PREFIX_PATH.

As a consequence, cross-compilation using Conan is broken when both CMAKE_SYSROOT and CMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY are set in the user’s toolchain file.

When editing the toolchain file generated by Conan, replacing CMAKE_PREFIX_PATH to CMAKE_FIND_ROOT_PATH, it picks up the Conan config.cmake files again, as expected.

Some final questions:

  • Should the Conan devs consider setting CMAKE_FIND_ROOT_PATH instead of CMAKE_PREFIX_PATH to make sure CMake locates the config.cmake files it generated?
  • The Mastering CMake chapter on cross-compilation does not set CMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY. Why not? Is it ever acceptable to select packages from the build machine when cross-compiling? (Ignoring cases where the user explicitly allows it at the find_package call site, e.g. when they know that the package is header-only.)

Files to reproduce:

The following files can be used to reproduce the problem, without using Conan.

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)
project(cmake-prefix-tests)

list(PREPEND CMAKE_PREFIX_PATH ${PROJECT_SOURCE_DIR}/packages)
# list(PREPEND CMAKE_FIND_ROOT_PATH ${PROJECT_SOURCE_DIR}/packages)
find_package(mypackage REQUIRED)

packages/mypackage-config.cmake

message(STATUS "FOUND: ${CMAKE_CURRENT_LIST_FILE}")

toolchainA.cmake

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER cc)
# set(CMAKE_SYSROOT "${CMAKE_CURRENT_LIST_DIR}/sysroot")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

toolchainB.cmake

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER cc)
set(CMAKE_SYSROOT "${CMAKE_CURRENT_LIST_DIR}/sysroot")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
$ rm -rf build; cmake -S. -Bbuild --toolchain toolchainA.cmake
-- FOUND: /home/.../packages/mypackage-config.cmake

$ rm -rf build; cmake -S. -Bbuild --toolchain toolchainB.cmake
  Could not find a package configuration file provided by "mypackage" with
  any of the following names:

    mypackageConfig.cmake
    mypackage-config.cmake

Is there anyone who has an answer to these questions?