Rust: Add support for the Visual Studio 2017 linker

Created on 24 Dec 2016  Ā·  49Comments  Ā·  Source: rust-lang/rust

With Visual Studio 2017, Microsoft has changed the folder structure it uses for everything. link.exe is now located at C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.10.24728\bin\HostX64\x64\link.exe. As you can see, the folder is dependent on the actual edition of Visual Studio as well.

Currently, Rust does not seem to recognize this path correctly. I donā€™t know exactly what Rust needs to figure out the paths, but if you need some information (e.g. registry values, contents of vcvarsall.bat or something), just tell me what you need and Iā€™ll try my best to provide you with it.

O-windows-msvc P-high T-dev-tools

Most helpful comment

I figured out the COM API and have created a basic demonstration of getting the path to the linker.

Next step is creating a proper crate for this and pulling in all the other detection logic used by rustc and gcc.

All 49 comments

I already spoke with the Visual C++ team a few months ago and someone was going to investigate adding support for this. I'll poke them and see if any progress has been made, and if not I'll end up doing it myself. I have VS 2017 myself so there should be nothing I'd need from you.

For reference, a code sample for finding VS 2017 instances https://code.msdn.microsoft.com/windowsdesktop/Visual-Studio-Setup-0cedd331

Concerning a workaround: if this is the same as Visual Studio ā€œ15ā€ Preview 4 (which had the new installer and directory structures): you can run vcvarsall.bat to set up the current terminal and then it all works.

For me, this works:

"C:\Program Files (x86)\Microsoft Visual Studio\VS15Preview\Common7\IDE\VC\vcvarsall.bat" x64

So, I spent some time with the Rust compilerā€™s source code and with vcvarsall.bat and my Visual Studio environment to figure out a few things:

  • get_vc_dir ā€“ At least on my machine, in order to find the Visual Studio install directory for the version code 15.0, we also have to check the Wow6432Node node at HKLM:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7. Fortunately, this also does appear to work for older versions, so it might be enough to simply check there first, and then fall back to HKLM:\SOFTWARE\Microsoft\VisualStudio\SxS\VS7.
  • vcvars.bat then loads the tools version from %VSINSTALLDIR%VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt which is a text file that contains the version number, currently __VCVARS_TOOLS_VERSION=14.10.24728.
  • VCToolsInstallDir then becomes %VSINSTALLDIR%VC\Tools\MSVC\%__VCVARS_TOOLS_VERSION%\".
  • link.exe is then inside %VCToolsInstallDir%bin%__VCVARS_HOST_DIR%%__VCVARS_HOST_NATIVEDIR%

The values for __VCVARS_HOST_DIR and _VCVARS_HOST_NATIVEDIR are dependant on the platform:

if /I "%VSCMD_ARG_HOST_ARCH%" == "x86" (
    set __VCVARS_HOST_DIR=\HostX86
    set __VCVARS_HOST_NATIVEDIR=\x86
)
if /I "%VSCMD_ARG_HOST_ARCH%" == "x64" (
    set __VCVARS_HOST_DIR=\HostX64
    set __VCVARS_HOST_NATIVEDIR=\x64
)
if /I "%VSCMD_ARG_HOST_ARCH%" == "arm" (
    set __VCVARS_HOST_DIR=\HostARM
    set __VCVARS_HOST_NATIVEDIR=\arm
)

Maybe that helps.

@chris-morgan Yeah, using the vcvarsall.bat to set up the environment variables always works. But itā€™s also enough to just augment the PATH environment variable to include the path to the linker and to set the LIB environment variable.

Visual Studio 2017 supports multi-instance installation, meaning you can install it more than once and each instance can have its own setup of options enabled/disabled. Therefore there is no longer a canonical location for Visual Studio to exists. That said, the "%ProgramFiles(x86)%\Microsoft Visual Studio\2017" is still the best shot.

Ideally, users can compile Rustlang projects from a "Developer Command Prompt for Visual Studio 2017", this will resolve most (if not all) of the issues.

For users developing Rust in an IDE (like VS Code) can benefit from launching the IDE from the developer command prompt or by leveraging the logic provided by @poke, which should work for the common case.

