Vcpkg: issues with vcpkg_chainload_toolchain_file

Created on 31 Jan 2018  Â·  10Comments  Â·  Source: microsoft/vcpkg

I started this discussion as a CMake issue:
https://gitlab.kitware.com/cmake/cmake/issues/17698
but finally it looks like something is not ok from the vcpkg point of view, not theirs.

Here it is a quick way to reproduce it:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
project(Issue17698)

if(VCPKG_CHAINLOAD_TOOLCHAIN_FILE)
    include("${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}")
endif()

find_package(OpenMP)

add_executable(openmp_test test.cpp)

test.cpp:

#include <omp.h>
#include <stdio.h>

int main() {
  int nthreads, tid;

/* Fork a team of threads giving them their own copies of variables */
#pragma omp parallel private(nthreads, tid)
  {
    /* Obtain thread number */
    tid = omp_get_thread_num();
    printf("Hello World from thread = %d\n", tid);

    /* Only master thread does this */
    if (tid == 0) {
      nthreads = omp_get_num_threads();
      printf("Number of threads = %d\n", nthreads);
    }

  } /* All threads join master thread and disband */

  return 0;
}

physycom_toolchain.cmake: download this file

https://github.com/physycom/sysconfig/blob/master/cmake/physycom_toolchain.cmake

Then, if you run CMake in this way:

cmake -G "Visual Studio 15" "-DCMAKE_TOOLCHAIN_FILE=$env:WORKSPACE\vcpkg\scripts\buildsystems\vcpkg.cmake" "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$env:WORKSPACE\sysconfig\cmake\physycom_toolchain.cmake" "-DVCPKG_TARGET_TRIPLET=x86-windows-static" "-DCMAKE_BUILD_TYPE=Release" ..

it fails, both at the ABI detection and at the FindOpenMP stage. Both because of a try_compile issue due to the fact that executables are produced in an unexpected folder.

If I run

cmake -G "Ninja" "-DCMAKE_TOOLCHAIN_FILE=$env:WORKSPACE\vcpkg\scripts\buildsystems\vcpkg.cmake" "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$env:WORKSPACE\sysconfig\cmake\physycom_toolchain.cmake" "-DVCPKG_TARGET_TRIPLET=x86-windows-static" "-DCMAKE_BUILD_TYPE=Release" ..

(using Ninja instead of MSBuild) it works.

It works also with just defining

cmake -G "Visual Studio 15" "-DCMAKE_TOOLCHAIN_FILE=$env:WORKSPACE\vcpkg\scripts\buildsystems\vcpkg.cmake" "-DVCPKG_TARGET_TRIPLET=x86-windows-static" "-DCMAKE_BUILD_TYPE=Release" ..

or

cmake -G "Visual Studio 15" "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$env:WORKSPACE\sysconfig\cmake\physycom_toolchain.cmake" "-DVCPKG_TARGET_TRIPLET=x86-windows-static" "-DCMAKE_BUILD_TYPE=Release" ..

just not both.

I need to have my chainloaded file in place to properly link with static builds, otherwise it's a flood of LIBCMT warnings.

Thanks a lot, in advance, for your help

Most helpful comment

Thank you a lot for all your precious suggestions.
Just a little of background to close this issue and explain our situation, maybe it could be useful for you to understand how we use vcpkg in a real case scenario.
I am a physicist and in my research group we have an extremely diverse development environment. macOS, linux (I am a big fan of the Windows Subsystem for Linux, btw) and also Windows, of course! Before I started a huge work inside my group, everyone was using different build tools, different versions of them, writing their own makefile or deploying custom VS solutions, depending on the personal preferences. Those techniques were effective but not really smart: they were, first of all, totally unportable.
Thanks to Git and CMake (and vcpkg on windows, apt on Ubuntu and brew on macOS) I was able to prepare a common workspace for everybody, based on versioning and being able to simply exchange codes (beside the public repositories, we have tens of private projects going on, mostly prototyping new ideas). This workflow finally (but slowly) entered into our life.
We are mostly C++ programmers, even if, to be honest, we really mastered programming while working on the codes themselves.
If it were not for vcpkg, I think that Windows would have had a hard life to really survive easily in our group, because other systems were starting to be much more C++-developers-friendly. I was also finally able to instruct my students with windows laptops (I teach Physics 1 to students in Computer Science, they will easily beat me later in their career but they start in a much more manageable situation :) ) to keep using Windows to code, without having to distribute a pre-built linux VM or suggesting Cygwin to quickly bootstrap their development environment and to create the simple tools I request them to study during the course, since libraries are necessary for almost every project they had to discuss.

This introduction is maybe necessary to explain why we need those flags and why maybe we are so naive, in the end, in using tools designed by _real_ programmers (we are not, in the end… For us coding should just be a way to instruct computers with our ideas, but in the end our code often evolve into fully-featured big projects…)

