Skip to content

Constrain VS2017 MSBuild detection to VS2017 in vswhere query - #1

Open
BIMvoice wants to merge 2 commits into
IfcOpenShell:masterfrom
BIMvoice:fix-vs2017-msbuild-detection
Open

Constrain VS2017 MSBuild detection to VS2017 in vswhere query#1
BIMvoice wants to merge 2 commits into
IfcOpenShell:masterfrom
BIMvoice:fix-vs2017-msbuild-detection

Conversation

@BIMvoice

Copy link
Copy Markdown

msbuild.bat locates MSBuild via vswhere -latest ... -property installationPath with no version constraint. When more than one Visual Studio version is installed (e.g. 2017 plus a newer 2019, 2022 or 2026), -latest picks the newest install instead of VS2017. The subsequent MSBuild\15.0\Bin\MSBuild.exe existence check then silently fails against that wrong path, msbdir is left unset, and the later !msbdir!\msbuild.exe call fails with a bare "msbuild.exe is not recognized" error that gives no indication of the real cause.

Adds -version "[15.0,16.0)", vswhere's documented range syntax for the VS2017 product line, so the query only ever matches a VS2017 install regardless of what else is present. Also fails loudly with a clear message if vswhere itself, a matching VS2017 install, or its MSBuild.exe can't be found, instead of falling through to the generic error further down.

Traced from IfcOpenShell/IfcOpenShell#8631 and the workaround in IfcOpenShell/IfcOpenShell#8685 (which pre-detects msbdir this same way from the caller side, since it could not fix this script directly). This is a root-cause fix of the detection logic itself so callers no longer need that workaround.

This was traced from the script logic and vswhere's documented version-range syntax, not run on a real Windows VS2017 installation, since none was available in the environment this was developed in. Would appreciate a test from anyone with a real VS2017 + newer-VS setup before merging.

Generated with the assistance of an AI coding tool.

msbuild.bat locates MSBuild via `vswhere -latest ... -property
installationPath` with no version constraint. When more than one
Visual Studio version is installed (e.g. 2017 plus a newer 2019,
2022 or 2026), `-latest` picks the newest install instead of VS2017.
The subsequent `MSBuild\15.0\Bin\MSBuild.exe` existence check then
silently fails against that wrong path, msbdir is left unset, and
the later `!msbdir!\msbuild.exe` call fails with a bare
"msbuild.exe is not recognized" error that gives no indication of
the real cause.

Add `-version "[15.0,16.0)"`, vswhere's documented range syntax for
the VS2017 product line, so the query only ever matches a VS2017
install regardless of what else is present. Also fail loudly with a
clear message if vswhere itself, a matching VS2017 install, or its
MSBuild.exe can't be found, instead of falling through to the
generic error further down.

Traced from IfcOpenShell/IfcOpenShell#8631 and the workaround in
IfcOpenShell/IfcOpenShell#8685 (which pre-detects msbdir this same
way from the caller side, since it could not fix this script
directly). This is a root-cause fix of the detection logic itself
so callers no longer need that workaround. Traced from the script
logic and vswhere's documented version-range syntax; not run on a
real Windows VS2017 installation, since none is available in this
environment.