@artl93 for a more authoritative information.

Hi. Let me go and start by saying I am not familiar with rust particulars, so I am going to draw some conclusions based on the thread above. Please bear with me, as must of what I am about to go into just relates to how we want to think of tools, the application and the system.Ā 

We wanted to make Visual Studio not impact the operating system. We wanted the fact thatĀ Visual Studio is installed to no longer impact the system-wide environment. By impacting the system environment, we made itĀ impossible to have our compilers or tools sit side-by-side without having to ship a new side-by-side version of Visual Studio.Ā We really wanted customers to be more free to install newer versions without the fear of corrupting their build environments and working tools, making it an easier call to try out new stuff without the fear of being able toĀ get work done.

To accomplish this, we really separated VS from the system-wideĀ SDKs and runtimes thatĀ VS works with. We really wanted VS to be an "application" that consumes these things, rather than a part of each these. This meant taking the time to de-couple VSĀ and the SDKsĀ from the environment, making a much more flexible system for everyone. That also means we're going to have to understand how tools really relate to the IDE so that they are coupled in the best way possible.

The VC++ toolset is interesting when we think of this model. In doing the work to de-couple SDKs from the IDE, the VC tools in particular turned out to be tightly coupled to the IDE. Therefore, the VC toolset ships as a part of the IDE application folder, and not something that is deployed system-wide. Therefore, each Visual Studio application on the system has its own, compatible copy of the VC tools.

In the case of Rust, it sounds like it's heavily dependent on the VC tools, based on the context in the thread. As such,Ā if you think of rust being heavily dependent on the VC tools, then they are by transitive closure dependent on the Visual Studio environment. Since the Visual Studio environment is a part of the application environment (rather than something that is shared and creating system-impact), I think we could think of the rust toolset as being a part of that environment. Therefore, I think we no longer want to think about the tools as being system-wide, but rather an extension to the VSĀ IDE (application) environment. Therefore, you'd want to just extend the environment for the VS application, and not some system wide setting.

To make this work, you can extend the environment for the particular application install using VSIXes (the new version). To add environment variables to the developer command prompt, you can add your own bat filesĀ (yes, *.bat) to: Common7\tools\vsdevcmdext. In the file, define a call_script_helper.

You can see how this is set up by looking at vsdevcmd.bat:
for /F %%a in ( 'dir "%VS150COMNTOOLS%vsdevcmd\ext\*.bat" /b /a-d-h /on' ) do (call :call_script_helper ext\%%a)

You can also look at examples in the ext folder as to how others have set this up. I'm not sure if this is documented, but it should be. I canĀ confirmĀ with the team that did the work.

The one last thing: if you depend on the Ā VC tools, then you'll want to declare that dependency in your VSIX.Ā 

Does that make sense, or am I way off base here?

@artl93 I do not believe that Rust has a Vsix, I believe that they just want to use the MS provided linker (and masm?) tools. Basically, how can a Rust make file find the location of the linker (and assembler?)?

@whoisj

Visual Studio 2017 supports multi-instance installation, meaning you can install it more than once

Iā€™m not sure how that works though. When I rerun the installer, I can only modify my existing 2017 Enterprise installation but not install a second parallel one. Since the installer is the primary entry point for this case, I would say we can assume that there would only be a single installation at the default location.

That said, the "%ProgramFiles(x86)%\Microsoft Visual Studio\2017" is still the best shot.

That wonā€™t work though since the actual edition is also part of the path now, e.g. ā€¦\2017\Enterprise in my case.

Ideally, users can compile Rustlang projects from a "Developer Command Prompt for Visual Studio 2017", this will resolve most (if not all) of the issues.

The dev command prompt is a very limited tool though, being way inferior to PowerShell or other command line interfaces. I assume Git Bash is also a common alternative shell for use with Rust projects. Requiring the developer command prompt there by default does not seem to be a good idea there.

@artl93

Therefore, the VC toolset ships as a part of the IDE application folder, and not something that is deployed system-wide.

But is there technically any difference between a VC toolset 14.10.24728 installed by VS2017 Enterprise, and a VC toolset 14.10.24728 installed by another edition? Would it not have been possible to ship this with the IDE but still place it in a more generic folder instead?

In the case of Rust, it sounds like it's heavily dependent on the VC tools, based on the context in the thread.

Itā€™s actually not as complicated as it may sound. Rust actually only depends on the linker link.exe and the libraries that ship with VC. Thatā€™s why itā€™s enough to add the VC bin folder to PATH and set up the LIB environment variable to make Rust work.

Thatā€™s why I donā€™t really agree that Rust should convert into some extension to Visual Studio or the VC tools. And as said above, the fact that the Visual Studio tools depend that much on a cmd command prompt can be pretty limiting.


Anyway, to get back to what I am trying to get at: Rust already figures out which Visual Studio version is available. The MSVC detection written by @retep998 is already very sophisticated and works fine in many cases. Itā€™s just that the detection is not yet compatible with the changed path structure in VS2017, so it simply wonā€™t work yet. What this issue is about is fixing that detection to work with a standard VS2017 installation. We canā€™tā€”and probably shouldnā€™t even attempt toā€”handle edge cases or highly customized setups. For situations like that, itā€™s always possible for users to set up the environment variables themselves, and Rust will simply use that.

So if you have multiple versions of Visual Studio installed (for whatever reason), and Rust ends up using a different version than the one you want it to use (not perfectly sure why that would make an actual difference though), then you will have to tell Rust to use the correct version anyway. That is not limited to just VS2017 but applies to any version.

The steps Iā€™ve written above are actually not that complicated. The current code that figures out where the MSVC linker is is already very well written to support this additional logic. And Iā€™m absolutely willing to provide the patches necessary to make it work myself. Itā€™s just that Iā€™m just beginning with Rust, so I donā€™t feel comfortable contributing to the compiler just yet. I would eventually give it a try though.

@poke

But is there technically any difference between a VC toolset 14.10.24728 installed by VS2017 Enterprise, and a VC toolset 14.10.24728 installed by another edition? Would it not have been possible to ship this with the IDE but still place it in a more generic folder instead?

The VC tools are tightly coupled to the IDE, therefore version X of the IDE must useĀ a specific version X of the toolset. They are aĀ part of the application folder.Ā 
In the future, we anticipate that 2017's updates can be installed side-by-side with the RTW or previous updates, so picking theĀ compiler associated with your install is critical to gettingĀ the environment set-up correctly.

@artl93 I believe the @poke specifically doesn't care about the IDE, but only the toolset. Given that information, and a system with VS 2017 installed (at least once): what is the best solution for the Rust make system to find the necessary tools provided by VS 2017>

Yeah, what Rust basically needs to do is to be able to answer the question: ā€œWhere is the VC linker and library folder?ā€

Currently it does that by explicitly checking against every Visual Studio version starting with the latest known version. Obviously, this requires to keep adding new logic as newer versions of Visual Studio are released. In an ideal world, there would be a single constant and future-proof location in the registry we could look at to find the path to the most-current VC toolset.

Until we are there, we keep adding logic, with the stategy outlined above being my suggestion to find the current tools for (hopefully) any VS2017 installation.

I'd really rather not hardcode all those relative paths; it would be incredibly brittle. The proper solution involves using special COM interfaces to enumerate the VC++ instances. Unless someone from Microsoft decides to do it themselves, I'm likely going to do the COM stuff myself. It's just a bit icky doing COM in rustc itself without winapi to help out.

@dten Thatā€™s an interesting thing, but Iā€™m not too sure if this could/should be bundled with rust. Maybe if it ends up being part of Visual Studio in the future and is provided in a central location, then one could rely on it.

Currently, it does not seem to support versions older than VS 2017, so using this wouldnā€™t make it the only solution but there would still a complex branching strategy be involved to find the correct installation.

Update:
https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/

Still nothing easily usable without undesirable dependencies.
Maybe someone is brave enough to translate this - https://github.com/adamwelch/vs-setup-samples/blob/master/Setup.Configuration.VC.NoDeps/Program.cpp into Rust though?

Because I have been trying to figure this out for way too long (I am not used to this stuff):