In fact, I found out that the static triplet was necessary for two reasons:

  1. to be able to run executables from the outside of their build folder (when testing ideas with multiple inputs, it’s useful to be able to put the executable in one predictable place (its bin folder), then prepare big testing scripts, put a link to executable inside a personal folder included in the PATH and then run it from wherever you want on your machine).
  2. Even working around dynamically-loaded libraries playing with PATH, some libraries (I look at you, FLTK - used even too much in our group - I’d like to switch many projects to QT5 now that they are clearly well supported under vcpkg) have unpredictable behaviour when dynamically linked (there are maybe some bugs in the port file, I have never been able to find them).

Why vcpkg does not tell the compiler to use /MT if using static triplets is still beyond my comprehension, since the libraries themselves are linked to the static CRT and so conflict with the dll version. If I don’t force the proper flags (CMake not supporting them out-of-the-box is another mystery to me, but again I am a physicist, I didn’t study computer science apart from playing with computers since 4 y.o.) the warnings are flooding the screen, so no reasons to not set it, what am I missing?

The redirect of the output, on the other hand, was necessary for two other reasons:

  1. If I remember correctly, the vcpkg auto-deploy of the dependencies does not work for the CMake install target, leaving libraries in the build folder (we need to have executables in a predictable folder for our scripts, so I thought about using a --target install for the build phase but I had to abandon it)
  2. Even if the auto-deploy was added to vcpkg for the install target, Visual Studio 2017, while being nicely integrated with CMake (finally in VS 15.5 it works ok, before it was a bug fest, to be honest), still cannot trigger the install target from the GUI, so it was not viable for our members which does not want to deal with these problems and just want to work on their ideas [cannot blame them!] (and in this case the executable was in a really total unpredictable folder since VS hides them in a hashed folder in your home)

Again, thanks to vcpkg, thanks to a lot of work and now also thanks to your suggestions, many problems are fixed and developing on windows is almost as smooth as on any other OSes (even better for some things), with the complicated things layered away.
As you say, maybe a custom triplet statically linking to libraries but not to CRT would be perfect (why is it not the default for static, btw?), but deploying another custom layer on top was scaring for me at the beginning. Now I think it should be _easily (?)_ feasible, I will let you know. This would clean up our CMakeLists.txt files, because all the flag manipulation could be removed, am I right?

All 10 comments

Thanks for this issue! This is a bit of an advanced scenario, so I'm happy to see it getting some use :)!

First, I don't think you should ever need to do the following from your project:

if(VCPKG_CHAINLOAD_TOOLCHAIN_FILE)
    include("${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}")
endif()

We automatically handle that here: https://github.com/Microsoft/vcpkg/blob/1f6f27a5480a02434614cc48aed5178a9e69bb4e/scripts/buildsystems/vcpkg.cmake#L10-L12

Next, the designed behavior should be equivalent to creating your own "super toolchain" file that looks like:

# supertoolchain.cmake
include(path/to/my/base/toolchain.cmake)
include(path/to/vcpkg/scripts/buildsystems/vcpkg.cmake)

and using that in your project as the CMAKE_TOOLCHAIN_FILE setting. Could you try that out and confirm that it works? If it doesn't, it means we're probably "functioning as advertised" but there's some cross-toolchain interference instead.

Looking at the toolchain you linked [1], I see a few things that aren't _strictly_ needed:

  • Defining VCPKG_CRT_LINKAGE and VCPKG_LIBRARY_LINKAGE here should have no effect -- if they do it's a bug :)
  • I notice you're setting the output directory with [2]. This _should_ be fine afaik, but I haven't seen that done in a _toolchain_ file. Could you try setting that inside your CMakeLists.txt after the project() directive instead of in the toolchain file?

[1] https://github.com/physycom/sysconfig/blob/a22e053fe2c69cef0121b8ff0e1a726b11cdf1fd/cmake/physycom_toolchain.cmake
[2] https://github.com/physycom/sysconfig/blob/a22e053fe2c69cef0121b8ff0e1a726b11cdf1fd/cmake/physycom_toolchain.cmake#L83-L88

Thanks a lot for your reply.
I tried experimenting as suggested. I modified the chainload file and created a supertoolchain.cmake as suggested. Instead of including both files, it is done defining the VCPKG_CHAINLOAD_TOOLCHAIN_FILE (https://github.com/physycom/sysconfig/blob/master/cmake/supertoolchain.cmake):

# supertoolchain.cmake

set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE $ENV{WORKSPACE}/sysconfig/cmake/physycom_toolchain.cmake)
include($ENV{WORKSPACE}/vcpkg/scripts/buildsystems/vcpkg.cmake)

