Sunday, March 12, 2017

… and You Can Write HoloLens Apps in macOS / Linux Too (Part 2: Project Setup)

In Part 1 we saw how to set up a cloud server to build Unity apps for HoloLens. That was setup you only need to do once. For remote builds, however, you’ll also need to do a bit of setup for each project you create.

Set Up Your Remote Git Repository

You’ll need a place to push and build your project on the remote server, so you’ll need to initialize a repository. However, we can’t just initialize a bare git repository, because we’ll need the files to be checked out.
Here are the commands to run in git bash which should already be installed in your VM to get things set up (the items in brackets are things you’ll need to fill in):
# One-time git setup; git will be annoyed at you if you don't do this.
git config --global user.email [YOUR_EMAIL]
git config --global user.name [YOUR_NAME]

cd /c/Users/[USER]/[PATH_TO_DIRECTORY_CONTAINING_REPOSITORIES]
git init [PROJECT_NAME]
cd [PROJECT_NAME]

# Allow pushing to checked-out (non-bare) repository
git config receive.denyCurrentBranch updateInstead
Now you’ll need  to set your local git repository up to push to the remote repository. If you use the script from Part 1 to modify your /etc/hosts file when you connect to the remote server, you won’t need to modify the git remote URL each time the remote VM starts up and gets a new IP address.
To get set up, you’ll need the Unity project on your local development machine to be checked in to git. Now you can run
git remote add holodevelop ssh://[WINUSER]@holodevelop/Users/[PATH_TO_REPO_WITH_FORWARD_SLASHES]
Note the forward slashes in the URL, and also the fact that for some reason there’s no “C:” in the path. I’m not sure why this is the case.
Note also that instead of “origin,” I call the remote repository “holodevelop,” but you can call it what you want. Note in addition that the remote server alias is also “holodevelop,” which should correspond to the alias the script creates in your /etc/hosts file.
Now you’ll be able to push your git repository to the remote server. For example, to push the master branch (if the remote repository is called “holodevelop”) the command would be:
git push holodevelop master
Since you’re pushing to a repository that has files checked out in the workspace, there are a few things you need to take note of. If you have unstaged changes in the remote repository which Unity sometimes initiates or if the remote gets ahead of the local repository, git won’t let you do a push; it will tell you you have unstaged changes in the remote directory, or that the remote contains work you don’t have locally. You’ll need to commit any unstaged changes into the remote repository first, then pull them back to your local repository (git pull holodevelop master) and merge them in if necessary.
With the setup so far, every time you push with git you’ll be asked for your password. To use public key authentication, put the following in your ~/.ssh/config file (assuming the remote host alias in /etc/hostsis holodevelop and your private key is named holodevelop):
Host            holodevelop
    Hostname        holodevelop
    IdentityFile    ~/.ssh/holodevelop
    IdentitiesOnly  yes

Pushing Up Your DLLs

For those building in F# or some other language not directly supported by Unity scripting, you’ll be compiling your script code into DLLs which will need to be copied into the Assets/Plugins directory of your Unity project. Since it’s not a good idea to put compiled artifacts into git, we’ll use a different approach.
First, make an Assets/Plugins directory in your Unity project and put an empty file named .gitkeep in there, to make sure the folder exists when you push your git repository to the remote server.  You’ll also want to add Assets/Plugins/*.dll* (and if you’re on macOS, .DS_Store) to your .gitignore file.
To get the DLLs onto the remote server, you’ll need a post-build step that copies them to the right place. Since we’ve already mounted a file share on the remote server, this should be as easy as doing a copy command.
One way to accomplish this is to add a post-build step to your MSBuild project file, but this will make your project file not work across platforms. (I also tried it, and it isn’t evident how to do it if you want to go this route I suggest using Xamarin Studio to edit the build configuration and add the post-build step.
I use the FAKE build tool for building F# projects; the Ionide IDE is also integrated with Forge, which creates the whole F# project for you. For the post-build step, I change the Deploy target in the build.fsx file to the following
let remoteProjectPath = "[PATH_TO_PROJECT_ON_FILESHARE]"

Target "Deploy" (fun _ ->
    let myCodeDll = buildDir + "[PROJECT_NAME].dll"
    let fsharpDll = buildDir + "FSharp.Core.dll"
    Copy "../Assets/Plugins" [ myCodeDll; fsharpDll ]
    Copy (remoteProjectPath + "/Assets/Plugins") [ myCodeDll; fsharpDll ]
)
I also change the RunTargetOrDefault entry at the bottom of the file to “Deploy”. Now in the Ionide command menu I can enter “FAKE: Build Default” and the DLLs will be copied to the Assets/Plugins directory in both the local and remote projects. NOTE: To prevent getting a “Lock violation on path” error when trying to copy up the dlls, don’t have the remote folder open or even visible in your Finder window (smb will lock it).

Doing a Unity Build

Before you can run a Unity headless build for HoloLens, you’ll need to do a few things.
First, annoyingly, Unity apparently needs to reactivate itself every time you start the VM, so each time you start the VM, or even restart Windows on the VM, you’ll need to open Unity. (I’m hoping Unity support can give me some workaround for this.)
The first time you create a HoloLens project on the remote box, you have to open it in Unity to add a few settings that can only be added when you’re running Unity in Windows:
  • In the build settings, make sure you Switch Platforms to Windows Store. Set the SDK to Universal 10 and the target device to HoloLens.
  • In the player settings, click the Windows Store tab and go to Other Settings > Rendering. Check the “Virtual Reality Supported” box, and make sure Windows Holographic is under the Virtual Reality SDKs.
  • In the player settings, under  Configuration > Scripting backend  pull down IL2CPP.
  • Save the project, commit, and pull any changes back to local machine.
If you’re compiling F# or other code to plugin .DLLs, before you close the project you’ll also need to make sure the MonoBehaviours in your code are attached to the relevant objects. Don’t delete the DLLs while Unity is open, or they’ll get detached and you’ll have to reattach them.
Now you’ll need a headless build script for Unity. This comes in two parts. The first is a Unity-flavored Javascript file that has to go in Assets/Editor, and the second a batch file that can go at the top-level of your Unity project.
Here’s the Javascript file; I call it build.js:
import System.Diagnostics;

class Autobuild {

    static function Build() {
        // The path to the directory where the IL2CPP files will be built.
        var path = "C:/[PATH_TO_OUTPUT_DIR]";
        // An array of paths to the files for the scenes you want to build.
        var scenes : String[] = [ "Assets/[SCENE_NAME].unity" ];
        // Build the player.
        BuildPipeline.BuildPlayer(scenes, path, BuildTarget.WSAPlayer, BuildOptions.None);
    }
}
You’ll need to fill in the path where you want the C++ files build, as well as the scenes you want to include. Note that the path starts with the drive letter, and the path dividers are forward slashes.
Now you’ll need a batch file to trigger the build; we’ll put this in unity_headless_build.bat at the root of the Unity project:
SET UNITY_FOLDER=[UNITY_VERSION_NAME]
SET OUTPUT_DIR=[PATH_TO_OUTPUT_DIR]

cd %~dp0
if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%"

"C:\Program Files\%UNITY_FOLDER%\Editor\Unity.exe" -batchmode -quit -projectPath %~dp0 -executeMethod Autobuild.Build
Here you need to fill in the name of the folder where Unity is installed (the full path is created further down) and the path to the directory where the IL2CPP files will be build. This needs to be Windows-style, with backslashes.
You should be able to run this batch file from the Windows command prompt and see a new Visual Studio project built in your target directory.

Running MSBuild

To get the final executable you can run on the HoloLens, you now have to build the project that Unity generated. You can do this by running msbuild using the following script, which I call msbuild_headless.batand put in the root of my Unity project:
SET OUTPUT_DIR=[PATH_TO_OUTPUT_DIR]
set SOLUTION_FILE_NAME=[PROJECT_NAME].sln
cd "%OUTPUT_DIR%"

msbuild.exe %SOLUTION_FILE_NAME% /t:Build /p:Configuration=Release;Platform=x86;AppxBundle=Always
Here again the path to the project should use the Windows-style path, and you’ll also need to fill in the name of the solution file generated by Unity.
Running this script at the command prompt should give you a new AppPackages directory inside the solution directory, and several levels under that you’ll see a file named after your project with the extension .appxbundle. This is the executable you’ll upload to your HoloLens (or deploy to the emulator).
If you want to run everything from your local command line in one step, you can make a remote_build.sh script as follows:
ssh [USER]@holodevelop "cd [UNITY_PROJECT_DIR] && unity_headless_build.bat && msbuild_headless.bat"
Here, you should fill in your user ID on the remote machine and a Windows-style path to the Unity project directory.
This should give you all you need to run a remote build of your application. In the last part of this series, we’ll look at how to deploy the application to the HoloLens.

[This piece was originally posted here.]

No comments:

Post a Comment