Use non-default libs with non-default compiler on Linux

I’m trying to build an app with non-system-wide clang & gcc. And while for gcc I just have to set the correct compiler and everything works correctly, with clang I have a different story. I use the following preset (only var parts):

"cacheVariables": {
        "CMAKE_C_COMPILER": "clang-23",
        "CMAKE_CXX_COMPILER": "clang++-23",
        "CMAKE_CXX_FLAGS": "--gcc-install-dir=/opt/gcc-16.2.0/lib/gcc/x86_64-linux-gnu/16.2.0"
      }

If I build the app it will link against an undesirable system-wide libstdc++: -L/usr/lib/gcc/x86_64-linux-gnu/15. As I understand it, this link is inferred from CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES which looks like this for me:

set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/15;/usr/lib64;/lib/x86_64-linux-gnu;/lib64;/usr/lib/x86_64-linux-gnu;/lib;/usr/lib")

I can fix it by providing the link path as follows:

"cacheVariables": {
        "CMAKE_C_COMPILER": "clang-23",
        "CMAKE_CXX_COMPILER": "clang++-23",
        "CMAKE_CXX_FLAGS": "--gcc-install-dir=/opt/gcc-16.2.0/lib/gcc/x86_64-linux-gnu/16.2.0",
        "CMAKE_EXE_LINKER_FLAGS": "-L/opt/gcc-16.2.0/lib64"
      }

And now the implicit var will look like:

set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/opt/gcc-16.2.0/lib64;/opt/gcc-16.2.0/lib/gcc/x86_64-linux-gnu/16.2.0;/lib/x86_64-linux-gnu;/lib64;/usr/lib/x86_64-linux-gnu;/usr/lib64;/lib;/usr/lib")

After that change, the app links against a desirable library and everything works.

Is this the way to do it, variable-wise? Or is there a better way to achieve what I want?
While I showed a preset here I will most likely do it with a toolchain file so my question is only about variables/commands to use, not where I should set them.

try CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN in the toolchain file, set to the gcc install root. that gets passed to clang during compiler detection, which is why a flag in CMAKE_CXX_FLAGS alone doesnt change the implicit dirs it already worked out.

Thanks! But as I understand this variable is the inferior --gcc-toolchain option in disguise, and I’d like to stay with the newer one

Fortunately, I have found a different proper solution. Unfortunately I still do not understand why the “unproper” one didn’t work.

So I posted some wrong info in the OP where I claimed that the CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES has an incorrect dir in it, it is in fact has all the correct dirs with just that preset vars:

"cacheVariables": {
        "CMAKE_C_COMPILER": "clang-23",
        "CMAKE_CXX_COMPILER": "clang++-23",
        "CMAKE_CXX_FLAGS": "--gcc-install-dir=/opt/gcc-16.2.0/lib/gcc/x86_64-linux-gnu/16.2.0"
      }

After such a configuration there are 2 places where I could find “/usr/lib/gcc/x86_64-linux-gnu/15”: CMakeConfigureLog.yaml and CMakeCCompiler.cmake where it is used as follows:

set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-linux-gnu/15;/usr/lib64;/lib/x86_64-linux-gnu;/lib64;/usr/lib/x86_64-linux-gnu;/lib;/usr/lib")

After seeing that it is the only place, the fix is obvious: set CMAKE_C_FLAGS:

"cacheVariables": {
      "CMAKE_C_COMPILER": "clang-23",
      "CMAKE_CXX_COMPILER": "clang++-23",
      "CMAKE_C_FLAGS": "--gcc-install-dir=/opt/gcc-16.2.0/lib/gcc/x86_64-linux-gnu/16.2.0",
      "CMAKE_CXX_FLAGS": "--gcc-install-dir=/opt/gcc-16.2.0/lib/gcc/x86_64-linux-gnu/16.2.0"
    }

And now “/usr/lib/gcc/x86_64-linux-gnu/15” would be gone from everywhere, the app would be building correctly and all is good. But. Why the CMAKE_C_IMPLICIT_LINK_DIRECTORIES variable influenced a linking step with a C++ compiler? Moreover, with the C-flags set I don’t have the “-L” anything where before I had “-L/usr/lib/gcc/x86_64-linux-gnu/15”—it is completely gone.

Edit:
Looks like CMAKE_C_IMPLICIT_LINK_DIRECTORIES would be used if I link against a library with a C++ compiler, and it is exactly what I do. Still not sure why it is filtered as much (having just “-L/usr/lib/gcc/x86_64-linux-gnu/15” out of all the content in one case, and completely missing in another).

When linking different languages together, say Fortran and Rust, CMake is only able to invoke one driver as the linker. It must use either the Fortran driver, or the Rust driver, it can’t use both.

To make this work, CMake discovers and forwards implicit link flags from each driver.

C and C++ are, to CMake, different languages. They get the same discovery and flag forwarding treatment.