How can I ignore all cmake dev warnings from subdirectories? - cmake

I am using a few external libraries that are included as git submodules using the add_subdirectory command. Some of them are using old versions of cmake and they're issuing warnings about policies CMP0048 and CMP0077.
Is there a way to turn off all cmake warnings for these libraries?
I've tried explicitly setting the policies to OLD before including the projects but it didn't help.
I'd prefer not to edit any files in the git submodule because then there would be extra steps when someone has to clone my project's repo and build it on their machine.

I know this is a old questions, but in case someone stumbles upon the same problem, here are my findings.
Why didn't your attempts work?
Setting the policies before the add_subdirectory() call won't work, because cmake_minimum_required() implicitly set a specific set of policies, depending on the required version, which might override the set policies. Also, setting them explicitly to OLD has no effect even if they weren't overridden, since there is no distinction between being OLD because it is the default for that version or because it was explicitly set. A warning is issued in both cases, because as the documentation of every policy states, the old behavior is deprecated by definition.
Possible Solutions
I can think of the following solutions:
Use the undocumented variable CMAKE_SUPPRESS_DEVELOPER_WARNINGS, which is also set when passing -Wno-dev on the command line (see cmake.cxx). Since you probably don't want this to have an influence on your project and disable all developer warning there as well, you should restore the original value after the add_subdirectory() call:
set(no_dev_warnings_backup "$CACHE{CMAKE_SUPPRESS_DEVELOPER_WARNINGS}")
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ON CACHE INTERNAL "" FORCE)
add_subdirectory(...)
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ${no_dev_warnings_backup} CACHE INTERNAL "" FORCE)
The variable might be undocumented and therefore not that reliable as documented variables, but I wouldn't worry too much about it. It has been around since the CMake 2.x days and is unlikely to go anywhere.
Set CMAKE_POLICY_DEFAULT_CMP<NNNN> to NEW:
set(CMAKE_POLICY_DEFAULT_CMP0048 "NEW")
set(CMAKE_POLICY_DEFAULT_CMP0077 "NEW")
add_subdirectory(...)
Note that this doesn't just disable the warnings, but alters how CMake processes the affected CMakeLists.txt files. This might break the build of your external libraries!
Use CMAKE_PROJECT__INCLUDE or one of its related variables to inject some CMake code into the build of the external libraries. The injected code could then call cmake_policy(SET CMP<NNNN> NEW) to override the policy. Note that just like the 2. solution, this could break the build!
Instead of a git submodule, you could use FetchContent which fetches the external libraries and calls add_subdirectory() one them. Since FetchContent is quite related to ExternalProject it allows to patch the retrieved sources, so you can fix/customize the build of the external libraries:
include(FetchContent)
find_package(Patch REQUIRED)
FetchContent_Declare(myextlib
GIT_REPOSITORY <url>
GIT_TAG <tag_or_hash>
PATCH_COMMAND "${Patch_EXECUTABLE}" -p1 < path/to/myextlib.patch
)
FetchContent_MakeAvailable(myextlib)
More on diffs and patches can be found here. Alternatively, you also use git apply as patch command, since you use Git anyway.
Recommendations
I would recommend either 1 or 4. Approach 4 is the most flexible, since you could improve the build in other aspects as well, if necessary, but it is also the most involved one, since whenever you bump the version of your external libraries you also need to take care of your patches and check whether they still work etc. In case you don't need that flexibility and only care about disabling warnings, approach 1 would be the most straight forward solution.

Related

Should I supply external libraries with a CMakeLists.txt or supply find_packages instead?

I am working on a project that needs some external libraries. Since it is meant to be cross platform, I am using cmake.
What is the preferred way when distributing such projects? Should I supply the external libraries (such as zlib) with their own CMakeLists.txt or should I signal the dependency by simply supplying find_packages()?
the former provides all things needed. while the latter let's the developer decide how to supply the dependency (vcpkg for example)
Althoug there is no universally preferred approach, I absolutely believe you should stick to find_package. Declare your dependencies like this:
find_package(Pkg [version] REQUIRED [components])
Include [version] and [components] only if you know Pkg itself provides first-party CMake package configuration files. If you are writing and distributing a library, you will include equivalent find_dependency calls in your MyProjConfig.cmake file.
If some dependency does not have a standard CMake find module or provide its own CMake package configuration file, you should write your own in ./cmake and add list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") to the root CMakeLists.txt, before any find_package call. You will install your find modules, too, and include the same addition to the module path in your config files.
Inside the find module, you can use whatever approach you want to create some imported targets for your dependencies. Using PkgConfig is a good approach here.
Going through find_package instantly works with a number of dependency providers: vcpkg, the cmake_paths Conan generator, Linux distro system packages, and so on.
The primary alternative to doing this is to vendor the code, meaning including your dependencies in your build directly, whether through copy/paste into your source tree, a git submodule, or by build-time download from the internet (FetchContent).
The mechanism used to build these is nearly always add_subdirectory in the end, which pulls your dependencies' CMake builds into yours.
Perhaps the biggest issue with this is that most projects' CMake code is totally unprepared to be used in this way. It might trample your cache variables, inject invalid flags into your targets, overwrite your generated headers, and so on. Integration is a nightmare.
Also, from a software distribution standpoint, doing this ties your code to particular versions of your dependencies and takes control away from others who might want to package your code. For instance, Debian packages are not allowed to bundle their dependencies... if libA depends on libB, then each gets its own package. With find_package, it is trivial for a maintainer to inject the appropriate dependencies into your build. Without, it typically involves a difficult-to-maintain patch.

Best practices to build vendored code with CMake

I'm trying to understand what some of the best practices are when using modern CMake (3.13+) with respect to building and including vendored or submoduled code.
Say I'm building a library MyLib. My file structure is something like this
MyLib
|-CMakeLists.txt
|-src
|-include
|-submodules
|-libgeos
In this example, I've included libgeos as a git submodule, because it's really convenient to be able to clone the project and immediately build and run tests because that dependency is present. This could also be solved by using FetchContent or something, and my question still stands; the important thing is that I do not want to rely on libgeos being installed in build environment.
Note I picked libgeos arbitrarily; I have no idea if libgeos is set up as a cmake project appropriately for this example, but this is all theoretical and I just needed some concrete library name. Please do not use the specific details of how libgeos is configured to answer this, unless libgeos is a good example of conventional cmake.
But now, there's some other project that wants to use my project, and it needs libgeos and doesn't want to depend on my project providing it.
OtherProject
|-CMakeLists.txt
|-src
|-include
|-submodules
|-libgeos
|-MyLib
|submodules
|-libgeos
When you clone OtherProject, you get two versions of libgeos, and maybe that's not great; but it's not a huge issue either. And maybe they're not the same version; say MyLib requires libgeos >= 2.0, so 2.0 is what MyLib includes, and OtherProject requires libgeos>=2.1 so OtherProject includes libgeos >= 2.1.
Now we potentially end up with some build issues. If we have the following line in OtherProject/CMakeLists.txt
add_subdirectory(submodules/libgeos)
and then again, that same line within MyLib/CMakeLists.txt, we end up with cmake errors because libgeos as a target is defined twice in the build. This can be solved a couple of ways.
Check if geos exists before adding it
if(NOT TARGET geos)
add_subdirectory(submodules/libgeos)
endif()
But this case has some issues; if that blob is in OtherProject at the top, it's fine and both projects use libgeos 2.1. But if it's in OtherProject after add_subdirectory(submodules/MyLib), then the geos 2.0 version gets added to the build, which may or may not fail loudly (Hopefully it would).
This could also be solved with find_package. Both projects include cmake/FindGeos.cmake which use that blurb above (if(NOT TARGET...)) to add geos the build and then the top project cmake files can do this
list(APPEND CMAKE_MODULE_PATH cmake)
find_package(geos 2) # (or 2.1)
then it doesn't matter what order they try to include geos, because they will both defer to FindGeos.cmake in OtherProject because it's first in the module path.
But now there's a new issue, some ThirdProject wants to use MyLib also, but ThirdProject wants to depend on libgeos which is in the system environment. It uses find_package(geos 2.1 CONFIG) to use the installed GeosConfig.cmake file, which adds geos::geos to the build and sets geos_FOUND. Suddenly, MyLib fails to build, because geos_FOUND was set, but I'm doing target_link_library(mylib PUBLIC geos).
So this could be solved by adding add_library(geos::geos ALIAS geos) in both custom FindGeos.cmake files, then it doesn't matter if geos was built from source or using the installed version, the target names are the same either way.
Now we get to my actual questions:
Lets start with
Am I crazy, no one does this, and my team is trying to use cmake all wrong?
Is there some feature of cmake that I've just completely missed that solves all these problems?
I suspect there's a good few books or presentations that cover this topic, but I just don't know where to look because there's so many; what should I be looking at? I've seen the CMake Packages page, which looks like it solves the problem when you're using all projects which are configured according to that page; but it doesn't really answer how to bridge the gap between older and newer projects.
If I'm not crazy and there's no straightforward answer or presentation that I can look at, then
What should the cmake configuration for both MyLib and libgeos look like so that these cases work?
MyLib is built alone
MyLib is built as part of a larger project which provides a different version of geos
MyLib is built as part of a larger project which depends on a different version of geos in the environment
I understand that cmake provides helpers that could be used to produce MyLibConfig.cmake if I wanted to install it in the environment. I also see that the export() function exists, which could be used to save those files in the build tree somewhere and then find them with find_package in config mode. But this feels a bit odd to me to do because it's not a multi-stage build, it's just one invocation of cmake then make.
But lets say that's the right answer and the CMake for libgeos doesn't follow it. Would it be appropriate to have FindGeos.cmake do something like this?
if(NOT geos_FOUND)
add_subdirectory(submodules/libgeos)
export(geos NAMESPACE geos)
find_package(geos CONFIG)
endif()

