Sunday, March 12, 2017

… and You Can Write HoloLens Apps in macOS / Linux Too (Part 1: The Cloud Server)

Interested in doing all your HoloLens development while running macOS or Linux? Well, you can’t — not all of it, anyway. HoloLens applications have to be compiled to UWP applications by the .NET Native compiler, which only runs on Windows. And Unity only builds Windows Store apps on Windows. Both Microsoft’s and Unity’s emulators only run on Windows.
What you can do is write your Unity code locally on macOS or Linux, push it up to a remote build server, and build it there. This might also be a desirable option if you have an underpowered Windows machine and want to speed up your builds.
And, depending on your usage, you can have the cloud server free of cost for a year.


If you don’t have a HoloLens, this setup might not be for you. Currently there is no way to run either Microsoft’s or Unity’s emulator in the cloud.1 So if you want to develop on Mac or Linux your best bet is to get yourself a HoloLens to test on. (Or, you could get a Windows machine just for build and emulation, in which case some of the information below will still be of use to you.)
There’s also the issue of debugging. You can use the Visual Studio debugger on apps running in the emulator or on even on the device; there may be ways to do this from Xamarin Studio or the command line, but I haven’t tried to make that work.

Setting Up the Cloud Server

For this article, we’re going to use Microsoft’s Azure cloud service, though you could also use Amazon’s AWS or other cloud services. One advantage of Azure is that as a developer, you can sign up for Visual Studio Dev Essentials and get $25 per month of complimentary Azure credit for your first year.2 Depending on the size of the VM you choose, you should be able to stay under that budget.
Once you’ve signed up, go to the Subscriber Portal, find the Azure feature, and click the “Activate” link. This will set you up with your Azure account and give you a dashboard, as well as a screen where you can view your developer subscription and see how much credit you’ve used for the month.
The dashboard is where you create the virtual machine on which your build server will run. Here’s how:
  1. In the portal menu on the right, select + New > Compute
  2. Hit the “See All” link
  3. Search for “Visual Studio Community 2017 RC on Windows Server 2016 (x64)”
  4. Click on it, then select the “Resource Manager” Deployment Model
  5. For the Basics step:
    • Give your VM a name.
    • Choose an SSD disk.
    • Create a username and password.
    • Use your “Developer Program Benefit” subscription
    • Create a new “resource group”3 (unless you have one you already want to use) and give it a name.
  6. For the Size step, choose your VM size. To fit within the developer benefit budget, you’ll probably need to use a DS2_V2, depending on how much you use the VM. Ideally you’ll want more than 8GB of RAM, and 4 cores is as many as you can get with the developer license, so you might prefer an F4S or DS3_V2 instead. Note that you won’t be using the VM 24×7, so the monthly prices are higher than what you’re going to pay, but you may have to limit your usage to fit inside the $25/month credit.
  7. For the Settings step, choose Managed disks (there’s no price difference, and it’s easier), and otherwise leave the defaults.
  8. Once you create the VM, you should see it in your dashboard, at least under “All Resources.” If not, click on “All Resources.” You can click on your VM to see it, and it will then appear under “All Resources.”

Connecting to the Server

Before you connect to the VM, you’ll need a remote desktop client. For macOS I suggest the Microsoft Remote Desktop Beta, but you can also get the non-beta version on the app store.4 For Linux, there are several RDP clients out there you can choose from. (I’m not using Linux, so I can’t make recommendations.)
To connect to the server, double-click on your VM in the Azure dashboard. You’ll see a row of buttons at the top the leftmost of which is “Connect.” Clicking it will cause you to download a file with the .rdp extension. On macOS, double-clicking the RDP file should open a connection to the remote server in the Microsoft remote desktop client.
Note that if you don’t want to spend the extra money for a static IP address, the IP address will change every time you restart the virtual machine. Since you’ll be using various automated scripts, you’d need to change the IP address in each one every time. Instead, you can write a script to read the IP address from the RDP file and modify /etc/hosts with the new IP address. Then you can use the alias in /etc/hosts in your scripts without having to modify them.
Here’s an example of such a script, which you can modify and run whenever you download a new RDP file; it uses the alias holodevelopfor the remote server. (This script is for macOS but a Linux version would be similar.) The script will automatically open the desktop client for you once it finishes modifying the hosts file, and then mount the \Users folder on the remote system, which the Azure VM should automatically share with you:

# May need to modify this for whatever directory the rdp files get downloaded to
RDP_FILE=`ls -tr ~/Downloads/*.rdp | tail -1`

# sed -E is for OS X, use sed -r on linux
IP_ADDRESS=`cat "$RDP_FILE" | grep 'address' | sed -E 's/.*s:(.*):[[:digit:]]+/\1/' | tr -d '\015' | tr -d '\012'`

# Before running this script for the first time, initialize /etc/hosts with:
# holodevelop

# Dig the remote IP address out of the RDP file
sudo sed -e 's/.* holodevelop'"/$IP_ADDRESS holodevelop/" -i old /etc/hosts

# This will open a session to the server in Microsoft Remote Desktop
open "$RDP_FILE"

# You'll need to add your remote user ID for USER and a path to a local directory LOCAL_PATH where you'll mount the drive
mount -t smbfs "//[USER]@$IP_ADDRESS/Users" "[LOCAL_PATH]"
Before this script can mount the remote file share, you have to tell Azure to let requests on port 445 access the server:
  • In the Azure web portal, find the Network Security Group you created. (You may have to go to Resource Groups and click link on your resource group to find it.)
  • Click on Inbound Security Rules > Add
  • Use the following settings — Source: Any, Service: Custom, Port Range: 445, Action: Allow
Note that when the script mounts the remote file share, it will ask you for your password on the remote machine.

Basic Server Setup

Once you remote in to your VM, here are some things to check and changes I recommend:
  • You should see Visual Studio 2017 Community and Unity already installed.
  • You should also see the directory C:\Program Files (x86)\Microsoft Visual Studio Tools for Unity.
  • You should see Git installed in C:\Program Files\Git.
  • You may want to set your time zone on the server in Settings > Time and Language > Date and Time
  • Turn off IE Enhanced Security Configuration, or your experience downloading additional software will be miserable. (You can turn it back on later, but this may not be the kind of server where you worry too much about locking down the browser.) To do this, go to Server Manager > Local Server, locate “IE Enhanced Security Configuration” in the right-hand column and click the link next to it. You should see a screen that lets you turn it off.
  • Add C:\Program Files\Git\mingw64\bin and C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin to the System PATH environment variable. (Use Control Panel > System and Security > System, click the Advanced System Settings on the right, and hit the Environment Variables button at the bottom.) The first is needed for remote git to work, and the second for msbuild.exe to be accessible by your build scripts.
  • Create a directory where you will put your git repositories for your projects. Mine is Documents/holodevelop.
Now it’s time to install a few things you need.

Install the Unity Beta

If you’re going to be doing IL2CPP builds from Unity — and this is the future of Unity scripting, not to mention a necessity for writing Unity scripts in F# — you’ll want to download and install the latest Unity beta, since they’re working hard on improving the performance of this feature. At the installer options, uncheck the standard assets, documentation, and download of Visual Studio 2015, and check the Windows Store il2cpp scripting backend.
After installing Unity, make sure it’s pointing to the path to Visual Studio 2017 as the script editor. (This isn’t strictly speaking necessary, since you’re going to do headless builds, but it will help if you decide to do any work directly on the remote server.)
  • Open Unity; Go to Edit -> Preferences, and make sure that Visual Studio 2017 Community is selected as the External Script Editor
  • If not, choose Browse in the External Script Editor pulldown, and point to the Visual Studio executable (which should be C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.exe)

Install and Set Up SSH

We will use git over SSH to transfer code from your local machine to the server.5 
Download and install the Bitvise Secure Windows Remote Access server, which is free for non-commercial use. Run the installer and accept the default install, check “Run Bitvise SSH Server Control Panel when done,” and select the Personal Edition.
After you’ve finished, you’ll see a Settings page. Click the “Open easy settings” link.
  • For Open Windows Firewall: Pull down “Open port(s) to any computer”
  • Click the “Next” button
  • On “Windows Accounts” unclick “Allow Login to Any Windows Account”
  • On “Windows Accounts” click “Add” and add your own Windows account.