(including or setting the variable didn't change the result, in both case I had to remove the output directory setting from the toolchain to the CMakeLists.txt to pass tests)
I also removed the superfluous definition from the CMakeLists.txt . The CMakeLists.txt now looks as follows:

cmake_minimum_required(VERSION 3.10)
project(Issue17698)

### Set output directories on a per-configuration base
# Single configuration
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin )
# Multi configuration
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
    string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
    set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_SOURCE_DIR}/bin )
endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES )

find_package(OpenMP)

add_executable(openmp_test test.cpp)

This fixed the ABI compatibility check, but also completely avoid my script, like it is not even there. It does not set anything for the build. All the warnings are back like there's no physycom_toolchain.cmake loaded.
Anyway, it seems that defining the output directory in the toolchain file breaks the MSBuild generator (all the others are perfectly fine, including also Ninja on Windows - at the cmake issue they said also MSBuild should work and to investigate it because the try_compile overload maybe is doing something wrong)
For what concern the VCPKG_CRT_LINKAGE and the VCPKG_LIBRARY_LINKAGE, yes I also thought that it should not have any effect, but in reality it has. If not set, I have many conflicts at linking time with libcmt.lib

I had to update also previous comment.
In fact, now that I removed the

if(VCPKG_CHAINLOAD_TOOLCHAIN_FILE)
    include("${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}")
endif()

from my CMakeLists.txt I have the problem that my script are not loaded... now I remember why I put it there!
Maybe I am using CMake wrong, but I don't want to copy the same thing in all my scripts, so I thought that using a common file and chainloading it would be ok, but it doesn't seem so...

I find it easiest to think about two distinct "modes" where your cmake code is run:

  • "toolchain" mode -- this is everything that gets pulled in via CMAKE_TOOLCHAIN_FILE
  • "project" mode -- this is everything that is pulled in via your CMakeLists.txt

Most things you want to do are only valid in one of those modes, so they shouldn't go into the same file. On the other hand, there's nothing to stop you from having two files!

# physycom_toolchain.cmake
set some CXX FLAGS here
# physycom_project.cmake
set your output directory, or maybe run a bunch of boilerplate find_package() calls here

Then chainload the first one through vcpkg, and use an include() statement to pull the second one in after your project() directive!

yeah, you're perfectly right. I was overcomplicating my situation.
Now at the beginning of my CMakeLists.txt I do this:

if (EXISTS "$ENV{WORKSPACE}/sysconfig/cmake/physycom_config.cmake")
   include("$ENV{WORKSPACE}/sysconfig/cmake/physycom_config.cmake")
endif()

and I just load vcpkg toolchain without any other chainloading.
In this way my scripts are pulled in during project mode and they are correctly run, and also ABI checks are performed ok since the modification to the output folder are not inserted during toolchain mode.

Also, you were right about VCPKG_LIBRARY_LINKAGE and VCPKG_CRT_LINKAGE, sorry for my previous comment. I remember I had to add them to remove the warnings, but now I deleted those lines and it works.
Just one question remains in my head. Why do I have to set the proper /MT flag when using static triplets and cmake is not instructed already to do so by vcpkg?

We could do that, but we felt it would be too invasive. We view the /MT setting (along with other compiler settings) as being part of a larger, fundamental "background" toolchain that we shouldn't _really_ be providing ourselves. This "background" would also include things like Windows SDK version, compiler version, target OS version, etc. If we specified too many of those, I think we would make the tool more difficult to use.

That said, I could definitely see a place to add a few convenience things such as explicitly requesting /MT.

Additionally, have you tried creating a triplet that dynamically links the CRT but uses static linking for the libraries? It might serve your use cases better.

Thank you a lot for all your precious suggestions.
Just a little of background to close this issue and explain our situation, maybe it could be useful for you to understand how we use vcpkg in a real case scenario.
I am a physicist and in my research group we have an extremely diverse development environment. macOS, linux (I am a big fan of the Windows Subsystem for Linux, btw) and also Windows, of course! Before I started a huge work inside my group, everyone was using different build tools, different versions of them, writing their own makefile or deploying custom VS solutions, depending on the personal preferences. Those techniques were effective but not really smart: they were, first of all, totally unportable.
Thanks to Git and CMake (and vcpkg on windows, apt on Ubuntu and brew on macOS) I was able to prepare a common workspace for everybody, based on versioning and being able to simply exchange codes (beside the public repositories, we have tens of private projects going on, mostly prototyping new ideas). This workflow finally (but slowly) entered into our life.
We are mostly C++ programmers, even if, to be honest, we really mastered programming while working on the codes themselves.
If it were not for vcpkg, I think that Windows would have had a hard life to really survive easily in our group, because other systems were starting to be much more C++-developers-friendly. I was also finally able to instruct my students with windows laptops (I teach Physics 1 to students in Computer Science, they will easily beat me later in their career but they start in a much more manageable situation :) ) to keep using Windows to code, without having to distribute a pre-built linux VM or suggesting Cygwin to quickly bootstrap their development environment and to create the simple tools I request them to study during the course, since libraries are necessary for almost every project they had to discuss.