For the workaround to work, you need to execute the vcvars*.bat (e. g. "C:\Program Files (x86)\Microsoft Visual Studio2017\Community\VC\Auxiliary\Build\vcvars64.bat") in the Command Prompt. It won't work in a PowerShell prompt. Also, the rustc command will only work within the Command Prompt where you ran vcvars*.bat

Hopefully this helps someone else who doesn't understand a word of the above, too. šŸ˜„

@Borkason You can also open a command prompt and set it up at the same time by running one of the "_ Command Prompt for VS 2017" shortcuts in the start menu. (Probably should be the "_ Native Tools" one matching your Rust toolchain - "x86 Native Tools" for i646, "x64 Native Tools" for x86_64 - though you could also use a "_ Cross Tools" one with a matching target, I think.)

@rpjohnst had to use the x86_x64 Cross Tools Command Prompt for VS 2017, the other wouldn't work. But that works beautifully. Thank you.

Seems like the increasingly complex detection logic is going to need to be extracted into a crate to share with rustup.

Maybe we can just bundle vswhere now? The latest release apparently supports legacy versions (i.e. older than VS2017), so maybe we could just rely on vswhere only now for everything?

I figured out the COM API and have created a basic demonstration of getting the path to the linker.

Next step is creating a proper crate for this and pulling in all the other detection logic used by rustc and gcc.

Just posting this, becaus vcvars.bat didn't do it in my case. Maybe it helps somebody to save the hours I spent setting up rust with VS2017 tooling.

I had to manually add the path to link.exe to my PATH env-variable:
C:\Program Files (x86)\Microsoft Visual Studio2017\Community\VC\Tools\MSVC\14.10.25017\bin\HostX64\x64
I also had to create a new env-variable called LIB with the paths to the libraries:
C:\Program Files (x86)\Microsoft Visual Studio2017\Community\VC\Tools\MSVC\14.10.25017\lib\x64;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.14393.0\um\x64;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.14393.0\ucrt\x64

@nik-fox while that is a great work around for you, please note that starting with Visual Studio 2017, there is no longer a canonical location to where VS installs itself. In fact, to a large degree VS2017 is xcopy deploy-able (not quite, but very close); and is designed to support side-by-side installations.

I put this on the 1.17 milestone in hopes it can be resolved in time, since it's quite important. The patch would need to be in by 4/14 though.

We're probably going to just have to clarify the docs for 1.17.

We've missed the window for 1.17, but I'm leaving this tagged for 1.17 so we don't forget to mention it in relnotes.

Sounds like you are going to look it up via the COM API, so this news may not matter. 15.2 will have vswhere installed.

vswhere is now installed with Visual Studio 2017
https://blogs.msdn.microsoft.com/heaths/2017/04/21/vswhere-is-now-installed-with-visual-studio-2017/

Note that in https://github.com/alexcrichton/gcc-rs/issues/143 @retep998 is working on crate to probe for VS installations.

Here is a guide for finding installed Visual Studio 2017 instances and C++ tools: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/

I'm tagging this as a @rust-lang/tools issue -- let me know if anyone disagrees with that classification.

Installing Rust on Windows 10 64 worked for me executing vcvars64.bat in cmd. But to run rustc, I have to rerun vcvars64.bat every time. Is there a possibility to circumvent this?

@Shtuka That's what this issue is about. It should be fixed in a later version of Rust. For now, a slightly easier workaround than running vcvars64.bat is using the x64 Native Tools Command Prompt for VS 2017 shortcut that gets installed with VS2017, which runs vcvars64.bat on startup.

Hopefully this can get fixed in a 1.17.1 release instead of waiting for 1.18.

@rpjohnst regarding using the x64 Native Tools Command Prompt for VS 2017, what I have experienced here is that while inside that prompt, all rust stuff works but if I use it to install something like rustfmt, racer and the like, then they doesn't work from programs that were not started from that shell such as Visual Studio Code.

@soapdog You can get the Rust toolchain to work when it is being invoked by VS Code if you set the windows system environment variables mentioned by @nik-fox here. Only a stop gap of measure of course.

@soapdog I personally just start VS Code from within my prompt so it picks up the environment variables from it.