Generated with the assistance of an AI coding tool.
BIMvoice added a commit to BIMvoice/IfcOpenShell that referenced this pull request Jul 19, 2026
Andrej730/mpir-vs2026's unconstrained "vswhere -latest" detection picks a
newer Visual Studio when multiple are installed, breaking the VS2017 MSBuild
path (IfcOpenShell#8631). A logic fix for this has been proposed directly in
IfcOpenShell's own fork of that dependency (IfcOpenShell/mpir-vs2026#1),
so point the clone at that fork instead of upstream.

Kept the local msbdir pre-detection workaround alongside this redirect: the
fork-side fix is unverified on real Windows and not yet merged, and the
workaround costs nothing once it lands, since msbuild.bat already skips its
own detection when msbdir is pre-set.

Generated with the assistance of an AI coding tool.
@ccic-techni

Copy link
Copy Markdown

I used the following
git clone --recursive -b fix-8631-vs2017-msbuild-detect https://github.com/BIMvoice/IfcOpenShell.git IfcOpenShell-fix

but got this result

C:\Users\chris\work\cad\ifcopenshellbuildissue\IfcOpenShell-fix>cd win

C:\Users\chris\work\cad\ifcopenshellbuildissue\IfcOpenShell-fix\win>build-deps.cmd

This script fetches and builds all IfcOpenShell dependencies

Generator not passed, but VisualStudioVersion=15.0 environment variable detected:
using '""Visual Studio 15 2017"`'" as the generator.
GENERATOR: ["Visual Studio 15 2017"]
VS_VER: [2017]
VS_PLATFORM: [x64]
VS_TOOLSET: []
VC_VER: [14.1]
ARCH_BITS: [64]
TARGET_ARCH: [x64]
BOOST_BOOTSTRAP_VER: [vc141]
BOOST_TOOLSET: [msvc-14.1]
BOOST_WIN_API: []
build-type-cfg.cmd: Warning: BUILD_CFG not specified - using the default RelWithDebInfo
\Microsoft was unexpected at this time.

C:\Users\chris\work\cad\ifcopenshellbuildissue\IfcOpenShell-fix\win>

@BIMvoice

Copy link
Copy Markdown
Author

Thanks for testing this @ccic-techni, and sorry for the regression. Found it: the VS2017 detection block references paths under Program Files (x86), and cmd.exe has a nasty quirk where a ( or ) inside an expanded %VAR% value confuses its parsing of multi-line IF (...) blocks. cmd expands the variable while reading the line, then scans that expanded text for the block's closing paren by counting parens without any quote awareness, so the literal (x86) supplies a phantom matched pair and the rest of the path is left dangling as a bogus statement. Its first token is \Microsoft, which is exactly the error you saw, right after the vswhere block started running.

The fix switches those references to delayed expansion (!VAR!), which resolves at execution time after the block boundaries are already determined. That matches the convention this file already uses elsewhere.

Could you pull the updated fix-8631-vs2017-msbuild-detect branch and re-run win\build-deps.cmd on your VS2017 machine? That is the real-world test this needs. I do not have a Windows box, so the fix is verified by reasoning against cmd.exe's documented parsing behaviour rather than by an actual run, and your retest is what would confirm it.

This comment was written with AI assistance.

@ccic-techni

Copy link
Copy Markdown

It is getting further.

HEAD is now at 365957b vs26 - update toolsets
Your branch is up to date with 'origin/master'.
HEAD is now at 365957b vs26 - update toolsets
C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild.exe /p:Platform=x64 /p:Configuration=Release /p:"WindowsLIB0SDKLIB0Version=" .\lib_mpir_gc\lib_mpir_gc.vcxproj
Microsoft (R) Build Engine version 15.9.30+gc0e4b0e683 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

MSBUILD : error MSB1008: Only one project can be specified.
Switch: (x86)\Microsoft

For switch syntax, type "MSBuild /help"
ERROR: msbuild failed for main project with exit code 1

An error occurred Last logged action:

Build ended at 18:44:22.80. Time elapsed 0:02:57.30.

…ucceeds

ccic-techni reported (IfcOpenShell#1, comment 5032275723)
that once msbdir is actually found, the build still fails:

    MSBUILD : error MSB1008: Only one project can be specified.
    Switch: (x86)\Microsoft

Two independent defects in the invocation lines, both pre-existing
(present on master before this branch, since this branch's earlier
commits only touch the detection block):

1. msbdir is assigned with the quote characters embedded in its
   value, e.g. set msbdir="!InstallDir!\MSBuild\15.0\Bin", then used
   as !msbdir!\msbuild.exe. The closing quote lands right after
   "Bin", with \msbuild.exe glued on unquoted immediately after with
   no separating space. That hybrid quoted/unquoted token is what
   MSBuild's own argument parser was choking on: a path like
   C:\Program Files (x86)\... that is not cleanly one quoted
   argument gets split into extra tokens, one of which
   ((x86)\Microsoft) is reported as a second, illegal "project".
   Fixed by storing msbdir without embedded quotes and quoting the
   whole exe path at the point of use ("!msbdir!\msbuild.exe"),
   matching the convention already used correctly in
   msvc/vs19/msbuild.bat, msvc/vs22/msbuild.bat and
   msvc/vs26/msbuild.bat (msb_exe="%msb_dir%\MSBuild.exe"). The
   msbdir="C:\...\Bin" literals for the unreachable (vs_version is
   hardcoded to 2017) VS2013/2015 branches are switched to
   set "msbdir=...", which protects the literal parentheses in
   "(x86)" from being misread as closing the enclosing if-block,
   without embedding quote characters into the value.

2. The Windows SDK version override property is written as
   /p:"Windows%20SDK%20Version=%win_sdk%". In batch, %20 is not a
   URL-encoded space, it is the substitution %2 (this script's own
   library-type argument, LIB or DLL) followed by a literal "0".
   With library type LIB this expands to
   /p:"WindowsLIB0SDKLIB0Version=", exactly matching the mangled
   property name ccic-techni saw. There is no property by that name
   anywhere, so the %5 "Windows SDK version" argument documented in
   this script's own header has never actually overridden anything.
   The vcxproj files declare <WindowsTargetPlatformVersion>, so that
   is the correct property name; fixed to
   /p:"WindowsTargetPlatformVersion=%win_sdk%".

The same %20 property-name bug also exists in msvc/vs19,
msvc/vs22 and msvc/vs26's msbuild.bat (all currently share the
literal /p:"Windows%20SDK%20Version=%win_sdk%" text), but those
scripts already quote msb_exe correctly at the point of use, so they
are not affected by defect 1. Left untouched here since this PR's
scope is the VS2017 script.

Generated with the assistance of an AI coding tool.
@BIMvoice

Copy link
Copy Markdown
Author

Thanks for testing, and for the detailed log, that helped a lot.

Two separate bugs, both further down the script than the detection fix in this PR:

  1. Windows%20SDK%20Version in the msbuild invocation isn't a URL-encoded space. In batch, %2 is your library-type argument (LIB), so %20 expands to LIB0, producing the WindowsLIB0SDKLIB0Version you saw. That property name is meaningless to MSBuild, and it's also why the "Windows SDK version" argument this script documents has never actually worked. Fixed to the real property name, WindowsTargetPlatformVersion.
  2. The detected MSBuild path was stored with the quote characters baked into the variable, then \msbuild.exe was appended unquoted right after. That mismatched quoting is the likely cause of the MSB1008: Only one project can be specified / Switch: (x86)\Microsoft error, since C:\Program Files (x86)\... needs to reach MSBuild as one cleanly quoted argument. Fixed to match how the newer vs19/vs22/vs26 scripts already do it.

Both are pushed to this branch now. One thing I noticed in your log: it shows HEAD is now at 365957bc ... up to date with origin/master right before the failure, which is master's tip, not this branch's. Could you double check you're on fix-vs2017-msbuild-detection when you re-test? Either way you would have hit this same failure, since the earlier commits on this branch didn't touch these lines, but I want the retest to reflect the latest state.

Would appreciate another test run when you get a chance.

@ccic-techni

Copy link
Copy Markdown

I have tried again. Note that in the mean time I used the original workaround and have started to compile the code.

I needed to use boost_1_74_0 as it won't easily compile with Visual Studio 2017.

There are a number of issues with lambda functions and the use of auto that Visual Studio 2017 won't compile.

I am required to use Visual Studio 2017 on this project, so I am stuck, but right now the issue below is the least of my problems.

I appreciate your help, but the work around is all I need for now.

I used
git clone --recursive -b fix-8631-vs2017-msbuild-detect https://github.com/BIMvoice/IfcOpenShell.git IfcOpenShell-fix
to get the code.

I see this

Cloning into 'C:\Users\chris\work\cad\ifcopenshellbuildissue\IfcOpenShell-fix_deps\mpir'...
remote: Enumerating objects: 56421, done.
remote: Counting objects: 100% (1125/1125), done.
remote: Compressing objects: 100% (157/157), done.
remote: Total 56421 (delta 654), reused 1102 (delta 646), pack-reused 55296 (from 2)
Receiving objects: 100% (56421/56421), 20.65 MiB | 20.04 MiB/s, done.
Resolving deltas: 100% (37966/37966), done.
Updating files: 100% (4283/4283), done.
Checking out mpir revision .
HEAD is now at 365957b vs26 - update toolsets
Your branch is up to date with 'origin/master'.
HEAD is now at 365957b vs26 - update toolsets
C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild.exe /p:Platform=x64 /p:Configuration=Release /p:"WindowsLIB0SDKLIB0Version=" .\lib_mpir_gc\lib_mpir_gc.vcxproj
Microsoft (R) Build Engine version 15.9.30+gc0e4b0e683 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

MSBUILD : error MSB1008: Only one project can be specified.
Switch: (x86)\Microsoft

For switch syntax, type "MSBuild /help"
ERROR: msbuild failed for main project with exit code 1

An error occurred Last logged action:

Build ended at 11:28:45.50. Time elapsed 0:02:46.95.

@BIMvoice

Copy link
Copy Markdown
Author

Thanks for the follow-up, and that log explains it: build-deps.cmd clones mpir from this repo's master branch, so the fixed msbuild.bat in this PR never reaches your build regardless of which IfcOpenShell branch you clone. The mangled WindowsLIB0SDKLIB0Version line in your output is the pre-fix script, exactly as expected.

So no action needed on your side, your workaround is the right call for now; once this PR is merged, the standard build path picks the fix up automatically.

On the VS2017 C++ compile errors you are now hitting in IfcOpenShell itself (lambdas/auto): current IfcOpenShell targets C++17, and VS2017's support for it is incomplete, so building the current codebase with VS2017 is likely to keep fighting you. If the 2017 toolset is a hard project constraint, an older IfcOpenShell release from the VS2017 era (or the prebuilt wheels/binaries if usable in your setup) may be the pragmatic route. Good luck with the project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants