Swift on Windows without Visual Studio?

Edit: This is not about Visual Studio Code with the very fine Swift extension, but about Visual Studio as prerequisite for compiling Swift programs.

First let me note that I do not regard the following as an “urgent” topic, but more a question of understanding and of what the future might bring.

Short question: Why is Visual Studio needed to develop with Swift on Windows, and might it be possible (and sensible) to dispense with it in the future?

Background: “Organisations” according to Microsoft are not allowed to use Visual Studio for free, and in such a case this makes the ”free” Swift language something where you actually have license costs (if you do not need Visual Studio anyway).

CC @compnerd Thanks.

1 Like

Are you confusing Visual Studio with Visual Studio Code? Visual Studio is not free for organisations while Visual Studio Code is

That said, no-one forces you to use any IDE with Swift. As uncomfortable as it may seem these days, but the majority of the software in existence has been written with just a terminal and perhaps an editor.

I don't think the question is about any IDEs but the fact that in order to get Swift up and running on Windows, you need to install some necessary components from Visual Studio. Which basically means you have to install Visual Studio 2019 or later.

Yes, not talking about any IDE for editing for which VS Code is fine, but about Visual Studio (the older sister or the bigger brother or what term you prefer) as prerequisite for compiling Swift programs on Windows. Sorry for any confusion.

2 Likes

Microsoft has long put out Windows SDKs, which used to suffice for alternate toolchains to integrate with. Maybe the Windows installer for Swift can be made to use that instead.

This is maybe my main gripe with Swift on Windows: the fact the installer isn't "all inclusive". Rather, it's expected the user takes care of installing the C/C++ dependencies etc themselves. Compared to Go and Rust, whose installers can take care of everything.

This is probably a considerable hurdle for people who'd like to check out Swift for the first time (either to just fiddle with or to actually learn it). For a language whose aim is "world domination" I think it's in its best interest to be as accessible as possible. :wink:

Not to take away the amazing progress that's been made on the Windows version of course!

3 Likes

I suppose that you can use Swift without Visual Studio but I fail to see the point in 99.9% of the cases. The only case I can come up with is uSwift.

Visual Studio provides exactly two pieces:

  1. WinSDK
  2. VCRUNTIME
  3. Visual C++ Redistributable

Yes, there are 3 parts of two. The last is listed only because it does provide it but is mostly for convenience at that point. You can download the redistributables from Microsoft independently and can be automated.

The Windows SDK is available standalone and can be installed instead if you like. That provides the necessary tools (rc, mt, and windbg specifically) and the headers and import libraries for the system libraries and ucrt.

So far so good, no need for Visual Studio. The problem is that the Windows SDK and ucrt have a dependency on VCRUNTIME. That can only be resolved by installing Visual Studio. If there’s an alternative that I’m not aware of please do share.

The reality is, you don’t need the full blown Visual Studio at all, the build tools are sufficient as all that is needed is WinSDK and VCRUNTIME. Oh, and git, and python, and the redistributable, at which point it’s just easier to use that.

Because the licensing terms do not permit the redistribution of the components. If you know of a way to get that, please do share. With winget you do not have control over the VS package, so that cannot be automated.

Oh if you’re looking for the go/rust equivalent there’s no need to install Visual Studio. You just get to live in the nostd/nocore world. You can bring your own Swift standard library (the windows one has system dependencies including ucrt/msvcprt). But then you are completely isolated from the system and that takes away IMO a wonderful aspect of Swift - the system integration.

3 Likes

Is it not the other way around? VCRuntime provides an interface to the C++ runtime and C++ programs that is built atop WinAPI and ucrt.

Installing the Visual C++ Redistributable package doesn't install VCRUNTIME?

Thank you for your answer.

It might be that this system integration is not broadly understood (and maybe deserves a blog entry)? This seems to add another viewpoint in addition to “just” porting the language and its standard libraries, namely also being able to use it as an alternative for pure Windows programming. Very interesting and I would like to hear more about this idea.

With the mentioned system integration I understand the kind of tradeoff here, and I think an acceptable alternative to the installation of VS should in no case be some “tricky” one.

VCRuntime is the Windows equivalent of crt0.o. It’s the thing that gets statically linked into your app that talks to the C++ runtime present on the system—either the ucrt (Universal C++ Runtime) which comes bundled with recent versions of Windows, or the VCRedist (an installer for which you typically bundle with your app; recent versions actually install the ucrt).

Correct. The redistributable is just the redistributable component - the DLL. The headers and import libraries are part of Visual Studio.

Nope, it is the other way around. AppCRT/DesktopCRT (aka UCRT) uses parts of the Visual C++ library. The C++ runtime (msvcprt) sits on top of the VCRUNTIME (it is akin to libc++abi and parts of libc) and UCRT. The Windows API sits on top of that.

Yes, I touched upon this point in my talk about porting Swift to Windows and how it was a much larger endeavor than just getting the compiler working. Swift/Win32, DXSample, Swift/COM, SwiftWinRT are examples of pure Windows programming - they are integrating with the underlying system, not creating a facsimile of the Windows environment.

It isn't particularly tricky per se. I went to great lengths to ensure that this was possible. All you need to do is provide an alternative SDK. You could use uSwift to create one if you like or perhaps port the official Swift runtime/standard library to not use the Windows APIs for things such as thread local storage, process startup, argument handling, C/C++ library functions, etc.

4 Likes

I am thoroughly confused as to how this can be possible. ucrt ships via Windows Update. Apps that are written in C++ and target recent versions of Windows can ship without a VCRedist. They’ll link the ucrt at launch, and the statically linked VCRuntime in the app will resolve imports found in ucrt. Are you saying that ucrt also imports symbols provided by ucrt? I would characterize that as an ABI rather than a dependency. A more typical (IMO) ABI would only have VCRedist import from ucrt, and use runtime callback registration to implement the other direction.

The redistributable isn't the issue, the headers and import libraries are. msvcprt headers, SAL, atomics, vcruntime.h, etc are all provided through the MSVC ToolSet. This also includes the low level C headers (e.g. stdint.h). The import libraries vcruntime.lib, msvcrt.lib, etc are part of the "vcruntime", which is part of the toolset.

vcruntime does not call into ucrt, ucrt calls into vcruntime. You don't statically link in vcruntime and dynamically link in ucrt. I'm not sure I see how this is ABI and not a dependency.

I need to crack open my copy of Visual Studio again, but I had to look into Windows app startup recently and this is how I recall it working. I recall that VCRedist contains the actual entry point that calls WinMain. Maybe in misremembering which component that is?

Maybe I'm misremembering, but Upgrade your code to the Universal CRT | Microsoft Learn seems to agree with me in that vcruntime sits below ucrt and is part of the ToolSet.

For other readers of this topic: this is Saleem‘s talk. Should be worth watching.

I would not like to create or use an alternative SDK. The way it is currently implemented certainly makes a lot of sense, and as I said, having to install Visual Studio seems like a good “tradeoff” in the light of the direct system integration. If there is a “clean” way without it, OK, but else I personally am completely happy with it now that I understand more. Thanks for the explanations.

Yeah, we’re talking about the same thing. I’m reasonably certain the way it works is:

  • When you install Visual Studio, the libraries (libvcruntime.lib, vcruntime.lib, etc.) get installed in Program Files\Microsoft Visual Studio.
  • LINK.exe implicitly links the appropriate VCRuntime.
    • If you’re building an executable, this is libvcruntime.lib (or libvcruntimed.lib if you are doing a debug build). This static library contains the real entry point that the kernel jumps to after starting your process. (The library actually contains multiple entry points, and the one chosen is based on other compiler options.) This hidden entry point in turn calls the developer-authored WinMain.
    • If you’re building a DLL, the /MD, /MDd, /LD, and LDd options to CL.exe cause it to direct the linker to statically link the appropriate version of vcruntime.lib. This library contains the real, hidden DLL entry point that in turn calls the developer-authored DllMain.

The role of the hidden entry points is to call into WinAPI and whatever C++ runtime is present on the system to register exit handlers, allocate heaps, etc. In this way, the program depends on VCRuntime to communicate with the C++ runtime (ucrt or VCRedist). Parts of the C++ runtime, in turn, call into WinAPI.

A Swift toolchain that didn’t have a Visual Studio dependency would link libc++ or libstd++ instead of the ucrt or VCRedist, and also link the appropriate analogue to VCRuntime (e.g. crt0.o for GNU toolchains, IIRC).

I think that the point that we might disagree on is the cleanliness of the approach. In my mind, a separate SDK is pretty clean, but I do admit not exactly trivial. Glad that the explanation was helpful.

I think that there is some confusion here.

Mostly correct, until recently, it would be %ProgramFiles(x86)%, there is now a 64-bit Visual Studio, however the build tools are still 32-bit only, except for the toolset, which is 64-bit. If this is clear as mud, then you are on the right path. (The install location was driven by the shell, which was 32-bit, but the toolset is 32/64 bit, and the latter is required to deal with memory limitations, the build tools are always a "32-bit shell" for some reason).

This is actually not entirely true. The linker is told what to do by the invocation by MSBuild (or in our case clang or clang++). Unless you are talking about something like an embedded linker directive, in which case, that is pretty much something embedded by the user.

No, this is not. This is dependent on /MT. The default (/MD) would use vcruntime.lib rather than libvcruntime.lib. You will dynamically link in the vcruntime.

The entry point is not from vcruntime.lib but rather from:

  • /MTd: libmsvcrtd.lib
  • /MT: libmsvcrt.lib
  • /MDd: msvcrtd.lib
  • /MD: msvcrt.lib

The actual CRT entry point is determined by the definition of the entry point:

  • wWinMain (-D_UNICODE, /SUBSYSTEM:GUI) => wWinMainCRTStartup
  • WinMain (-U_UNICODE, /SUBSYSTEM:GUI) => WinMainCRTStartup
  • wmain (-D_UNICODE, /SUBSYSTEM:CONSOLE) => mainCRTStartup
  • main (-U_UNICODE, /SUBSYSTEM:CONSOLE) => wmainCRTStartup

The command line flags take precedence, but if missing the spelling will select the CRT Startup path.

The appropriate version of msvcrt.lib and vcruntime.lib. The startup paths are (lib)?msvcrtd?.lib. (lib)?vcruntimed?.lib contains the compiler support functions (aka compiler-rt). This primarily will provide static copies of the atomic operations and IAT thunks for any dynamically linked content (or their static counterpart).

This is not so. The role of the "hidden entry point" (which is not a hidden entry point at all), is to setup the C runtime. On Unix, this is spelt crt0.o, crti.o. On Windows, this is spelt msvcrt.lib. There is nothing magical going on here. There is simply the C startup routines that must be invoked (e.g. walking the init table for C++ global constructors).

I think that you had mixed up msvcrt and vcruntime. VCRuntime is significantly lower in the stack than msvcrt.

No, ucrt is the equivalent to msvcrt, libc++ or libstdc++ are the equivalents to msvcprt. The UCRT is the top half msvcrt after "The Great C Runtime Refactoring", the bottom half remains embedded in VS.

3 Likes