Now back on the main control panel screen, click the “Start Server” link (just above the Host Keys).
Before you can use SSH, you have to tell Azure to let requests on port 22 access the server:
  • In the Azure web portal, find the Network Security Group you created. (You may have to go to Resource Groups and click link on your resource group to find it.)
  • Click on Inbound Security Rules > Add
  • Use the following settings — Source: Any, Service: SSH, Action: Allow
Now set up public-key authentication, which will allow scripts to transfer files without you having to enter a password each time.
    • On your local machine, run the command ssh-keygen -t rsa
    • Note that you don’t want a passphrase for this key, because it will be used by automated programs. Keep it safe!
    • Name the private and public key files something that will distinguish them from other keys you might have, e.g., holodevelop.
    • Generally it’s customary to keep the key pair in your ~/.sshfolder.
    • Copy the public key up to the Windows remote server using the shared folder you created.
    • Import the public key on the remote server. From the Start menu, open the Bitvise SSH Server Control Panel, click the “Open easy settings” link, click the “Next” button, and select your own account in the “Windows Accounts” screen. Click on the “Public Keys” link and add the public key that you copied up in the last step.
    • Now you can delete the public key file you copied up from the remote Windows machine.
    • Restart Windows.
Now test to see if you can connect using your public key, for example:
ssh -i ~/.ssh/holodevelop [USER]@holodevelop

Modify Windows Defender

Windows Defender is an antivirus program that does security scans of  the local system. Subjecting your git repository and your build directories to Windows Defender scans may slow your build. We’ll exclude these directories from being scanned by Windows Defender:
  • Settings > Update & Security > Windows Defender
  • Scroll down to Exclusions and click Add an Exclusion
  • Exclude the folder that will hold your git repository as well as the folder you created on the E: drive for builds).

Some Last Thoughts for Part 1

When you’re done with the VM, stop it so that you’re not incurring charges for the VM time. You’ll need to remember to restart it when you want to do development. To stop a VM go into the Azure portal, click on the VM, and hit Stop from the buttons at the top row.
Now we’re ready for Part 2, where we’ll do one-time project-specific setup.

[This piece was originally posted here.]

1 Microsoft’s HoloLens emulator requires Hyper-V, and since Azure VMs are already virtualized, and you can’t have nested Hyper-V, it won’t work. (I’ve tested this.) If you want to run Microsoft’s emulator, you’ll need a machine running Windows Pro or some other edition of Windows that supports Hyper-V.
Unity’s Holographic Emulation also has problems in a VM environment.
2 Microsoft also has a build service included in Visual Studio Team Services, which is free of charge for teams of fewer than five members. Unfortunately, it currently doesn’t support Unity builds.
3 A resource group holds everything you’re going to create — not just the virtual machine, but the network interface, IP address, network security group, storage, etc. This is so that if you want to get rid of a VM you can get rid of the entire group and not continue to be charged for resources outside the VM, such as storage.
4 One productivity tip: Once logged in to a remote server using the macOS remote desktop client, if I Cmd-Tab switched from that app, I found myself unable to Cmd-Tab back to the remote screen; I’d always come back to the “View Connection Center” window of the desktop client, and then have to double-click on my active connection to switch to the main screen. To be able to Cmd-Tab back directly to the remote screen, minimize the View Connection Center window after connecting.
5 I tried doing this by having git use the drive shared from the server, but git doesn’t play nice with smb protocol, at least not when going from macOS to Windows.
6 The -a switch creates a new disk, -t puts it in virtual memory, -s is the size, -m creates it as drive E:, and the options to -p say to create an ntfs file system, do a quick format on it, and automatically answer “Yes” to the user prompts. The second line creates a holodevelop folder in the E: drive. (You can name it whatever you want instead.)
7 Each time you log in to the VM the script will run, but it’s idempotent so nothing is harmed by running it more than once. You could have the E: drive created whenever Windows starts by creating a Windows service, but that’s is more complicated. Also, imdisk has a -P parameter which will create the drive on Windows startup, but it won’t format it, so there would be a manual step involved.

No comments:

Post a Comment