(See Updates section at end for latest notes.)
In the middle of the journey of our life I came to myself within a
dark wood where I was no longer comfortable identifying myself as a
Mac user. Superficially, the relationship with my MacBook air had
been rewarding. It lasted all day, ran the necessary few bits of
commercial software I needed, and otherwise did a great job of
pretending to be running unix. Also, I felt a pleasant sense of
well-moderated individuality when my fellow developers and I exchanged
knowing glances at our discreetly weathered, FP-inflected decal
But there is an equivalent, in the realm of our relationships with machinery,
to the discovery that someone we know has seen Cats more than a dozen times or
regards Robert Ludlum as a serious author. For me, that came with the breathless
speculation about an upcoming Mac "refresh," said to feature
an "e-Paper function key row" and better multimedia Siri integration (or something).
This sort of vulgarity did not sit well with my professed enthusiasm for middle-period
Mahler and/or the Oxford comma.
Also, I couldn't follow along with
cool posts on strace.
If I had the courage of my convictions, I would just run linux and be
done with it, but hewing that way without revealing myself as a
pretentious idiot would be difficult. To use linux without embarrassment,
one actually needs to know what one is doing, and I do not.
Armed with this self-knowledge, I hatched the idea of selling the Mac and buying
a newish Windows 10 laptop. I would be a contrarian! Not only that, but, since
I don't hang around with anyone who knows anything about developing on Windows,
I could easily spin my accomplishments as impressive.
Getting anything done on Windows is a bloody fucking nightmare. They made a big deal
recently about WSL, the "Windows
Subsystem for Linux". It's also known as "bash for windows", which is a grotesquely
stupid name that belies the ambitious goal of executing undiluted linux elf64
binaries by fully emulating the linux kernel interface. Unfortunately,
it doesn't really work,
a fact which is both illustrated and exemplified by
lists of programs you can run.
Yeah, it does a decent job of letting you pipe about text, but, while you
might evaluate "programs you can run" with the 80:20 rule, the decency of an
API is better appraised with the 100:0 rule. Installing a mountain of dependencies
and than discovering that
the JVM hangs
is the sort of experience that not even masochists enjoy.
If you are indeed mostly interested in a Potemkin village of apparently functional
unix command-line utilities, there are less ambitious projects like
cygwin and MinGW, the latter of which
Unlike WSL, these do not attempt binary compatibility with actual linux, but require
you to compile especially for the framework. The process for doing so is pretty
well baked by now, so the likelihood that someone else has already run into and
solved the dev-related issue you have is quite high. Generally speaking, google
is your friend.
Sometimes, though, it's a friend in the misery-loves-company sense.
It was, for example, pretty shocking to discover that python
setuptools has been broken under MinGW
for half a year, and nobody really cares.
To install pelican (without which you would not be
reading this deathless prose), it is actually necessary to patch two different
setuptools source. Moreover, aborted setups will have left your installation in
an inconsistent state that can only be repaired by manually removing files.
(Doubly exasperatingly, another thing you can't install without patching is
virtualenv, so there is truly no way to avoid a day of awfulness.)
Similar problems crop up with other, supposedly OS-independent languages and
tools. For example scalameta examples don't run.
These issues reflect the broad preference of the development community for
working in linux-like environments. Windows problems get ironed out when its time to
ship to end-users... but who cares about end users?
If WSL, MinGW and Cygwin are a little half assed, perhaps virtualization can
provide the full buttock. The remainder of this post is a recipe for getting
linux to run well and continuously enough under Windows 10's built-in "Hyper-V"
that you truly have the best of both worlds. It's a little complicated.
Read about requirements first.
In summary, you can't be running the cheaper "Home" variety of Windows, and systeminfo.exe
must report that your hardware supports virtualization.
If you pass those hurdles, may need to make sure that virtualization is enabled
in your BIOS. Then you can enable it in Windows from the "Turn Windows features
on or off" screen, and reboot.
Setting up a network environment
You will want your linux installation to be able to communicate both with the
outside world and with the windows host. What seems to be the easiest way to
accomplish this is to create two "virtual switches", which will show up as two
interfaces in linux.
Run the "Hyper-V Manager" and from the right-hand pane launch the "Virtual Switch
Manager." Create two switches.
Call the first "External Virtual Switch" and select an "External network", connected
to your host's main network adapter.
Call the second "Internal Virtual Switch" and select "Internal network" (not
"Private network"; that's a network that not even the host can see).
It will turn out that this isn't quite enough, but we'll repair it later.
Create the virtual machine
Again from the right-hand Hyper-V pane, select "New Virtual Machine" and follow
the wizard. Things to be careful about:
You want a "generation 2" VM. Otherwise it's only going to know about quaint
old things like floppies and CD-roms. This setting cannot be changed.
You should disable secure boot, which apparently causes some versions of linux
not to boot.
You do not want dynamic memory, as appealing as that sounds, because there are
memory corruption problems, supposedly related to linux issues. You will be able
to adjust the amount of memory later, but if you're serious about living in
both worlds simultaneously, you'll set it to half the available RAM.
Create a new virtual disk. Have the courage to make it large.
On the networking page, pick the External switch.
Under "Installation options', you might as well enter the location of your
Now, from the lower right pane, start the VM and connect to it (I think one of the
reasons Hyper-V is less resource intensive is that it doesn't bother emulating the
display unless you're connected). Go through the full installation process, with
as many reboots as it requires.
Fix the networking
At this point, you have a bridged networking environment. Both the VM and your host
have DHCP leases from the network you're on. To connect from the host to the guest
and vice-versa, you'll need to know the IP addresses you've been given, which will
be a bit awkward (and it will be impossible to maintain a connection when you're away
Shut down the linux guest, and select its "Settings". Choose to "add hardware", and
add the internal network switch. You'll also find an "automatic start" option, and
it will be best to disable this entirely.
Now, in the Windows control panel, find the
"Network Connections" page. You'll see a bewildering variety of little networky
icons, two of which will have the names you gave to your virtual switches.
Select "Properties" for the internal switch, highlight the "Internet Protocol Version 4"
line, and select "Properties" again. Set a static IP address with a nice ring to it,
preferably not one starting 192.168.1, as that will someday soon clash with a
dynamic IP address you're handed on another network. The mask should be 255.255.255.0,
and you can leave gateway blank.
Boot the guest; ifconfig should now show you two eth interfaces. Of those, one
will have an IP address and the other won't. Whichever one doesn't have an address is
the internal switch; note its hardware address.
Depending on your distro, the next steps will be different.
On Ubuntu, you'll right-click on the network icon on the menu bar and "Edit
connections." There will be two connections, with unhelpful names. Open them in turn
to find the one with the hardware device ID of the internal switch. Then, on that
connection's IPv4 tab, manually set an address on the same subnet as you chose
on windows (and the same subnet mask).
Verify now that you can ping the linux guest from the windows host. You might also
install openssh-server and verify that you can ssh to the linux guest. Most likely,
you will not be able to ping the windows guest from linux, however. This is due
to the stupidity of the windows firewall.
Fix the firewall
Windows supports the notion of a network "profile", which can be either "private" or
"public". When you connect to a network for the first time, you're given the option
of allowing network discovery, and if you say yes, you'll have designated the network
as private. Otherwise, it's public.
The Windows firewall can be turned on and off separately for public and private
networks. You certainly don't want to turn it off for public networks, but turning
it off for private networks is an acceptable compromise. That can be done from the control
panel in a more or less obvious way.
You will find, with some annoyance, that Windows had decided to designate your
internal switch as a public network. Changing this is incredibly difficult and will
require the Windows abomination known as PowerShell. From the start menu (or whatever
they call it these days), find PowerShell, right-click it and launch as administrator.
Within PowerShell, type Get-NetConnectionProfile. You should see your external
and internal switches; at least the internal one will show "NetworkCategory: Public".
You can change this state of affairs with something like
(using the exact InterfaceAlias listed by the previous command).
This is nice, but you'll find that it gets reset every time you boot. We thus want
to fix it every time we boot. Find a nice directory and create SetInternalPrivate.ps1,
containing this nonsense (adjusting the first line as appropriate):
The last line makes sure the firewall is off for private networks - another setting
that seems to get undone by Windows on your behalf with startling frequency.
If you run all of this in your administrator PowerShell window, you should now be
able to ping the host from the guest.
In the same directory, create a SetInternalPrivate.bat containing
Now find Task Scheduler in the start menu, right-click and run as administrator.
There's a little folder structure on the left; create a new folder for yourself.
In that folder, Create a Basic Task, triggered "When I log on", to "Start a Program" and
choose your .bat file. Immediately select the new task's properties.
There are a few possible ways forward now, but the most robust seems to be the following.
Under security options, run it as yourself and "Run only when the user is logged on", but
check "Run with highest privileges." On the Conditions tab, you want to uncheck
"Start the task only if the computer is on AC power" (you can imagine how much fun I
had discovering that).
Now reboot the windows host. If all has gone well, when you log in,
you'll see a black command window pop up to run the script, and you should then
be able to verify that the internal switch is private and that the firewall is
off for private networks.
From the Hyper-V console, start the VM. The reason we disabled auto-startup is that
we wanted to make sure it got started after the network was fixed.
Sharing is caring
On Windows, make sure that your home directory, or some other directory is
shared, and shared only with you. (I.e. not literally "shared", but "made available
in some sense that we don't wish to describe using the English language".)
On linux, we invoke the ritual incantations to mount this shared drive. The
first horror is that we'll have to store our windows password in plain-text.
Create something like /home/you/.credentials-cifs containing
and chmod it to 0600. (Hopefully, you chose to encrypt your
windows disk with bitlocker, because otherwise, yes, your password is
potentially visible to anyone with physical access to your computer.)
Now (sudo'd) edit /etc/fstab and add
Barring typos, you should now be able to sudo mount -a and see your windows
home directory under /media/win.
Once the VM is launched, I find that everything else just works.
I usually don't bother to
"Connect" to the VM at all, but just keep a putty window open, with tmux running on
the guest. Because of the internal switch, I can remain connected across
suspensions and multiple wifi networks. The one main change to my development
workflow is that I run emacs in text mode (under tmux), as I haven't found
a glitch-free combination of emacs builds and windows X-servers, but I've found
I actually prefer it this way.
Sun Oct 30 14:42:27 EDT 2016
It is suggested that one not select "secure boot" when setting up the VM. Apparently, some versions of
linux will not boot. I obeyed and didn't investigate.
There is a trove of perplexing information on Ubuntu under Hyper-V in
this technet article
After reading it, I switched to the supposedly lighter weight virtual kernel:
Despite this, uname still reports 4.4.0-45-generic, but nothing seems to be broken.
I've recently been setting X11 forwarding in putty and running the VcXrv
X11 server, which seems to be XMing but compiled directly with VC++ and supposedly more performant. It seems to work.
After some research and fiddling, I discovered that exported X11 emacs will render properly if you
It still produces a lot of GLib and GTk warnings on the console.