microsoft / vcpkg

C++ Library Manager for Windows, Linux, and MacOS
MIT License
22.9k stars 6.32k forks source link

vcpkg-tool does not respect CMake project's CMAKE_CXX_COMPILER #41136

Open ADKaster opened 6 days ago

ADKaster commented 6 days ago

Describe the bug vcpkg-tool only considers the target triplet when detecting what compiler to use to build vcpkg dependencies for CMake projects.

Environment

To Reproduce Steps to reproduce the behavior, on Ubuntu 22.04:

  1. Create a new project in manifest mode
  2. Add a dependency, e.g. fmt
  3. Create a CMakeLists.txt that loads the vcpkg toolchain file
  4. Configure CMake project with -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_C_COMPILER=clang-18
  5. Observe the message from vcpkg-tool:
    Detecting compiler hash for triplet x64-linux...
    Compiler found: /usr/bin/c++

The compiler name/path is not used when detecting what compiler to use when building vcpkg ports. Only the default search mechanisms for CMake are involved. That means, /usr/bin/cc and $CC environment variable

Expected behavior

vcpkg will build ports with the compiler specified to be used via -DCMAKE_C_COMPILER, -DCMAKE_CXX_COMPILER.

This behavior should be consistent with how chain-loading another toolchain file works.

Failure logs

Terminal output shown inside details ``` andrew@Andrew-Workstation:~/dev/vcpkg-cmake-cxx-compiler$ cat vcpkg.json { "dependencies": [ "fmt" ] } andrew@Andrew-Workstation:~/dev/vcpkg-cmake-cxx-compiler$ cat CMakeLists.txt cmake_minimum_required(VERSION 3.22) project(Example LANGUAGES CXX) find_package(fmt CONFIG REQUIRED) add_executable(example main.cpp) target_link_libraries(example PRIVATE fmt::fmt) target_compile_features(example PUBLIC cxx_std_17) andrew@Andrew-Workstation:~/dev/vcpkg-cmake-cxx-compiler$ cat main.cpp #include int main() { fmt::println("hello, world!"); } andrew@Andrew-Workstation:~/dev/vcpkg-cmake-cxx-compiler$ cmake -GNinja -B build -DCMAKE_TOOLCHAIN_FILE=/home/andrew/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 -- Running vcpkg install Detecting compiler hash for triplet x64-linux... Compiler found: /usr/bin/c++ The following packages will be built and installed: fmt:x64-linux@11.0.2 * vcpkg-cmake:x64-linux@2024-04-23 * vcpkg-cmake-config:x64-linux@2024-05-23 Additional packages (*) will be modified to complete this operation. Restored 3 package(s) from /home/andrew/.cache/vcpkg/archives in 16.1 ms. Use --debug to see more details. Installing 1/3 vcpkg-cmake-config:x64-linux@2024-05-23... Elapsed time to handle vcpkg-cmake-config:x64-linux: 534 us vcpkg-cmake-config:x64-linux package ABI: bc50feb59200c32f0d107fdd56cb9673025fd1674e50fe8357b309bbc4aead5c Installing 2/3 vcpkg-cmake:x64-linux@2024-04-23... Elapsed time to handle vcpkg-cmake:x64-linux: 454 us vcpkg-cmake:x64-linux package ABI: a8369f48befea9026b5586cad8f947eb75c0f115fc69bd111f9e3167d72d7bb5 Installing 3/3 fmt:x64-linux@11.0.2... Elapsed time to handle fmt:x64-linux: 1.01 ms fmt:x64-linux package ABI: 08a2a9c498ee0d9b65a31c9e28a5de1b844824be28171bf1d7e775a63e663757 Total install time: 2.02 ms The package fmt provides CMake targets: find_package(fmt CONFIG REQUIRED) target_link_libraries(main PRIVATE fmt::fmt) # Or use the header-only version find_package(fmt CONFIG REQUIRED) target_link_libraries(main PRIVATE fmt::fmt-header-only) -- Running vcpkg install - done -- The CXX compiler identification is Clang 18.1.8 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/clang++-18 - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done (1.0s) -- Generating done (0.0s) CMake Warning: Manually-specified variables were not used by the project: CMAKE_C_COMPILER -- Build files have been written to: /home/andrew/dev/vcpkg-cmake-cxx-compiler/build andrew@Andrew-Workstation:~/dev/vcpkg-cmake-cxx-compiler$ /usr/bin/c++ --version c++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ```

