ilammy/msvc-dev-cmd

Using bash Overrides Microsoft LINK.EXE With /usr/bin/link

Closed this issue · 3 comments

This action appears to work as advertised for CL.EXE and NMAKE.EXE, great!

But I hit a snag when trying to call LINK, and got a message extra operand '/subsystem:console which I did not get when running the same command in a "Developer Command Prompt"

It seems that path-wise, there's a non-MS /usr/bin/link file that out-prioritizes the MS LINK.EXE that is added to the path. To prove this is the case, I tried just deleting it:

- name: Delete Unwanted Link to Stop It From Interfering
  run: rm /usr/bin/link.exe

Once I did this, the correct link was found and worked. Ugly workaround, though. :-/

Is it within the scope of this GitHub Action's mission to try and sort out such an issue? Even if there was just a setting like need-ms-link: true that renamed the other linker out of the way, it seems it would help document the issue in a central place for when a better answer came along.

Oh hello! Thanks for you the interest, I'm constantly surprised how many people actually use this thing...

Is it within the scope of this GitHub Action's mission to try and sort out such an issue?

It depends.

Removing specific files is probably not in scope of “setting up MSVC development environment”. It's not like there an exhaustive list of binaries that must be available in PATH and must point to MSVC's stuff after this tool is run. So I'd rather not hardcode LINK.EXE specifically.

Say, what if someone sets up their env to have CL.EXE mean "Common Lisp" and they would like to use MSVC but for compiling C# code or whatever, nor caring for the C compiler. They wouldn't be amused if CL.EXE was removed so we probably shouldn't do that. But now, why LINK.EXE should be removed but CL.EXE shouldn't? That's why I'm not a fan of tweaking the env like that. If your environment contains /usr/bin which conflicts with what MSVC wants to use, then please set up your environment so that it does not happen.


Now, there's another thing about why msvc-dev-cmd does not override /usr/bin. I have assumed that this happens because it appends MSVC's directories to PATH. So if /usr/bin was already there then it will be preferred. If that was the case, it could have been a reasonable feature to have a flag, say, prepend-env: true which would make msvc-dev-cmd prepend its stuff into the env, not append it.

However, after running some experiments I see that msvc-dev-cmd does both.

What new paths are added and where

These paths are prepended to existing Path value and should be preferred:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\\Extensions\Microsoft\IntelliCode\CLI
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.28.29333\bin\HostX64\x64
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\VCPackages
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\bin\Roslyn
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Team Tools\Performance Tools\x64
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Team Tools\Performance Tools
C:\Program Files (x86)\Microsoft Visual Studio\Shared\Common\VSPerfCollectionTools\vs2019\\x64
C:\Program Files (x86)\Microsoft Visual Studio\Shared\Common\VSPerfCollectionTools\vs2019\
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\
C:\Program Files (x86)\HTML Help Workshop
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\FSharp\
C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64
C:\Program Files (x86)\Windows Kits\10\bin\x64
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\\MSBuild\Current\Bin
C:\windows\Microsoft.NET\Framework64\v4.0.30319
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\

These paths are appended to Path value and will be used, unless original Path has an override:

C:\Program Files (x86)\Microsoft Visual Studio\Installer
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\x64\bin
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\Linux\bin\ConnectionManagerExe

So I'd guess that if /usr/bin were in the PATH before msvc-dev-cmd is run then it should have been shadowed by newly added paths. If it's not, this probably means that /usr/bin is prepended to the path after msvc-dev-cmd is run. If that's the case, nothing we can do about it here.

Could you please clarify which /usr/bin causes path conflicts for you and how exactly it gets into PATH?


P.S. Wow, that's a fancy username and userpic you've got there 😂 Got me scared a bit.

Could you please clarify which /usr/bin causes path conflicts for you and how exactly it gets into PATH?

I figured out that the problem came from the fact that I'm using shell: bash on Windows. (It's a logical choice when trying to maintain a scripts on lots of platforms to have the uniformity.)

/usr/bin/link is actually not a linker at all (like ld) but a tool for creating a symlink (like ln).

https://www.gnu.org/software/coreutils/manual/coreutils.html#link-invocation

Removing specific files is probably not in scope of “setting up MSVC development environment”.

Maybe the step could have outputs which tell you where files are as a last resort?

- name: Enable MSVC Dev Commands
  id: msvc-dev-cmd
  uses: ilammy/msvc-dev-cmd@v1

- name: Set Detected LINK and CL Environment Variables
  run: |
    echo "LINK=${{ steps.msvc-dev-cmd.outputs.link }}" >> $GITHUB_ENV
    echo "CL=${{ steps.msvc-dev-cmd.outputs.cl }}" >> $GITHUB_ENV

If those can be assumed to be accessible from a common directory with more tools in it then maybe giving back that directory is the more general thing...

I'm constantly surprised how many people actually use this thing...

Thanks for the prompt response...and if you keep maintaining small things and become the go-to person for that thing, it can take on a life of its own over time. Maybe there are enough people that I'm not the last person to try and use bash with it!

Perhaps just checking to see what the shell is and printing out a warning if it's not Powershell or CMD would be helpful. Or a section in the README.md on the general topic of path conflicts--this being an example--and what people might do about it.


that's a fancy username and userpic you've got there 😂 Got me scared a bit.

Programming is serious business. 🍴

Uh-huh... So MSVC paths are added to the environment, but then any task that uses shell: bash will have GNU environment set up for it. As it appears, it prepends /mingw64/bin, /usr/bin, and /c/Users/runneradmin/bin to PATH. This basically ensures that unqualified link will resolve into /usr/bin/link. Regardless of if you call MSVC action before or after that. I wonder if there is a nice workaround for that.

Maybe the step could have outputs which tell you where files are as a last resort?

I believe an appropriate action here depends on how this output is meant to be used to work around the issue with GNU link conflicting with MSVC's link. Which only surfaces if you actually use MSVC toolchain from within bash, which is probably the case for people using this action if they are running bash.

One thing that would be "easy" to do is to return all the 'interesting' variables like PATH which the actions sets. Then it it's to users to... uh... prepend that to their PATH again?..

If we do return absolute paths resolved immediately after setting MSVC variables, what are the users supposed to do with them? Somehow persuade their build system to use this absolute path for link instead of just link?..

IDK. All of it seems to be awful and as a user I'd prefer everything to "just work" 🤪

I got this problem into my subconscious, maybe a solution will present itself. I'll also go review the docs for action developers, maybe there's some neat hack available there to work around issues like this. GITHUB_PATH does not look like it, since shell: bash adds its stuff on top of GITHUB_PATH still.

Perhaps just checking to see what the shell is and printing out a warning if it's not Powershell or CMD would be helpful.

I guess this could catch the case when bash is set as the default shell for the entire job. (No idea how to access that, but I guess it's possible). But this action definitely wouldn't see the case if you leave the default shell for most actions, but then set shell: bash only for some – which then may have their link resolve into something unexpected.

A warning (from the action) in that might be appropriate. But there should be a way to disable it to not be annoying. And it's not really nice to bash people with warnings just because they are using bash by default.

A section in README would be nice, I'll add that regardless of any other thing. Hopefully, it will help people avoid this pitfall. There's at least link which can have conflicts, maybe there's more.