17

There are utilities which use an existing compiler by adding a command as a prefix (so instead of calling cc -c file.c you could call distcc cc -c file.c).

When using CMake the compiler command can be changed, however I ran into problems trying to use distcc, though this would likely apply to any command prefix to the compiler (ccache too).

  • CMake expects the compiler to be an absolute path,
    so setting CMAKE_C_COMPILER to /usr/bin/distcc /usr/bin/cc, gives an error:

    /usr/bin/distcc /usr/bin/cc is not a full path to an existing compiler tool.

  • Setting the compiler to /usr/bin/distcc and
    CMAKE_C_COMPILER_ARG1 or CMAKE_C_FLAGS to begin with /usr/bin/cc works in some cases, but fails with CHECK_C_SOURCE_COMPILES
    (checked if there was some way to support this, even prefixing CMAKE_REQUIRED_FLAGS didn't work).

The only way I found to do this is to wrap the commands in a shell script.

#!/bin/sh
exec /usr/bin/distcc /usr/bin/cc "$@"

While this works, It would be nice to be able to use compiler helpers with CMake, without having to go though shell scripts (giving some small overhead when the build system could just use a command prefix).


So my question is:

Can CMake use compiler prefix commands (such as distcc) directly?, without shell script wrappers?

ideasman42
  • 42,413
  • 44
  • 197
  • 320
  • 1
    Is setting your `CC`/`CXX` environment variables when calling cmake (e.g. `CC="distcc gcc" cmake ..`) insufficient for some reason? – Iskar Jarak Sep 24 '15 at 03:10
  • @Iskar Jarak, This works! (so I guess this can be the answer). Interestingly, internally its using `CMAKE_C_COMPILER` for the first command and `CMAKE_C_COMPILER_ARG1` for the second. I'm not sure whats going on - because I tried setting these in cmake-gui already and `CMAKE_C_COMPILER_ARG1` was being ignored with `CHECK_C_SOURCE_COMPILES`. – ideasman42 Sep 24 '15 at 06:39
  • Glad it works. Setting the environment variable probably sets some other things as well, things that `CHECK_C_SOURCE_COMPILES` use... although I'm not 100% what... which is why messing with individual variables in CMake is such a pain in the neck. – Iskar Jarak Sep 24 '15 at 06:48

2 Answers2

18

Since CMake 3.4.0 there has been a CMAKE_<LANG>_COMPILER_LAUNCHER variable and corresponding target property <LANG>_COMPILER_LAUNCHER. So if your project is C-only you would do something like:

cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache /path/to/source
CCACHE_PREFIX=distcc make -j`distcc -j`

If you have a C++ project, use -DCMAKE_CXX_COMPILER_LAUNCHER=ccache.

Or, make your CMakeLists.txt smart and use ccache automatically if it can be found:

#-----------------------------------------------------------------------------
# Enable ccache if not already enabled by symlink masquerading and if no other
# CMake compiler launchers are already defined
#-----------------------------------------------------------------------------
find_program(CCACHE_EXECUTABLE ccache)
mark_as_advanced(CCACHE_EXECUTABLE)
if(CCACHE_EXECUTABLE)
  foreach(LANG C CXX)
    if(NOT DEFINED CMAKE_${LANG}_COMPILER_LAUNCHER AND NOT CMAKE_${LANG}_COMPILER MATCHES ".*/ccache")
      message(STATUS "Enabling ccache for ${LANG}")
      set(CMAKE_${LANG}_COMPILER_LAUNCHER ${CCACHE_EXECUTABLE} CACHE STRING "")
    endif()
  endforeach()
endif()
nocnokneo
  • 1,857
  • 20
  • 24
  • According to the CMake docs, the `_COMPILER_LAUNCHER` target property will only be honoured by the Makefiles and Ninja generators. You have to do more work to get Xcode to use the launcher. See [this answer](http://stackoverflow.com/a/36515503/1938798) for a more general method which uses the older RULE_LAUNCH_COMPILE variable and CMAKE_XCODE_ATTRIBUTE_..., but has similarities to your approach. – Craig Scott Apr 10 '16 at 22:33
  • Sadly, CMAKE__COMPILER_LAUNCHER is ignored in CHECK__SOURCE_COMPILES, even with the latest cmake (3.10.1). That means the wrapper is still needed if the test result depends on the launcher. – proski Jan 23 '18 at 00:26
3

Just as a hint: never use <LANG>_COMPILER_LAUNCHER to cross compile. If <LANG>_COMPILER_LAUNCHER is used together with distcc the absolute compiler path is sent to distcc and the host is not using the cross comping toolchain!

Instead you should use the old school method, just overwrite the compiler path:

export PATH=/usr/lib/distcc:$PATH

It took me hours to find out...