www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [Proposal] Switch to the Universal CRT on Windows.

reply Adam Wilson <flyboynw gmail.com> writes:
I recently got hit by a rather esoteric bug that looked like a 
real bug, but was actually not a bug, but also kind of a bug. 
Specifically, on Windows the printf `%zd` format would fail with 
a Segfault. The simple fix was to delete `%zd` and use `%d` with 
a cast to a fixed size int type. But it was the wrong fix.

The actual root cause is that I was running `build.d` and 
`build.d` decided to use the 13 year old MSVCR120.dll, which does 
not support `%zd` printf formats. Despite the fact that I have 
the most modern C++ build tools and SDK's that you can get 
installed on my machine. (I work on Windows for a living, to my 
enduring shame)

The result is that we ship a fallback CRT that doesn't even 
support building the compiler itself. Egg, meet face.

The solution is pretty straight-forward. Use the Universal CRT 
for everything on Windows.

And it turns out that there is another reason to abandon 
everything but UCRT. MINGW64 is now deprecated in favor of UCRT. 
(See blog post 
[here](https://www.msys2.org/news/#2026-03-15-deprecating-the-m
ngw64-environment)) Therefore, we will be required to make this change at some
point in the near future because MinGW is forcing us too. Moving to UCRT will
also allow us to deprecate and eventually remove the giant bag of hacks that is
our MINGW64 support.

Anybody not using Windows 10 or greater will need to install the 
UCRT as a minimum from 
[here](https://support.microsoft.com/en-us/topic/update-for-universal-c-runtime-in-windows-322bf30f-4735-bb94-3949-49f5c49f4732).
However, I think this is acceptable because our policy is that we 
only officially support the versions of Windows that Microsoft 
does for ESU, which means Win10 22H2 (until October 2028) or 
Win11. And since UCRT has been back-ported to older systems by 
Microsoft itself, it's an easy problem to handle for people 
deploying D-code.

Note for Walter: You will need at least the 2015 Build Tools on 
Win7, which you can get 
[here](https://www.microsoft.com/en-us/download/details.aspx?id=48159). Or if
you want something a little newer, the VS2017 build tools are
[here](https://aka.ms/vs/15/release/vs_buildtools.exe).

I have posted a PR 
[here](https://github.com/dlang/dmd/pull/23345). This PR 
deprecates (but still allows) the msvcr120 fallback path as well 
as updating various detection routines to include the latest 
versions of VS and deprecate anything prior to VS2015. If no 
WinSDK or VS installation is detected then the compiler throws an 
error.
Jun 30
next sibling parent Adam Wilson <flyboynw gmail.com> writes:
On Wednesday, 1 July 2026 at 01:24:05 UTC, Adam Wilson wrote:
 I have posted a PR 
 [here](https://github.com/dlang/dmd/pull/23345). This PR 
 deprecates (but still allows) the msvcr120 fallback path as 
 well as updating various detection routines to include the 
 latest versions of VS and deprecate anything prior to VS2015. 
 If no WinSDK or VS installation is detected then the compiler 
 throws an error.
It was noted to me by ADR on Discord that we could keep the ability to build without the WinSDK/VS if we included the appropriate UCRT library files from MinGW and updated `lld-link.exe`. We might want to consider including more than just the UCRT library files from MinGW so we can support building projects that need more than the UCRT without requiring the WinSDK. Although, I would note that this is a bigger shift into a policy of shipping our own complete development environment, with the attendant overhead of making sure that environment is up-to-date.
Jun 30
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 1 July 2026 at 01:24:05 UTC, Adam Wilson wrote:
 The solution is pretty straight-forward. Use the Universal CRT 
 for everything on Windows.
I'm in favor, but can I say that this shows how much of a nuisance depending on libc is? Walter often says how printf is the most debugged function ever so we should leverage that in dmd, but here we are getting bitten by libc nonsense yet again. Between that, its [unsafe interface](https://github.com/dlang/dmd/pull/13987), stupid [global locale system](https://github.com/dlang/dmd/blob/cfae207a4dfa38bdc67b28986e263365adc577c3/compiler/src/dmd/r ot/port.d#L93-L104) and [random breakages](https://bugzilla-archive.dlang.org/bugs/23846/), are we really better off than if we just wrote our own D function to interpolate numbers in strings? In my own code I avoid libc as much as I can, using `CreateFileW` instead of `fopen` for example. Only a few essentials like memcpy and memset remain since LDC sometimes emits calls to that even when you don't write them yourself. At least those are simple and deterministic: I haven't found a libc yet that implements them visibly different than the rest.
Jul 01
next sibling parent Denis Feklushkin <feklushkin.denis gmail.com> writes:
On Wednesday, 1 July 2026 at 12:37:22 UTC, Dennis wrote:
 On Wednesday, 1 July 2026 at 01:24:05 UTC, Adam Wilson wrote:
 The solution is pretty straight-forward. Use the Universal CRT 
 for everything on Windows.
I'm in favor, but can I say that this shows how much of a nuisance depending on libc is? Walter often says how printf is the most debugged function ever so we should leverage that in dmd, but here we are getting bitten by libc nonsense yet again. Between that, its [unsafe interface](https://github.com/dlang/dmd/pull/13987), stupid [global locale system](https://github.com/dlang/dmd/blob/cfae207a4dfa38bdc67b28986e263365adc577c3/compiler/src/dmd/r ot/port.d#L93-L104) and [random breakages](https://bugzilla-archive.dlang.org/bugs/23846/), are we really better off than if we just wrote our own D function to interpolate numbers in strings?
I also agree with this, for the reason that libc slows down porting to systems where it is not present. Maybe we need to get together somewhere and discuss it? My proposal was outlined [here](https://forum.dlang.org/post/mssdrzylsdqmpcnvakar forum.dlang.org): "druntime should not import core.stdc" Let the druntime use these functions through wrapper functions or aliases. There will be only about 10 of them in total, I think: malloc/free, some sync methods...
Jul 01
prev sibling next sibling parent reply Guillaume Piolat <first.name gmail.com> writes:
On Wednesday, 1 July 2026 at 12:37:22 UTC, Dennis wrote:
 In my own code I avoid libc as much as I can, using 
 `CreateFileW` instead of `fopen` for example. Only a few 
 essentials like memcpy and memset remain since LDC sometimes 
 emits calls to that even when you don't write them yourself. At 
 least those are simple and deterministic: I haven't found a 
 libc yet that implements them visibly different than the rest.
I'm not sure if it's easy to implement and maintain fopen for many systems. At least the libc is maintained by others. Other small challenges involves: emitting and parsing floating-point correctly. And the transcendental functions, that are not especially better in Phobos than in libc.
Jul 02
parent Denis Feklushkin <feklushkin.denis gmail.com> writes:
On Thursday, 2 July 2026 at 16:32:06 UTC, Guillaume Piolat wrote:

 I'm not sure if it's easy to implement and maintain fopen for 
 many systems. At least the libc is maintained by others.
It can be "implemented" by bypassing alias to libc's `fopen`
 Other small challenges involves: emitting and parsing 
 floating-point correctly.
Seems this isn't used in the druntime.
 And the transcendental functions, that are not especially 
 better in Phobos than in libc.
And the Phobos problem is a Phobos problem, essentially unrelated to the compiler and druntime. It will even be possible to disable some of its parts for which there will be no implementations.
Jul 03
prev sibling next sibling parent reply Adam Wilson <flyboynw gmail.com> writes:
On Wednesday, 1 July 2026 at 12:37:22 UTC, Dennis wrote:
 In my own code I avoid libc as much as I can, using 
 `CreateFileW` instead of `fopen` for example. Only a few 
 essentials like memcpy and memset remain since LDC sometimes 
 emits calls to that even when you don't write them yourself. At 
 least those are simple and deterministic: I haven't found a 
 libc yet that implements them visibly different than the rest.
There is a technical argument for leaving `libc` behind as well. Specifically if you want to do anything with async operations you're going to have to use the API's specific to that platform. For example: Linux has `io_uring` where Windows has I/O Completion Ports. `libc` stops working as a universal system interface when you need to do async work, which means that anywhere we might want to support an async interface, we'll need to use something other than `libc`. Another example is terminal colors. Windows has `SetConsoleTextAttribute()` where `libc` is the only choice on Linux. Which means we really should be abstracting away `libc`. IMO, BaseD is going to become that abstraction.
Jul 02
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 03/07/2026 5:14 PM, Adam Wilson wrote:
 On Wednesday, 1 July 2026 at 12:37:22 UTC, Dennis wrote:
 In my own code I avoid libc as much as I can, using `CreateFileW` 
 instead of `fopen` for example. Only a few essentials like memcpy and 
 memset remain since LDC sometimes emits calls to that even when you 
 don't write them yourself. At least those are simple and 
 deterministic: I haven't found a libc yet that implements them visibly 
 different than the rest.
There is a technical argument for leaving `libc` behind as well. Specifically if you want to do anything with async operations you're going to have to use the API's specific to that platform. For example: Linux has `io_uring` where Windows has I/O Completion Ports. `libc` stops working as a universal system interface when you need to do async work, which means that anywhere we might want to support an async interface, we'll need to use something other than `libc`. Another example is terminal colors. Windows has `SetConsoleTextAttribute()` where `libc` is the only choice on Linux. Which means we really should be abstracting away `libc`. IMO, BaseD is going to become that abstraction.
This is sounding far too platform specific, and will make it so it cannot be the base most library for D. This is what druntime is for.
Jul 02
parent reply Adam Wilson <flyboynw gmail.com> writes:
On Friday, 3 July 2026 at 05:18:38 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 03/07/2026 5:14 PM, Adam Wilson wrote:
 IMO, BaseD is going to become that abstraction.
This is sounding far too platform specific, and will make it so it cannot be the base most library for D. This is what druntime is for.
And how do you avoid making it platform specific? The reason I ask is that DRT does not yet exist in BaseD code. DRT would be after it in the build chain (BaseD->DRT->Phobos). So everything you write is going to be platform specific.
Jul 02
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 03/07/2026 5:57 PM, Adam Wilson wrote:
 On Friday, 3 July 2026 at 05:18:38 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 On 03/07/2026 5:14 PM, Adam Wilson wrote:
 IMO, BaseD is going to become that abstraction.
This is sounding far too platform specific, and will make it so it cannot be the base most library for D. This is what druntime is for.
And how do you avoid making it platform specific? The reason I ask is that DRT does not yet exist in BaseD code. DRT would be after it in the build chain (BaseD->DRT->Phobos). So everything you write is going to be platform specific.
Simple, you won't be doing these things in it. They belong much further up the dependency graph. Based can only really do simple stuff like bit manipulation, memory copying byte by byte. Trying to expand the scope of it beyond this is just going to lead to massive problems both with binary sizes, as in 64k limit and make porting extremely difficult to even get off the ground. The scope is basically the same as compiler-rt builtins.
Jul 02
parent reply Adam Wilson <flyboynw gmail.com> writes:
On Friday, 3 July 2026 at 06:08:39 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 The scope is basically the same as compiler-rt builtins.
That is nothing like what I proposed last year or what we have discussed since then. The idea for BaseD has always been a trimmed-down no-gc standard library for projects that need it. That means basic terminal I/O, data structures, etc. If your goal for BaseD is limited "things the compiler needs to link successfully" then we have fundamentally different views on the topic.
Jul 04
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 05/07/2026 4:50 PM, Adam Wilson wrote:
 On Friday, 3 July 2026 at 06:08:39 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 The scope is basically the same as compiler-rt builtins.
That is nothing like what I proposed last year or what we have discussed since then. The idea for BaseD has always been a trimmed-down no-gc standard library for projects that need it. That means basic terminal I/O, data structures, etc. If your goal for BaseD is limited "things the compiler needs to link successfully" then we have fundamentally different views on the topic.
Indeed. The biggest difference is I know for a fact that what you want as-is is going to be a nightmare to fix. It will appear to work, right up until we can't do a release anymore.
Jul 04
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
https://github.com/dlang/dmd/pull/23351
Jul 03
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/1/2026 5:37 AM, Dennis wrote:
 its [unsafe interface](https://github.com/dlang/dmd/pull/13987)
This should be picked up by chkformat.d ?
Jul 03
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/1/2026 5:37 AM, Dennis wrote:
 In my own code I avoid libc as much as I can, using `CreateFileW` instead of 
 `fopen` for example.
I agree. fopen() is not used in dmd.
Jul 03
prev sibling parent Lance Bachmeier <no spam.net> writes:
On Wednesday, 1 July 2026 at 01:24:05 UTC, Adam Wilson wrote:

 The solution is pretty straight-forward. Use the Universal CRT 
 for everything on Windows.
It's 2026 so I agree. I don't do much with Windows beyond my work with R (much of it being interoperability with D), but they changed to UCRT years ago, and it made everything easier. Windows was a nightmare for R before the change.
Jul 01