Updated based on discussion on Hacker News
Developing on Windows can be frustrating; while some development environments are Windows-native or fully cross-platform, a lot of tools were originally made for Linux-like or macOS environment. Surprisingly this even includes very popular ones like Git. How do we work around this? Well, there are many solutions, though sadly none are perfect.
dev system | ABI | paths | Linux compat | access to Win. files | file access from Win. | GUI | GPU acceleration |
native | MSVC | Windows | none | direct | direct | native | native |
MinGW-w64 | MinGW-w64 | Windows | tiny | direct | direct | native | native |
Cygwin | Cygwin | Windows or Linux (virtual) | moderate | via mounts | direct | native/X11 | native/none? |
WSL1 | Linux | Linux (virtual) | good | via mounts | via share | X11 | none? |
WSL2 | Linux | Linux | full VM | via mounts (slow) | via share (slow) | full Linux support | OpenGL, OpenCL |
Use native Windows
If you can develop with native tools, that will be the simplest. Lots of programming languages fully support Windows.
To get development tools, programming languages, etc. like Git, Go, Node.js, fzf, you can use Scoop, a Windows command-line package installer. Scoop provides official builds where available.
The case where you’ll have the most difference to Linux is with C and C++. The official, 100% Microsoft way of building C software is to use the Windows SDK, along with Visual Studio and associated tools. The official C and C++ compiler is known colloquially as MSVC (Microsoft Visual C/C++). You can install these components with the Visual Studio installer (omitting the actual IDE if you don’t want it).
Unfortunately, you may still run into issues using Windows:
Windows doesn’t easily support symlinks, since by default it requires admin privileges to create them. NTFS hard links and junction points are typically used instead. See NTFS links - Wikipedia, and the mklink
cmd command.
Windows conventionally uses CR LF as line endings, whereas Linux and macOS use LF. This can cause issues when sharing source files between OSes. By default, Git for Windows converts all CR LF to LF when committing to the repo internals, and converts back to CR LF when writing files to the filesystem. To avoid unexpected issues from this conversion, you may wish to just set your editor to use LF and turn off this feature of Git with
git config --global core.autocrlf false
See What do the crlf settings mean? - Stack Overflow and line endings - Why should I use core.autocrlf=true in Git? - Stack Overflow.
Sometimes Windows ports of Linux utilities don’t work as expected. For example, I installed cwrsync, a port of rsync, via Scoop, and it didn’t work at all. I found out that it was picking up Windows’ built-in install of ssh.exe
which wasn’t compatible with it, and that cwrsync had shipped its own version of ssh.exe
which was expected to be in the PATH. I didn’t find any instructions informing users of this.
Use a VM / WSL2
The cleanest way of getting a Linux-like environment is to just use Linux on Windows, via a full VM, or through WSL2, which is a lightweight, well-integrated VM.
Pros
- Fully-compatible Linux experience with GUI support
Cons
- Minimal hardware access e.g. GPU, USB: While VM hosts can have some support for these (e.g. WSL2 has OpenGL and OpenCL acceleration) you won’t get the full experience.
- Slower file access between VM and host. (You ideally want to be working only on files in the VM, especially avoiding major I/O work over the host bridge like long builds, or using Git in large repositories, or having large
node_modules
, etc.) - File path issues: If a program uses Linux paths, it may be difficult to integrate with Windows tools, as they won’t be able to see each others’ paths. Such is the case when using Windows VSCode and WSL Git. You may need to wrap the tools and convert paths to allow them to integrate; see andy-5/wslgit
(By the way: WSL2 exposes files to Windows via network shares, but these are inaccessible to some Windows programs. You have to mount a network drive (e.g. via net use
command) to give it a drive letter for direct path access.)
There is also WSL1, which isn’t actually a VM; it’s a virtual environment which parses Linux executables and translates Linux OS API calls to Windows. The main advantage to WSL1 over 2 is that it access Windows files directly, making it faster for some file I/O than WSL2. Personally, I use WSL2 as I haven’t found that I’ve needed this.
Use Windows with a choice of compatibility tools
Another option, particularly where a native version is not available, is to use Cygwin. Cygwin is based around a modified port of Linux-based C compiler GCC which can compile code written for Linux; it understands lots of Linux APIs and compiles them to Windows equivalents. It does this with the help of a runtime library cygwin1.dll
that is linked into the executable or library, which provides it a Linux-like virtual environment including a virtual filesystem. Cygwin also ships with a shell and package manager for lots of Linux software built with Cygwin, giving you a Linux feel on native Windows. It’s not fully compatible though, as there are many mismatches between Windows and Linux OS APIs.
In particular, Cygwin GCC produces builds with its own ABI, which makes it incompatible with pre-built libraries built with other compilers. This is particularly important for programming languages. For example, the official Windows Python builds use the Windows MSVC ABI, so the package ecosystem is built around that; if you use Python from Cygwin, you’ll be unable to install compiled packages with pip and pip will have to build them from source. Some such pip packages might not build at all with Cygwin. (Of course, pure Python ones will work.)
Also, the same point with VMs about path incompatibility can apply to Cygwin programs.
So on Windows, I prefer native builds from official sources or Scoop, falling back to Cygwin for any unsupported utilities.
C and C++
If you want to develop C or C++ in a Linux-like way, i.e. not using Visual Studio tools, you could use Cygwin’s GCC and develop as if you were on Linux. However, you’ll get a Cygwin build at the end, which
- has the Cygwin ABI
- has the Cygwin DLL as a dependency
- may have other compatibility quirks versus native software
Instead, you can get the convenience of both with MinGW-w64. MinGW-w64, like Cygwin, is a port of Linux-based compiler C GCC to Windows, but it compiles native Windows code to native Windows executables. This means you can use Linux-style build scripts to build normal Windows C and C++ programs. (It’s a fork of an earlier project called MinGW.) The only disadvantage is it again has a different ABI to MSVC and Cygwin.
The sweet spot of compatibility for C, then, is:
- use build utilities like Bash, Make, coreutils, etc. from Cygwin for build system compatibility
- use MinGW-w64 for the actual compilation
This is precisely the goal of MSYS2, which nicely integrates them together into a unified Linux-like environment with a package manager. With MSYS2 you can use Cygwin or MinGW-w64 compilers as you choose, along with shell tools and build scripts written for Linux. I use MSYS2 to build native Windows versions of Brogue with barely any build system changes.
Using MSYS2 instead of Cygwin
MSYS2 is designed with a smaller usage scope than Cygwin and has fewer packages, but I find it’s perfectly good enough for when you want to do a bit of Bash scripting or use some common Linux tools. Hence, I don’t use the official Cygwin install at all.
By default, the MSYS2 environments don’t inherit the Windows PATH, preventing accessing native tools from Scoop, etc. I’ve enabled inheritance in the core MSYS environment by uncommenting
#MSYS2_PATH_TYPE=inherit
in C:\msys64\msys2.ini
.
Other notes
-
You can use Linux GUI software with Cygwin or WSL1, even though they aren’t VMs, by running a native X11 server on Windows. You won’t get any graphics acceleration, though.
-
Personally, I’ve primarily used WSL2 for my personal software dev; but now that I understand the native Windows landscape better I’m considering moving in that direction, mainly for better integration.