This introduction is maybe necessary to explain why we need those flags and why maybe we are so naive, in the end, in using tools designed by _real_ programmers (we are not, in the end… For us coding should just be a way to instruct computers with our ideas, but in the end our code often evolve into fully-featured big projects…)

In fact, I found out that the static triplet was necessary for two reasons:

  1. to be able to run executables from the outside of their build folder (when testing ideas with multiple inputs, it’s useful to be able to put the executable in one predictable place (its bin folder), then prepare big testing scripts, put a link to executable inside a personal folder included in the PATH and then run it from wherever you want on your machine).
  2. Even working around dynamically-loaded libraries playing with PATH, some libraries (I look at you, FLTK - used even too much in our group - I’d like to switch many projects to QT5 now that they are clearly well supported under vcpkg) have unpredictable behaviour when dynamically linked (there are maybe some bugs in the port file, I have never been able to find them).

Why vcpkg does not tell the compiler to use /MT if using static triplets is still beyond my comprehension, since the libraries themselves are linked to the static CRT and so conflict with the dll version. If I don’t force the proper flags (CMake not supporting them out-of-the-box is another mystery to me, but again I am a physicist, I didn’t study computer science apart from playing with computers since 4 y.o.) the warnings are flooding the screen, so no reasons to not set it, what am I missing?

The redirect of the output, on the other hand, was necessary for two other reasons:

  1. If I remember correctly, the vcpkg auto-deploy of the dependencies does not work for the CMake install target, leaving libraries in the build folder (we need to have executables in a predictable folder for our scripts, so I thought about using a --target install for the build phase but I had to abandon it)
  2. Even if the auto-deploy was added to vcpkg for the install target, Visual Studio 2017, while being nicely integrated with CMake (finally in VS 15.5 it works ok, before it was a bug fest, to be honest), still cannot trigger the install target from the GUI, so it was not viable for our members which does not want to deal with these problems and just want to work on their ideas [cannot blame them!] (and in this case the executable was in a really total unpredictable folder since VS hides them in a hashed folder in your home)

Again, thanks to vcpkg, thanks to a lot of work and now also thanks to your suggestions, many problems are fixed and developing on windows is almost as smooth as on any other OSes (even better for some things), with the complicated things layered away.
As you say, maybe a custom triplet statically linking to libraries but not to CRT would be perfect (why is it not the default for static, btw?), but deploying another custom layer on top was scaring for me at the beginning. Now I think it should be _easily (?)_ feasible, I will let you know. This would clean up our CMakeLists.txt files, because all the flag manipulation could be removed, am I right?

I am setting up my own triplet and things are promising. Setting the environment variable VCPKG_DEFAULT_TRIPLET also makes it very easy to control the default for the installation of libraries. But one things looks strange and I'd like a confirmation: this default does not work for the cmake configuration, am I right (when I am using the library in my project, not when building the libraries themselves)? It looks like I still have to pass the -DVCPKG_TARGET_TRIPLET="my-custom-triplet" on the command line. I looked into vcpkg.cmake toolchain file and in fact I couldn't find any hint to it loading the $ENV{VCPKG_DEFAULT_TRIPLET} before trying to build the VCPKG_TARGET_TRIPLET string. Why not?

Your observations are correct: you will still need to pass -DVCPKG_TARGET_TRIPLET. This is because we currently autodetect the correct triplet based on the compiler used (so if you're targeting x64, you get x64-windows, if x86, x86-windows, if uwp, uwp). We _assume_ that users will be better served by the autodetection by default, but this could be wrong!

If you want it to always target VCPKG_DEFAULT_TRIPLET:

  • You could always pass -DVCPKG_TARGET_TRIPLET=%VCPKG_DEFAULT_TRIPLET%
  • You could create a toolchain file that sets everything you'd like:
# cenit-toolchain.cmake
set(VCPKG_TARGET_TRIPLET $ENV{VCPKG_DEFAULT_TRIPLET})
include(C:/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake)

and then use -DCMAKE_TOOLCHAIN_FILE=cenit-toolchain.cmake.

passing -DVCPKG_TARGET_TRIPLET=%VCPKG_DEFAULT_TRIPLET% was exactly my preferred choice. Thanks a lot!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

oahzuw picture oahzuw  Â·  3Comments

pkeir picture pkeir  Â·  3Comments

tzbo picture tzbo  Â·  3Comments

spindensity picture spindensity  Â·  3Comments

invy picture invy  Â·  3Comments