What is the one-step process to clone a repo and run a CMake+vcpkg project, not assuming vcpkg exists?

I'm missing something in my understanding of CMake+vcpkg, and I'm also missing proper keywords to search for a solution. (Plus I'm new to both CMake and vcpkg, unfortunately.)
I want to have a public repo for a C++ project that uses CMake as its build system and vcpkg as its package manager.
At my currently level of understanding the user needs to have CMake and vcpkg already installed before he can type cmake and build the repo. I'd like to make it as simple as possible to build the repo and not have a bunch of instructions telling him how to get set up even before he can build.
Is this right?
I'd like a one-step solution: After cloning the repo user types ... something ... and the repo gets built.
I am willing in this day-and-age to assume he's got CMake installed ... plus that it can find the right toolchain. So maybe all he needs to type is 'cmake' ...
Is it a reasonable assumption that the user has CMake installed and configured with his preferred toolchain?
I am not willing to assume he's got vcpkg installed.
Is it a reasonable assumption that the user does not have vcpkg installed and configured?
(TBH, I don't even know if it is CMake or vcpkg that configures the toolchain - I assumed CMake but one of the suggested questions suggests it is vcpkg ...)
What are the reasonable assumptions today, and what is the minimal-step solution?
There's nothing wrong in assuming that the user has certain tools installed.
Let's say you are developing libfoo which depends on libbar and you want to make it as easy as possible for your users to install libfoo.
With a package manager
If libfoo and libbar are available via the same package manager all your users have to do is:
vcpkg install libbar libfoo
You don't have to do anything special in libfoo for this, just instruct the user to install all dependencies in your readme.
It doesn't really matter what package manager is used.
Without a package manager
You will still want to make it easy for people to build and install your project directly. It may seem that invoking a package manager during the build or configuration phase of your project and solving all dependencies is user friendly because the user no longer has to deal with installing those, but it isn't for a number of reasons, including:
you or someone else may want to add your project to another package manager (like conan, spack, etc)
someone may want to consume libfoo with FetchContent, CPM, directly with add_subdirectory, etc
someone may not be a user of vcpkg - there's no need to force them to use it, if possible
you may want to add another dependency, libbaz, which is not available on vcpkg
a user may have the right version of libbar already installed (not necessarily through vcpkg)
This list is not exhaustive. If you're not writing a library some points don't really apply.
This means that someone who has all the dependencies already installed should be able to use libfoo like this:
git clone your-repo
cd your-repo
cmake -Bbuild
cmake --build build
cmake --install build
Resolving dependencies without a package manager
However, it may be desirable to solve dependencies automatically. If your dependencies are using CMake the easiest way of doing this is with FetchContent. For some of the reasons outlined above you should provide an escape hatch so people can still use the already installed dependencies. This can be done with an option. For example, something like FOO_USE_EXTERNAL_BAR. This can be set either to yes or no by default, there's no right answer. As long as the user can control this I don't think it matters that much. You should namespace your options to avoid possible conflicts with options used by other projects.
In this case your build script could do this:
if (FOO_USE_EXTERNAL_BAR)
find_package(bar REQUIRED)
else ()
FetchContent_Declare(
bar
GIT_REPOSITORY bar-repo
GIT_TAG release-tag
)
FetchContent_MakeAvailable(bar)
endif ()
target_link_libraries(foo PRIVATE bar::bar)
Depending on how libbar's CMakeLists.txt is written and organized the if and else branches may get more complicated. See Effective CMake for some details and tips.
Now I can either let libfoo resolve the libbar by setting FOO_USE_EXTERNAL_BAR to ON when I configure your project, or I can set it to OFF to have more control over how it is resolved. I may even use libfoo as a dependency for a project that already depends on libbar. If you always pull it in I can't avoid conflicts in this case.
Using CMake to update dependencies
You may still find it easy for you to be able to update all the project's dependencies using CMake without downloading them via FetchContent. While this will probably raise some eyebrows you could add a custom target for solving dependencies with a package manager. This should also be controllable by an option. Unlike in the above case I strongly believe that if you do this the option should be set to off by default:
if (FOO_AUTO_USE_VCPKG)
add_custom_target(
update_deps
COMMAND vcpkg install libbar
)
add_dependencies(foo update_deps)
endif ()
This will invoke vcpkg every time you build foo so it will make your builds slower. If you remove the add_dependencies call you would have to manually run the update_deps target whenever you need to (which shouldn't be that often anyway).
Notes
Using options is a great way of providing options to your users. It should be noted that they increase the cognitive load, so picking strong defaults can help with that.
FetchContent is a nice way of taking the care away from the user, but at the same time multiple projects that use it will end up re-downloading the same libraries over and over again. It is still more user friendly than invoking a package manager at build time and as long as the users can disable this behavior there's nothing to worry about.
Some parts of this answer may be regarded more as opinion and less as facts. As I said, there is no one right way of doing this, different people will have different ways of solving this problem. Different projects and different environments will have different constraints.
I already recommended the Effective CMake talk above, other useful recourses are available here. If you're a library author you may also want to take a look at Deep CMake for Library Authors.
I had this same question. For my part, I am not willing to assume that the user has either CMake or vcpkg preinstalled.
Here is my solution so far, as a Windows batch file:
#REM Bootstrap...
set VCKPG_PARENT_DIR=C:\Projects
set CMAKE_VERSION="3.20.2"
mkdir "%VCKPG_PARENT_DIR%"
pushd "%VCKPG_PARENT_DIR%"
git clone https://github.com/Microsoft/vcpkg.git
.\vcpkg\bootstrap-vcpkg.bat -disableMetrics
set PATH=%PATH%;%VCKPG_PARENT_DIR%\vcpkg\downloads\tools\cmake-%CMAKE_VERSION%-windows\cmake-%CMAKE_VERSION%-windows-i386\bin
set VCPKG_DEFAULT_TRIPLET=x64-windows
set PYTHONHOME=%VCKPG_PARENT_DIR%\vcpkg\packages\python3_x64-windows\tools\python3
popd
#REM Build the project...
cmake -B build -S .\engine\ -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DUSE_PYTHON_3=ON
cmake --build .\build\ --config Release
mkdir bin
xcopy .\build\Release\*.* .\bin\
xcopy .\build\objconv\Release\*.* .\bin\
xcopy .\build\setup\Release\*.* .\bin\
It could use some improvement, but hopefully this gives you an idea of one route you could take.

Is it possible to alter CMAKE_MODULE_PATH from CMake commandline?

Edit: The accepted answer actually shows that it is pretty normally possible to set CMAKE_MODULE_PATH as any other CMake variable e.g. via the -DCMAKE_MODULE_PATH path CLI parameter. It seems that in my case there is some included CMake script that calls set(CMAKE_MODULE_PATH /library_path), which erases all previous paths set to the variable. That's why I couldn't get the variable to do what I wanted it to do. I'll leave the question here in case anybody else faces this kind of situation.
I'm building a (3rd party) project that uses the Protobuf library (but this question is general). My system has a system-wide install of a newer version of Protobuf than the project is compatible with. So I've downloaded and compiled from source an older version of Protobuf.
The project uses CMake, and in its CMakeLists.txt, there is:
find_package(Protobuf REQUIRED)
Which, however, finds the (incompatible) system install. Of course, CMake doesn't know about my custom build of Protobuf. But how do I tell it?
I've created a FindProtobuf.cmake file in, say, ~/usr/share/cmake-3.0/Modules/ and want the build process to use this one for finding Protobuf. But I haven't succeeded forcing CMake to pick up this one and not the system one. I think the reason is quite obvious from the CMake docs of find_package:
The command has two modes by which it searches for packages: “Module” mode and “Config” mode. Module mode is available when the command is invoked with the above reduced signature. CMake searches for a file called Find<package>.cmake in the CMAKE_MODULE_PATH followed by the CMake installation. If the file is found, it is read and processed by CMake. ... If no module is found and the MODULE option is not given the command proceeds to Config mode.
So until I succeed to change CMAKE_MODULE_PATH, CMake will just pick up the FindProtobuf.cmake installed to the default system path and won't ever proceed to the "Config" mode where I could probably make use of CMAKE_PREFIX_PATH.
It's important for me to not edit the CMakeLists.txt since it belongs to a 3rd party project I don't maintain.
What I've tried (all without success):
calling CMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules cmake ... (the env. variable is not "transferred" to the CMake variable with the same name)
calling cmake -DCMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules ... (doesn't work, probably by design?)
calling Protobuf_DIR=path/to/my/protobuf cmake ... (the project doesn't support this kind of override for Protobuf)
It seems to me that, unfortunately, the only way to alter the CMAKE_MODULE_PATH used by find_package is to alter it from within CMakeLists.txt, which is exactly what I want to avoid.
Do you have any ideas/workarounds on how not to touch the CMakeLists.txt and still convince find_package to find my custom Protobuf?
For reference, the CMake part of this project is on github .
As a direct answer to your question, yes, you can set CMAKE_MODULE_PATH at the command line by running cmake -DCMAKE_MODULE_PATH=/some/path -S /path/to/src -B /path/to/build.
But that probably doesn't do what you want it to do; see below.
The Bitbucket link you supplied is dead, but here are a few suggestions that might help.
Avoid writing your own find modules, especially when the upstream supplies CMake config modules.
You can direct CMake to your custom Protobuf installation by setting one of CMAKE_PREFIX_PATH or Protobuf_ROOT (v3.12+) to the Protobuf install root.
You can tell find_package to try CONFIG mode first by setting CMAKE_FIND_PACKAGE_PREFER_CONFIG to true (v3.15+). Then set Protobuf_DIR to the directory containing ProtobufConfig.cmake.
Failing all else, you can manually set the variables documented in CMake's own FindProtobuf module, here: https://cmake.org/cmake/help/latest/module/FindProtobuf.html
All these variables can be set at the configure command line with the -D flag.
There are very few environment variables that populate CMake variables to start and I would avoid relying on them. There is an exhaustive list here: https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html. CMAKE_MODULE_PATH is not among them.

CMAKE_SYSROOT in CMakeTestCCompiler

I have a project which builds for PPC, the Toolchain is working correctly, i can build when the sysroot is installed under /opt/poky/1.5. Now i tried to move that Installation to the Project Directory (it is not a part of the Repository there, it is just installed there so it is not reliant on that fix path, so that everyone can check out the project and build it wothout setting up the sysroot under that fixed folder).
To achieve this I set CMAKE_SYSROOT to "${PROJECT_SOURCE_DIR}/poky" where the poky will be installed upon execution of a custom build script (the project also needs to build a secure image, so it is way simpler to use a build script instead of anything else, also this is convenient for jenkins).
Since the CMAKE_SYSROOT is build from the PROJECT_SOURCE_DIR which is different for the CMakeTestCCompiler Project, the cmake call fails teloling me that the CCompiler is broken of course. So I want to know, how I am supposed to get the CMakeTestCCompiler Project to compile with the same CMAKE_SYSROOT variable, without altering the CMakeTestCCompiler Project itself (of course).
Somehow I cannot find an answer anywhere, it seems that noone ever had this issue (which frankly i cannot understand, this should be a common setup in my opinion). (Or maybe i am just too much of a noob when it comes to CMAKE, which i will gladly admit)
I am not interested in solutions like: "JUST INSTALL IT IN A FIX PATH" or such... please, I need the setup like this, I have reasons for that.
THX for reading/trying/answering/helping
Have a nice day
EDIT1:
In CMakeLists.txt (top level CMakeFile so it should be used by any build):
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots")`
In ToolchainCMake (the one given to the cmake as CMAKE_TOOLCHAIN_FILE):
`SET(CMAKE_SYSTEM_NAME Linux)`
`SET(CMAKE_SYSTEM_VERSION 1)`
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots")`
`SET(COMPILER_ROOT ${PROJECT_SOURCE_DIR}/poky/sysroots/i686-pokysdk-linux/usr/bin/powerpc-poky-linux-gnuspe)`
`SET(CMAKE_C_COMPILER ${COMPILER_ROOT}/powerpc-poky-linux-gnuspe-gcc)`
`SET(CMAKE_CXX_COMPILER ${COMPILER_ROOT}/powerpc-poky-linux-gnuspe-g++)`
`MESSAGE("CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")`
`MESSAGE("CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}")`
`MESSAGE("COMPILER_ROOT: ${COMPILER_ROOT}")`
`SET(CMAKE_FIND_ROOT_PATH ${SYS_ROOT}/ppce500v2-poky-linux-gnuspe)`
`SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)`
`SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)`
`SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)`
EDIT2:
I used the
`set(CMAKE_C_COMPILER_WORKS 1 CACHE INTERNAL "")`
`set(CMAKE_CXX_COMPILER_WORKS 1 CACHE INTERNAL "")`
settings to simulate the CMakeTestCCompiler build succeeding and realized that I am facing some additional problems: It seem that the packages are looked up on the system instead of the CMAKE_SYSROOT folder. Even tried the
`SET(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})`
to try to force the search in there, but without luck. In the CMakeError.log I can see, that the compiler itself was configured with the prefix option that points to /opt/poky/1.5, the path that i want to "overwrite", now I am not sure if the compiler could even deal with an alternate path.
I felt the need to add these information, they not really add to the problem at hand.
ERRORS:
I also found some errors in the above cmake:
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots")`
must be
`SET(CMAKE_SYSROOT "${PROJECT_SOURCE_DIR}/poky/sysroots/ppce500v2-poky-linux-gnuspe")`
instead and therefor the
`SET(CMAKE_FIND_ROOT_PATH ${SYS_ROOT}/ppce500v2-poky-linux-gnuspe)`
changes to
`SET(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})`
EDIT: Whole answer changed.
My first suspicion was that the problem is that value of ${PROJECT_SOURCE_DIR} is not known in CMAKE_TOOLCHAIN_FILE as it is processed before CMakeLists.txt. But this isn't true.
I had similar problem (CMake 2.8.12.2), everything worked OK, when I passed cross compiler by CC environment variable with --sysroot option, i.e. CMake was invoked as follows:
CC="arm-linux-gnueabi-gcc --sysroot=/path/to/sysroot" cmake /path/to/sources
When I switched to using toolchain file, CMake started to report that C compiler doesn't work.
To workaround this problem, I use CMakeForceCompiler package. Parts toolchain file (along with comments) I think are relevant:
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# Force compiler - only option that actually works
cmake_force_c_compiler (${TOOLCHAIN_PATH}/bin/arm-linux-gnueabi-gcc GNU)
cmake_force_cxx_compiler(${TOOLCHAIN_PATH}/bin/arm-linux-gnueabi-g++ GNU)
# NOTE: CMAKE_SYSROOT doesn't work as expected
add_definitions("--sysroot=${TOOLCHAIN_SYSROOT}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${TOOLCHAIN_SYSROOT}" CACHE INTERNAL "" FORCE)
Note, that TOOLCHAIN_PATH and TOOLCHAIN_SYSROOT are my local variables set before.