FYI everything works fine (for me, Windows 7) with standalone VS 2017 C++ Build Tools http://landinghub.visualstudio.com/visual-cpp-build-tools. Install and Rust + MSVC just works.

I had to use nix-fox's workaround to get the hello world to work :)

Seems pretty unfeasible that this will make it into 1.18 at this point.

@retep998 Did you have an opportunity to put together that crate?

Seems pretty unfeasible that this will make it into 1.18 at this point.

Would it be possible to add some workaround documentation somewhere at least? Running vcvarsall from the terminal first (and launching editors from there) works fine and isn't too much hassle really.

A simple .cmd file with the following should remedy most issues:

@echo off

set vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe

@echo %VSCMD_ARG_no_logo%

for /f "usebackq tokens=1* delims=: " %%i in (`"%vswhere%" -latest -requires Microsoft.Component.MSBuild -requires Microsoft.VisualStudio.Workload.NativeDesktop`) do (
  if /i "%%i"=="installationPath" set InstallDir=%%j
)

if exist "%InstallDir%\VC\Auxiliary\Build\vcvars64.bat" (
  call "%InstallDir%\VC\Auxiliary\Build\vcvars64.bat"
)

@freebroccolo @whoisj Running vcvarsXY is limited to cmd shells which is only one of (by now) many command lines that exist on Windows. Surely, doing that is an acceptable workaround, but ultimately, Rust needs the ability to find the correct linker itself (which is what this issue is about).

@poke, then the build scripts need to find and run the vswhere.exe executable, and use it's output to populate the build's environmental variables. The vcvars scripts do only that.

@freebroccolo @whoisj Running vcvarsXY is limited to cmd shells which is only one of (by now) many command lines that exist on Windows. Surely, doing that is an acceptable workaround, but ultimately, Rust needs the ability to find the correct linker itself (which is what this issue is about).

My point is that if this can't be fixed in the next release then there should at least be documentation on a workaround so that people don't have to waste a lot of time tracking down a solution that is already known. I agree it should be fixed properly somehow.

@whoisj Populating environment variables just for the sake of calling one specific executable from the PATH then is a bad way to solve this. The proper solution, which is being worked on, is to just call the linker at the correct location.

@freebroccolo Technically, according to the installation manuals, Rust does not appear to officially support Visual Studio 2017 yet, and always mentions to require VS 2015 or the 2015 C++ build tools.

But yeah, in general I agree that this should have been fixed a long time ago. I did open this issue quite a while in advance in order to get this done in time for the release of VS 2017ā€¦

I've started a patch to gcc-rs to fix this: https://github.com/brson/gcc-rs/tree/vs2017

Based on https://github.com/retep998/msvc-bunny

It gets rustup past the check in https://github.com/rust-lang-nursery/rustup.rs/issues/1003, and now I'm working on integrating it into rustc.

cc https://github.com/alexcrichton/gcc-rs/issues/143

It would be good if some one could summarize it would be really helpful for beginner encountering this error so that they don't have to read all the comments.

@Rafi993 Starting with Rust 1.19 this will work out of the box.

For previous versions, you need to adjust some environment variables in order to allow Rust to find and use the C linker. You can do that easiest by running Visual Studioā€™s vcvarsall.bat. You can also launch the Visual Studio developer command prompt which already does this for you.

Unfortunately, you cannot run vcvarsall.bat from PowerShell and have it impact the environment variables of the PowerShell. There are ways of copying over the environment variable changes from a .bat file, so you can do that.

However, since Rust only needs the linker, you can also just modify the two environment variables directly that you need. I had the following added to my PowerShell profile to make this work:

$env:PATH = 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.10.25017\bin\HostX64\x64;' + $env:PATH
$env:LIB = 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.10.25017\lib\x64;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\lib\um\x64;C:\Program Files (x86)\Windows Kits\10\lib\10.0.14393.0\ucrt\x64;C:\Program Files (x86)\Windows Kits\10\lib\10.0.14393.0\um\x64;'

Note that you might need to update the MSVC version in the path when Visual Studio is updated.

Was this page helpful?
0 / 5 - 0 ratings