find_package() stops working when CMAKE_SYSTEM_NAME=iOS

Hi,

My problem concerns find_package() in the “Config” mode.

I created a PDFiumConfig.cmake and set the environment variable PDFium_DIR to the correct directory.
find_package() works fine for all platforms except for iOS: as soon as CMAKE_SYSTEM_NAME is defined, it ignores the PDFium_DIR environment variable.

Curiously, when I run with --debug-find, I can see that the environment variable is considered:

Env variable PDFium_DIR [CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH].

    /Users/runner/work/pdfium-binaries/pdfium-binaries/staging

Of course, I listed the directory to ensure that PDFiumConfig.cmake is present; moreover, I use the same setup for the eleven other platforms, and they work.

I think I tried every possible combination of CMAKE_IOS_INSTALL_COMBINED, CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH, CMAKE_FIND_APPBUNDLE, and CMAKE_FIND_FRAMEWORK. I also searched the documentation thoroughly and even looked at CMake’s source code, but I couldn’t understand why it didn’t work.

You can find more information about this problem there:

Any help would be greatly appreciated!

Best regards,
Benoit

I just opened an issue: https://gitlab.kitware.com/cmake/cmake/-/issues/23041

I closed the issue and will reply here instead.

The PDFium_DIR environment variable needs to point to the directory that contains the PDFiumConfig.cmake file. Are you sure you are not pointing it to some parent directory of that? The path you’ve specified for it in the description looks suspiciously like you’ve set it to the base staging/install location instead.

Hi Craig. Thank you very much for your answer.

As I said in my original message, I already checked that the directory pointed by PDFium_DIR does contain PDFiumConfig.cmake.
You can check the actual content of this folder in this build artifact uploaded in the previous step.

Again, I insist on the fact that my 11 other builds are working fine with the same build scripts.
From what I can tell, things go wrong only when CMAKE_SYSTEM_NAME is set to iOS.

Let me know if I can bring any other helpful information.

I’m trying to make a dent in my backlog of CMake things to look at. It would help immensely if you could reduce your project down to an absolute minimum that reproduces the problem. The app doesn’t have to run or represent anything meaningful. We’re only interested in what happens at CMake configure time.

I’m sorry, I should have written an MCVE from the beginning.
Here it is: GitHub - bblanchon/cmake-findpackage-ios-bug

You’ll see that it boils down to the three following files:

# CMakeLists.txt
find_package(ThePackage REQUIRED)
# ThePackage/ThePackageConfig.cmake
message("It works!!!")
# build.sh
export ThePackage_DIR="$(dirname $0)/ThePackage"
cmake -D CMAKE_SYSTEM_NAME="iOS" .

In the build.sh of that MCVE, the value of ThePackage_DIR might not be an absolute path, but it should be if you want it to work. That’s not the main issue though…

This was a bit of a head-scratcher until I followed the code in a debugger. The documentation has the necessary details, but it’s not presented very clearly and it’s easy to miss what’s going on. What’s happening is that find_package() first checks the ThePackage_DIR CMake variable, if it is defined, and will use the config file found there preferentially if there is one. If there isn’t, then it proceeds through its documented set of search paths. What’s perhaps not so clear is that depending on the value of CMAKE_FIND_ROOT_PATH_MODE_PACKAGE, all those search paths may be re-rooted to the find root(s) and sysroot, when those things are set. For cross-compiling to iOS (i.e. when you set CMAKE_SYSTEM_NAME to iOS), there will be a sysroot. The default value of CMAKE_FIND_ROOT_PATH_MODE_PACKAGE is ONLY, which means find_package() will only search the re-rooted paths and will not search the original paths. Naturally, the ThePackage_DIR environment variable you’re setting won’t exist when re-rooted, so that’s why it has been having no effect.

To see this in action, try setting CMAKE_FIND_ROOT_PATH_MODE_PACKAGE to BOTH before the call to find_package(). After that, the config file will be found even on iOS. That might not necessarily be what you actually want to do though. Personally, I’d set the ThePackage_DIR CMake variable rather than the environment variable. Where possible, I don’t even do that, I use CMAKE_PREFIX_PATH instead, but that requires that your config file sits under a standard directory layout that matches one of the various combinations that find_package() will search under the prefix by default.

The fact that the ..._DIR environment variable behaves differently to the CMake variable of the same name when re-rooting paths is involved feels unintuitive and bordering on a bug to me. It’s technically the documented behavior, but I can’t say I like it.

Thank you very much for your explanation, Craig. I’ve been using CMake for years, yet I had to read your message several times to make sure I understood it correctly.

Here are my observations:

  • Passing -D ThePackage_DIR=... to the command line fixes the issue.
  • Passing -D CMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH also fixes the issue.
  • Passing -D CMAKE_PREFIX_PATH=... doesn’t fix the issue, but maybe I’m not using it correctly.

I would never have thought of setting the ThePackage_DIR CMake variable because I believed this was equivalent to the environment variable.
Indeed, the documentation only mentions <PackageName>_DIR as a “cmake-specific environment variable”.
I would qualify this behavior as a bug since I cannot imagine someone who expects it to behave that way.

I tried to use the best practices when designing my package, and I thought setting the environment variable was the way to go. Do you know where I could find a guide to package a compiled library correctly? I tried to make sense of the find_package() documentation, but it’s overwhelming and covers many use cases that may not represent the best practices.

PS: I had to set CMAKE_FIND_ROOT_PATH_MODE_INCLUDE and CMAKE_FIND_ROOT_PATH_MODE_LIBRARY to fix similar issues with find_path() and find_library().