Environment and version details
Operating System+version: Windows Server 2025 (Docker container, windowsservercore-ltsc2025)
Compiler+version: Visual Studio 2026 Build Tools (18.x), with additional MSVC toolset components: v141 (14.16), v142 (14.29), v143 (14.44)
Shell: cmd
B2 Version: [output of b2 -v]
Brief problem description
Since VS2017, the Visual Studio installer supports installing older MSVC toolsets as components of a single newer Visual Studio, e.g. Microsoft.VisualStudio.Component.VC.v141.x86.x64 inside VS2026 Build Tools. They install under the newer VS root:
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.16.27023
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.29.30133
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.44.35207
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.50.35717
(Directory names above are examples from one machine. The exact versions change with every Visual Studio servicing release, and the minor component grows over a product's lifetime — the v142 toolset began at 14.20 with VS2019 16.0 and ended at 14.29, and the VS2022 v143 family spans both 14.3x and 14.4x. So a fix cannot pin literal versions; it has to match version families.)
Microsoft's supported mechanism for selecting one is vcvarsall.bat -vcvars_ver=14.16, where a two-component value selects the newest installed 14.16.x toolset.
b2 cannot use these toolsets. toolset=msvc-14.1 fails to find them, because msvc.jam only looks for each toolset version inside its own generation's Visual Studio product:
Detection — the path globs are pinned per generation (.version-14.1-path only matches Microsoft Visual Studio/2017//VC/Tools/MSVC/, 14.2 only 2019, 14.3 only 2022, 14.5 only 18), and the vswhere fallback limits by product version range (e.g. -version "[15.0,16.0)" for 14.1). A 14.16 toolset under the 18 root is invisible to both.
Environment setup — even when given the command explicitly (using msvc : 14.1 : <path to 14.16 cl.exe> ;), generate-setup-cmd walks up to the enclosing VS's Auxiliary\Build\vcvarsall.bat and calls it without -vcvars_ver, initializing INCLUDE/LIB for the newest installed toolset (14.50) while running the 14.16 compiler. The string vcvars_ver does not appear anywhere in msvc.jam.
This appears to share a root cause with #445, which is the mirror image: there, the version-blind glob VC/Tools/MSVC/* enumerates the oldest embedded toolset first and uses it for a newer toolset request. In both directions, msvc.jam assumes toolset version == Visual Studio product generation, which multi-toolset installations break.
This setup is increasingly common: GitHub Actions runner images and consolidated CI containers ship exactly this kind of multi-toolset Visual Studio.
Steps to reproduce the issue
Install VS2026 Build Tools with an older toolset component, e.g.: vs_buildtools.exe --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.VC.v141.x86.x64 ... (no other Visual Studio on the machine)
b2 toolset=msvc-14.1
Actual behavior summary
warning: Did not find command for MSVC toolset. If you have Visual Studio 2017 installed
you will need to specify the full path to the command, set VS150COMNTOOLS for your
installation, or build from the 'Visual Studio Command Prompt for VS 2017'.
All subsequent configuration checks fail (cxx11_constexpr : no, default address-model : none, ...), no targets are built — and the exit code is still 0, so CI reports success while having compiled nothing (that last aspect is #544).
Expected behavior summary
toolset=msvc-14.1 locates the v141 toolset inside any installed Visual Studio and initializes the environment with vcvarsall.bat -vcvars_ver=. Two implementation notes, since the toolset directory versions cannot be hardcoded:
Detection should enumerate VC/Tools/MSVC/* under every VS root (or use vswhere without the product-version fence) and map each discovered directory to a b2 toolset by version family, not by literal version: 14.1x -> msvc-14.1, 14.2x -> msvc-14.2, 14.3x and 14.4x -> msvc-14.3 (Microsoft continued the VS2022 v143 family into 14.4x, see https://devblogs.microsoft.com/cppblog/msvc-toolset-minor-version-number-14-40-in-vs-2022-v17-10/), 14.5x -> msvc-14.5. Alternatively, when the corresponding component is installed, VC\Auxiliary\Build\Microsoft.VCToolsVersion.v141.default.txt (likewise v142, v143) contains the exact default toolset version for that family; these files are what vcvarsall.bat itself reads, and other tools (rust cc-rs, conan) already use them for the same purpose.
Environment setup only needs the first two version components: vcvarsall.bat -vcvars_ver=14.16 resolves to the newest installed 14.16.x, so b2 never has to know the full patch version.
Workaround:
Registering each embedded toolset explicitly in site-config.jam, with a one-line wrapper batch file as the script to pin the toolset:
C:\vcvars\v141.bat
@call "C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" %* -vcvars_ver=14.16
site-config.jam
using msvc : 14.1 : "C:/Program Files (x86)/Microsoft Visual Studio/18/BuildTools/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe" : "C:/vcvars/v141.bat" ;
(The full cl.exe path is discovered at container-build time by globbing VC/Tools/MSVC/14.1*; the wrapper itself is stable across servicing updates because -vcvars_ver=14.16 resolves to whatever 14.16.x is installed.)
This works correctly, which suggests the fix is contained to detection and setup-command generation in msvc.jam rather than anything deeper.
Environment and version details
Operating System+version: Windows Server 2025 (Docker container, windowsservercore-ltsc2025)
Compiler+version: Visual Studio 2026 Build Tools (18.x), with additional MSVC toolset components: v141 (14.16), v142 (14.29), v143 (14.44)
Shell: cmd
B2 Version: [output of b2 -v]
Brief problem description
Since VS2017, the Visual Studio installer supports installing older MSVC toolsets as components of a single newer Visual Studio, e.g. Microsoft.VisualStudio.Component.VC.v141.x86.x64 inside VS2026 Build Tools. They install under the newer VS root:
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.16.27023
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.29.30133
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.44.35207
C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.50.35717
(Directory names above are examples from one machine. The exact versions change with every Visual Studio servicing release, and the minor component grows over a product's lifetime — the v142 toolset began at 14.20 with VS2019 16.0 and ended at 14.29, and the VS2022 v143 family spans both 14.3x and 14.4x. So a fix cannot pin literal versions; it has to match version families.)
Microsoft's supported mechanism for selecting one is vcvarsall.bat -vcvars_ver=14.16, where a two-component value selects the newest installed 14.16.x toolset.
b2 cannot use these toolsets. toolset=msvc-14.1 fails to find them, because msvc.jam only looks for each toolset version inside its own generation's Visual Studio product:
Detection — the path globs are pinned per generation (.version-14.1-path only matches Microsoft Visual Studio/2017//VC/Tools/MSVC/, 14.2 only 2019, 14.3 only 2022, 14.5 only 18), and the vswhere fallback limits by product version range (e.g. -version "[15.0,16.0)" for 14.1). A 14.16 toolset under the 18 root is invisible to both.
Environment setup — even when given the command explicitly (using msvc : 14.1 : <path to 14.16 cl.exe> ;), generate-setup-cmd walks up to the enclosing VS's Auxiliary\Build\vcvarsall.bat and calls it without -vcvars_ver, initializing INCLUDE/LIB for the newest installed toolset (14.50) while running the 14.16 compiler. The string vcvars_ver does not appear anywhere in msvc.jam.
This appears to share a root cause with #445, which is the mirror image: there, the version-blind glob VC/Tools/MSVC/* enumerates the oldest embedded toolset first and uses it for a newer toolset request. In both directions, msvc.jam assumes toolset version == Visual Studio product generation, which multi-toolset installations break.
This setup is increasingly common: GitHub Actions runner images and consolidated CI containers ship exactly this kind of multi-toolset Visual Studio.
Steps to reproduce the issue
Install VS2026 Build Tools with an older toolset component, e.g.: vs_buildtools.exe --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.VC.v141.x86.x64 ... (no other Visual Studio on the machine)
b2 toolset=msvc-14.1
Actual behavior summary
warning: Did not find command for MSVC toolset. If you have Visual Studio 2017 installed
you will need to specify the full path to the command, set VS150COMNTOOLS for your
installation, or build from the 'Visual Studio Command Prompt for VS 2017'.
All subsequent configuration checks fail (cxx11_constexpr : no, default address-model : none, ...), no targets are built — and the exit code is still 0, so CI reports success while having compiled nothing (that last aspect is #544).
Expected behavior summary
toolset=msvc-14.1 locates the v141 toolset inside any installed Visual Studio and initializes the environment with vcvarsall.bat -vcvars_ver=. Two implementation notes, since the toolset directory versions cannot be hardcoded:
Detection should enumerate VC/Tools/MSVC/* under every VS root (or use vswhere without the product-version fence) and map each discovered directory to a b2 toolset by version family, not by literal version: 14.1x -> msvc-14.1, 14.2x -> msvc-14.2, 14.3x and 14.4x -> msvc-14.3 (Microsoft continued the VS2022 v143 family into 14.4x, see https://devblogs.microsoft.com/cppblog/msvc-toolset-minor-version-number-14-40-in-vs-2022-v17-10/), 14.5x -> msvc-14.5. Alternatively, when the corresponding component is installed, VC\Auxiliary\Build\Microsoft.VCToolsVersion.v141.default.txt (likewise v142, v143) contains the exact default toolset version for that family; these files are what vcvarsall.bat itself reads, and other tools (rust cc-rs, conan) already use them for the same purpose.
Environment setup only needs the first two version components: vcvarsall.bat -vcvars_ver=14.16 resolves to the newest installed 14.16.x, so b2 never has to know the full patch version.
Workaround:
Registering each embedded toolset explicitly in site-config.jam, with a one-line wrapper batch file as the script to pin the toolset:
C:\vcvars\v141.bat
@call "C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" %* -vcvars_ver=14.16
site-config.jam
using msvc : 14.1 : "C:/Program Files (x86)/Microsoft Visual Studio/18/BuildTools/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe" : "C:/vcvars/v141.bat" ;
(The full cl.exe path is discovered at container-build time by globbing VC/Tools/MSVC/14.1*; the wrapper itself is stable across servicing updates because -vcvars_ver=14.16 resolves to whatever 14.16.x is installed.)
This works correctly, which suggests the fix is contained to detection and setup-command generation in msvc.jam rather than anything deeper.