As seen in the terminal output above, vcpkg finds /usr/bin/c++, which is g++-11 in my system. However, I explicitly asked for clang++-18, which is properly detected by the CMake output for configuring my project proper.

vcpkg:

-- Running vcpkg install
Detecting compiler hash for triplet x64-linux...
Compiler found: /usr/bin/c++

my project:

-- The CXX compiler identification is Clang 18.1.8
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/clang++-18 - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done

Additional context

The code involved is here: https://github.com/microsoft/vcpkg-tool/blob/2af426b284d7a321628471c6baef46452385534d/src/vcpkg/commands.build.cpp#L683-L699

The code seems to use the toolset argument, which I would expect corresponds to the -T flag used for MSVC compiles. However, on Linux and macOS, the toolset flag is not valid and not used for Ninja, Unix Makefile and Xcode generators, and most users will configure with -DCMAKE_C_COMPILER and -DCMAKE_CXX_COMPILER.

ADKaster commented 6 days ago

Note that I can work around this by doing something like this:

/path/to/my/local/vcpkg-triplets/release-triplets/x64-linux.cmake

set(VCPKG_CMAKE_SYSTEM_NAME Linux)
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_BUILD_TYPE release)
set(VCPKG_LIBRARY_LINKAGE static)

# Hackery using a private vcpkg variable to get to the root of the invoking project's CMake binary dir when using manifest mode
include(${_VCPKG_INSTALLED_DIR}/../build-vcpkg-variables.cmake OPTIONAL)

And then generating build-vcpkg-variables.cmake like so:

CMakeLists.txt

cmake_minimum_required(VERSION 3.25)

# Pass additional information to vcpkg toolchain files if we are using vcpkg.
if (CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg.cmake$")
    set(CMAKE_PROJECT_MyProject_INCLUDE_BEFORE "vcpkg/generate_vcpkg_toolchain_variables.cmake")
endif()

project(MyProject LANGUAGES C CXX)

vcpkg/generate_vcpkg_toolchain_variables.cmake

set(EXTRA_VCPKG_VARIABLES "")
if (NOT "${CMAKE_C_COMPILER}" STREQUAL "")
    string(APPEND EXTRA_VCPKG_VARIABLES "set(ENV{CC} ${CMAKE_C_COMPILER})\n")
endif()
if (NOT "${CMAKE_CXX_COMPILER}" STREQUAL "")
    string(APPEND EXTRA_VCPKG_VARIABLES "set(ENV{CXX} ${CMAKE_CXX_COMPILER})\n")
endif()

file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/build-vcpkg-variables.cmake" "${EXTRA_VCPKG_VARIABLES}")

But this approach feels verbose and possibly fragile? For something that I would expect to "just work" (and does seem to just work for MSVC toolsets).

ADKaster commented 5 days ago

For additional context, this seems to be a thing limited to Unix Makefiles and Ninja Generators (and other bespoke generators, I suppose).

To quote Brad King from a recent CMake Issue https://gitlab.kitware.com/cmake/cmake/-/issues/26316#note_1571027

The VS and Xcode generators do not support specifying the path to the compiler directly for any language. The situation is the same for CMAKE_C_COMPILER and CMAKE_CXX_COMPILER. For those generators one is supposed to set CMAKE_GENERATOR_TOOLSET to control compiler selection.

So it seems like for VS and Xcode, the toolset should specify all the information needed to select a specific compiler. But for other generators, users expect that CMAKE_C_COMPILER and CMAKE_CXX_COMPILER will set the name/path of the desired compiler.